From b544661e81bfe4358e41b001aef79579e1bbd324 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 11 Nov 2022 10:32:54 +0000 Subject: [PATCH 001/268] Experimental skin fix, inspired by craftycodie's Signed-off-by: TheKodeToad --- launcher/minecraft/MinecraftInstance.cpp | 5 + .../pages/instance/InstanceSettingsPage.cpp | 7 + .../ui/pages/instance/InstanceSettingsPage.ui | 10 + libraries/launcher/CMakeLists.txt | 4 + .../org/prismlauncher/EntryPoint.java | 3 + .../launcher/org/prismlauncher/fix/Fix.java | 48 ++ .../launcher/org/prismlauncher/fix/Fixes.java | 56 +++ .../org/prismlauncher/fix/skins/SkinFix.java | 107 +++++ .../fix/skins/SkinFixUrlStreamHandler.java | 182 ++++++++ .../org/prismlauncher/utils/JsonParser.java | 433 ++++++++++++++++++ 10 files changed, 855 insertions(+) create mode 100644 libraries/launcher/org/prismlauncher/fix/Fix.java create mode 100644 libraries/launcher/org/prismlauncher/fix/Fixes.java create mode 100644 libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java create mode 100644 libraries/launcher/org/prismlauncher/fix/skins/SkinFixUrlStreamHandler.java create mode 100644 libraries/launcher/org/prismlauncher/utils/JsonParser.java diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 1d37224aa..91de955ad 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -192,6 +192,8 @@ void MinecraftInstance::loadSpecificSettings() m_settings->registerSetting("JoinServerOnLaunch", false); m_settings->registerSetting("JoinServerOnLaunchAddress", ""); + m_settings->registerSetting("LegacySkinFix", true); + qDebug() << "Instance-type specific settings were loaded!"; setSpecificSettingsLoaded(true); @@ -661,6 +663,9 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS launchScript += "traits " + trait + "\n"; } + if (profile->getTraits().contains("legacySkins") && settings()->get("LegacySkinFix").toBool()) + launchScript += "fixes legacySkinFix\n"; + launchScript += "launcher " + getLauncher() + "\n"; // qDebug() << "Generated launch script:" << launchScript; diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index af2ba7c80..537271a2c 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -3,6 +3,7 @@ * PolyMC - 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 @@ -275,6 +276,9 @@ void InstanceSettingsPage::applySettings() m_settings->reset("JoinServerOnLaunchAddress"); } + bool legacySkinFix = ui->legacySkinFix->isChecked(); + m_settings->set("LegacySkinFix", legacySkinFix); + // FIXME: This should probably be called by a signal instead m_instance->updateRuntimeContext(); } @@ -372,6 +376,9 @@ void InstanceSettingsPage::loadSettings() ui->serverJoinGroupBox->setChecked(m_settings->get("JoinServerOnLaunch").toBool()); ui->serverJoinAddress->setText(m_settings->get("JoinServerOnLaunchAddress").toString()); + + ui->legacySkinFix->setChecked(m_settings->get("LegacySkinFix").toBool()); + ui->legacySkinFix->setVisible(m_instance->traits().contains("legacySkins")); } void InstanceSettingsPage::on_javaDetectBtn_clicked() diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index b064367d1..ca49cbbe5 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -608,6 +608,16 @@ + + + + Enables support for modern skins on old versions. + + + Enable legacy skin fix + + + diff --git a/libraries/launcher/CMakeLists.txt b/libraries/launcher/CMakeLists.txt index 55ed58756..1ad398ff5 100644 --- a/libraries/launcher/CMakeLists.txt +++ b/libraries/launcher/CMakeLists.txt @@ -15,6 +15,10 @@ set(SRC org/prismlauncher/launcher/impl/legacy/LegacyFrame.java org/prismlauncher/exception/ParameterNotFoundException.java org/prismlauncher/exception/ParseException.java + org/prismlauncher/fix/Fix.java + org/prismlauncher/fix/Fixes.java + org/prismlauncher/fix/skins/SkinFix.java + org/prismlauncher/fix/skins/SkinFixUrlStreamHandler.java org/prismlauncher/utils/Parameters.java org/prismlauncher/utils/ReflectionUtils.java org/prismlauncher/utils/logging/Level.java diff --git a/libraries/launcher/org/prismlauncher/EntryPoint.java b/libraries/launcher/org/prismlauncher/EntryPoint.java index 78804b3e8..88ecb48ce 100644 --- a/libraries/launcher/org/prismlauncher/EntryPoint.java +++ b/libraries/launcher/org/prismlauncher/EntryPoint.java @@ -59,6 +59,7 @@ import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import org.prismlauncher.exception.ParseException; +import org.prismlauncher.fix.Fixes; import org.prismlauncher.launcher.Launcher; import org.prismlauncher.launcher.impl.StandardLauncher; import org.prismlauncher.launcher.impl.legacy.LegacyLauncher; @@ -107,6 +108,8 @@ public final class EntryPoint { } try { + Fixes.apply(params); + Launcher launcher; String type = params.getString("launcher"); diff --git a/libraries/launcher/org/prismlauncher/fix/Fix.java b/libraries/launcher/org/prismlauncher/fix/Fix.java new file mode 100644 index 000000000..da578c24f --- /dev/null +++ b/libraries/launcher/org/prismlauncher/fix/Fix.java @@ -0,0 +1,48 @@ +// 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.fix; + +import org.prismlauncher.utils.Parameters; + +public interface Fix { + + String getName(); + + boolean isApplicable(Parameters parameters); + + void apply(); + +} diff --git a/libraries/launcher/org/prismlauncher/fix/Fixes.java b/libraries/launcher/org/prismlauncher/fix/Fixes.java new file mode 100644 index 000000000..ad472fea5 --- /dev/null +++ b/libraries/launcher/org/prismlauncher/fix/Fixes.java @@ -0,0 +1,56 @@ +// 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.fix; + +import java.util.Collections; +import java.util.List; + +import org.prismlauncher.fix.skins.SkinFix; +import org.prismlauncher.utils.Parameters; + +public final class Fixes { + + private static final Fix[] FIXES = { new SkinFix() }; + + public static void apply(Parameters parameters) { + List fixes = parameters.getList("fixes", Collections.emptyList()); + + for (Fix fix : FIXES) + if (fixes.contains(fix.getName()) && fix.isApplicable(parameters)) + fix.apply(); + } + +} diff --git a/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java b/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java new file mode 100644 index 000000000..8d0969a6d --- /dev/null +++ b/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java @@ -0,0 +1,107 @@ +// 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.fix.skins; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Method; +import java.net.Proxy; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; +import java.net.URLStreamHandlerFactory; + +import org.prismlauncher.fix.Fix; +import org.prismlauncher.utils.Parameters; +import org.prismlauncher.utils.logging.Log; + +public final class SkinFix implements Fix, URLStreamHandlerFactory { + + private static URLStreamHandler http; + private static MethodHandle openConnection; + private static MethodHandle openConnection2; + + static { + try { + Method getURLStreamHandler = URL.class.getDeclaredMethod("getURLStreamHandler", String.class); + getURLStreamHandler.setAccessible(true); + http = (URLStreamHandler) getURLStreamHandler.invoke(null, "http"); + + Method openConnectionReflect = URLStreamHandler.class.getDeclaredMethod("openConnection", URL.class); + openConnectionReflect.setAccessible(true); + openConnection = MethodHandles.lookup().unreflect(openConnectionReflect); + + Method openConnectionReflect2 = URLStreamHandler.class.getDeclaredMethod("openConnection", URL.class, + Proxy.class); + openConnectionReflect2.setAccessible(true); + openConnection2 = MethodHandles.lookup().unreflect(openConnectionReflect2); + } catch (Throwable e) { + Log.error("Could not perform URL reflection; skin fix will not be availble", e); + } + } + + static URLConnection openConnection(URL url) throws Throwable { + return (URLConnection) openConnection.invokeExact(http, url); + } + + static URLConnection openConnection(URL url, Proxy proxy) throws Throwable { + return (URLConnection) openConnection2.invokeExact(http, url, proxy); + } + + @Override + public String getName() { + return "legacySkinFix"; + } + + @Override + public boolean isApplicable(Parameters parameters) { + return http != null && openConnection != null; + } + + @Override + public void apply() { + URL.setURLStreamHandlerFactory(this); + } + + @Override + public URLStreamHandler createURLStreamHandler(String protocol) { + if ("http".equals(protocol)) + return new SkinFixUrlStreamHandler(); + + return null; + } + +} diff --git a/libraries/launcher/org/prismlauncher/fix/skins/SkinFixUrlStreamHandler.java b/libraries/launcher/org/prismlauncher/fix/skins/SkinFixUrlStreamHandler.java new file mode 100644 index 000000000..71af341c5 --- /dev/null +++ b/libraries/launcher/org/prismlauncher/fix/skins/SkinFixUrlStreamHandler.java @@ -0,0 +1,182 @@ +// 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.fix.skins; + +import java.io.IOException; +import java.io.InputStream; +import java.net.Proxy; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; +import java.util.Map; + +import javax.xml.bind.DatatypeConverter; + +import org.prismlauncher.utils.JsonParser; + +@SuppressWarnings("unchecked") +final class SkinFixUrlStreamHandler extends URLStreamHandler { + + private URL redirect(URL address) throws IOException { + String skinOwner = findSkinOwner(address); + + if (skinOwner != null) + return convertSkin(address, skinOwner); + + String capeOwner = findCapeOwner(address); + + if (capeOwner != null) + return convertCape(address, capeOwner); + + return address; + } + + @Override + protected URLConnection openConnection(URL address) throws IOException { + address = redirect(address); + + try { + return SkinFix.openConnection(address); + } catch (RuntimeException | Error e) { + throw e; + } catch (Throwable e) { + throw new IllegalStateException(e); + } + } + + @Override + protected URLConnection openConnection(URL address, Proxy proxy) throws IOException { + address = redirect(address); + + try { + return SkinFix.openConnection(address, proxy); + } catch (RuntimeException | Error e) { + throw e; + } catch (Throwable e) { + throw new IllegalStateException(e); + } + } + + private URL convertSkin(URL defaultUrl, String owner) throws IOException { + Map textures = getTextures(owner); + + if (textures != null) { + textures = (Map) textures.get("SKIN"); + return new URL((String) textures.get("url")); + } + + return defaultUrl; + } + + private URL convertCape(URL defaultUrl, String owner) throws IOException { + Map textures = getTextures(owner); + + if (textures != null) { + textures = (Map) textures.get("CAPE"); + + if (textures == null) + return defaultUrl; + + return new URL((String) textures.get("url")); + } + + return defaultUrl; + } + + private static Map getTextures(String owner) throws IOException { + try (InputStream in = new URL("https://api.mojang.com/users/profiles/minecraft/" + owner).openStream()) { + Map map = (Map) JsonParser.parse(in); + String id = (String) map.get("id"); + + try (InputStream profileIn = new URL("https://sessionserver.mojang.com/session/minecraft/profile/" + id) + .openStream()) { + Map profile = (Map) JsonParser.parse(profileIn); + + for (Map property : (Iterable>) profile.get("properties")) { + if (property.get("name").equals("textures")) { + Map result = (Map) JsonParser + .parse(new String(DatatypeConverter.parseBase64Binary((String) property.get("value")))); + result = (Map) result.get("textures"); + return result; + } + } + + return null; + } + } + } + + private static String findSkinOwner(URL address) { + switch (address.getHost()) { + case "www.minecraft.net": + return stripPng(strip(address.getPath(), "/skin/")); + + case "s3.amazonaws.com": + case "skins.minecraft.net": + return stripPng(strip(address.getPath(), "/MinecraftSkins/")); + } + + return null; + } + + private static String findCapeOwner(URL address) { + switch (address.getHost()) { + case "www.minecraft.net": + return stripPng(strip(address.getQuery(), "user=")); + + case "s3.amazonaws.com": + case "skins.minecraft.net": + return stripPng(strip(address.getPath(), "/MinecraftCloaks/")); + } + + return null; + } + + private static String stripPng(String string) { + if (string != null && string.endsWith(".png")) + return string.substring(0, string.lastIndexOf('.')); + + return string; + } + + private static String strip(String string, String prefix) { + if (string != null && string.startsWith(prefix)) + return string.substring(prefix.length()); + + return null; + } + +} diff --git a/libraries/launcher/org/prismlauncher/utils/JsonParser.java b/libraries/launcher/org/prismlauncher/utils/JsonParser.java new file mode 100644 index 000000000..e1703972b --- /dev/null +++ b/libraries/launcher/org/prismlauncher/utils/JsonParser.java @@ -0,0 +1,433 @@ +// 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 . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * MIT License + * + * Copyright (c) 2022 TheKodeToad + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 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. + */ + +package org.prismlauncher.utils; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringReader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Single-file JSON parser to allow for usage in versions without GSON. + */ +public final class JsonParser { + + private final Reader in; + private char[] buffer; + private int pos, length; + + public static Object parse(String in) throws JsonParseException, IOException { + return parse(new StringReader(in)); + } + + public static Object parse(InputStream in) throws JsonParseException, IOException { + return parse(new InputStreamReader(in, StandardCharsets.UTF_8)); + } + + public static Object parse(Reader in) throws JsonParseException, IOException { + return new JsonParser(in).readSingleValue(); + } + + private JsonParser(Reader in) throws IOException { + this.in = in; + pos = length = 0; + read(); + } + + private int character() { + if (length == -1) + return -1; + + return buffer[pos]; + } + + private int read() throws IOException { + if (length == -1) + return -1; + + if (buffer == null || pos++ == length - 1) { + pos = 0; + buffer = new char[8192]; + length = in.read(buffer); + } + + return character(); + } + + private void assertCharacter(char character) throws JsonParseException { + if (character() != character) + throw new JsonParseException("Expected '" + character + "' but got " + + (character() != -1 ? ("'" + (char) character() + "'") : "EOF")); + } + + private void assertNoEOF(String expected) throws JsonParseException { + if (character() == -1) + throw new JsonParseException("Expected " + expected + " but got EOF"); + } + + private void skipWhitespace() throws IOException { + while (isWhitespace()) + read(); + } + + private boolean isWhitespace() { + return character() == ' ' || character() == '\n' || character() == '\r' || character() == '\t'; + } + + private Object readSingleValue() throws IOException { + skipWhitespace(); + Object result = readValue(); + + if (!(result instanceof Double)) + read(); + + skipWhitespace(); + + if (character() != -1) + throw new JsonParseException("Found trailing non-whitespace characters"); + + return result; + } + + private Object readValue() throws IOException { + assertNoEOF("a value"); + + int character = character(); + + switch (character) { + case '{': + return readObject(); + + case '[': + return readArray(); + + case '"': + return readString(); + + case 't': + case 'f': + // probably boolean + Boolean bool = readBoolean(); + if (bool != null) + return bool; + + break; + + case 'n': + // probably null + if (readNull()) + return null; + + break; + } + + if (character == '-' || isDigit()) + // probably a number + return readNumber(); + + throw new JsonParseException("Expected a JSON value but got '" + (char) character + "'"); + } + + private Map readObject() throws IOException { + assertCharacter('{'); + Map obj = new HashMap<>(); + boolean comma = false; + + read(); + skipWhitespace(); + + while (character() != '}') { + if (comma) { + assertCharacter(','); + read(); + skipWhitespace(); + } + + String key = readString(); + read(); + skipWhitespace(); + assertCharacter(':'); + read(); + skipWhitespace(); + + Object value = readValue(); + obj.put(key, value); + + if (!(value instanceof Double)) + read(); + + skipWhitespace(); + comma = true; + } + + return obj; + } + + private List readArray() throws IOException { + assertCharacter('['); + List array = new ArrayList<>(); + boolean comma = false; + + read(); + skipWhitespace(); + + while (character() != ']') { + if (comma) { + assertCharacter(','); + read(); + skipWhitespace(); + } + + Object value = readValue(); + array.add(value); + + if (!(value instanceof Double)) + read(); + + skipWhitespace(); + comma = true; + } + + return array; + } + + private String readString() throws IOException { + assertCharacter('"'); + + StringBuilder result = new StringBuilder(); + + while (read() != '"') { + int character = character(); + + if (character >= '\u0000' && character <= '\u001F') + throw new JsonParseException("Found unescaped control character within string"); + + switch (character) { + case -1: + throw new JsonParseException("Expected '\"' but got EOF"); + + case 0x7F: + if (read() == '"') { + return result.toString(); + } + continue; + + case '\\': + int seq = read(); + + switch (seq) { + case -1: + throw new JsonParseException("Expected an escape sequence but got EOF"); + + case '\\': + break; + + case '/': + case '\"': + character = seq; + break; + + case 'b': + character = '\b'; + break; + + case 'f': + character = '\f'; + break; + + case 'n': + character = '\n'; + break; + + case 'r': + character = '\r'; + break; + + case 't': + character = '\t'; + break; + + case 'u': + // char array to allow allocation in advance. + char[] digits = new char[4]; + + for (int index = 0; index < digits.length; index++) { + character = read(); + if (index == 0 && character() == '-') { + throw new JsonParseException("Hex sequence may not be negative"); + } else if (character() == -1) { + throw new JsonParseException("Expected a hex sequence but got EOF"); + } + digits[index] = (char) character; + } + + String digitsString = new String(digits); + + try { + character = Integer.parseInt(digitsString, 16); + } catch (NumberFormatException e) { + throw new JsonParseException("Could not parse hex sequence \"" + digitsString + "\""); + } + + break; + default: + throw new JsonParseException("Invalid escape sequence: \\" + (char) seq); + } + break; + } + + result.append((char) character); + } + + return result.toString(); + } + + private boolean isDigit() { + return character() >= '0' && character() <= '9'; + } + + private Double readNumber() throws IOException { + StringBuilder result = new StringBuilder(); + + if (character() == '-') { + result.append((char) character()); + read(); + } + + if (character() == '0') { + result.append((char) character()); + read(); + if (isDigit()) + throw new JsonParseException("Found superfluous leading zero"); + } else if (!isDigit()) + throw new JsonParseException("Expected digits"); + + while (character() != -1 && isDigit()) { + result.append((char) character()); + read(); + } + + if (character() == '.') { + result.append('.'); + + read(); + assertNoEOF("digits"); + + if (!isDigit()) + throw new JsonParseException("Expected digits after decimal point"); + + while (character() != -1 && isDigit()) { + result.append((char) character()); + read(); + } + } + + if (character() == 'e' || character() == 'E') { + result.append('E'); + + read(); + assertNoEOF("digits"); + + if (character() == '+' || character() == '-') { + result.append((char) character()); + read(); + } + + if (!(character() == '+' || character() == '-' || isDigit())) + throw new JsonParseException("Expected exponent digits"); + + while (character() != -1 && isDigit()) { + result.append((char) character()); + read(); + } + } + + String resultStr = result.toString(); + + try { + return Double.parseDouble(resultStr); + } catch (NumberFormatException e) { + throw new JsonParseException("Failed to parse number '" + resultStr + "'"); + } + } + + private Boolean readBoolean() throws IOException { + if (character() == 't') { + if (read() == 'r' && read() == 'u' && read() == 'e') { + return true; + } + } else if (character() == 'f') { + if (read() == 'a' && read() == 'l' && read() == 's' && read() == 'e') { + return false; + } + } + return null; + } + + private boolean readNull() throws IOException { + return character() == 'n' && read() == 'u' && read() == 'l' && read() == 'l'; + } + + public class JsonParseException extends IOException { + + private static final long serialVersionUID = 1L; + + public JsonParseException(String message) { + super(message); + } + + } + +} \ No newline at end of file From 8a81aaaa0a0ead0a5398b4f3b4e967a3e78b6792 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sat, 12 Nov 2022 12:55:31 +0000 Subject: [PATCH 002/268] Add separate util class Signed-off-by: TheKodeToad --- libraries/launcher/CMakeLists.txt | 3 +- .../launcher/org/prismlauncher/fix/Fix.java | 17 ++- ...nFixUrlStreamHandler.java => Handler.java} | 21 +-- .../org/prismlauncher/fix/skins/SkinFix.java | 73 +++++------ .../org/prismlauncher/utils/UrlUtils.java | 121 ++++++++++++++++++ 5 files changed, 177 insertions(+), 58 deletions(-) rename libraries/launcher/org/prismlauncher/fix/skins/{SkinFixUrlStreamHandler.java => Handler.java} (91%) create mode 100644 libraries/launcher/org/prismlauncher/utils/UrlUtils.java diff --git a/libraries/launcher/CMakeLists.txt b/libraries/launcher/CMakeLists.txt index 1ad398ff5..d022d2532 100644 --- a/libraries/launcher/CMakeLists.txt +++ b/libraries/launcher/CMakeLists.txt @@ -18,9 +18,10 @@ set(SRC org/prismlauncher/fix/Fix.java org/prismlauncher/fix/Fixes.java org/prismlauncher/fix/skins/SkinFix.java - org/prismlauncher/fix/skins/SkinFixUrlStreamHandler.java + org/prismlauncher/fix/skins/Handler.java org/prismlauncher/utils/Parameters.java org/prismlauncher/utils/ReflectionUtils.java + org/prismlauncher/utils/UrlUtils.java org/prismlauncher/utils/logging/Level.java org/prismlauncher/utils/logging/Log.java net/minecraft/Launcher.java diff --git a/libraries/launcher/org/prismlauncher/fix/Fix.java b/libraries/launcher/org/prismlauncher/fix/Fix.java index da578c24f..3ecd2e90a 100644 --- a/libraries/launcher/org/prismlauncher/fix/Fix.java +++ b/libraries/launcher/org/prismlauncher/fix/Fix.java @@ -39,10 +39,25 @@ import org.prismlauncher.utils.Parameters; public interface Fix { + /** + * Gets the name of the fix. If the name isn't passed into the program, the fix + * won't run. + * + * @return The name + */ String getName(); - boolean isApplicable(Parameters parameters); + /** + * Determines whether the fix will be run. This is additional to the name check. + * + * @param params The parameters + * @return true to proceed to applying the fix + */ + boolean isApplicable(Parameters params); + /** + * Applies the fix. + */ void apply(); } diff --git a/libraries/launcher/org/prismlauncher/fix/skins/SkinFixUrlStreamHandler.java b/libraries/launcher/org/prismlauncher/fix/skins/Handler.java similarity index 91% rename from libraries/launcher/org/prismlauncher/fix/skins/SkinFixUrlStreamHandler.java rename to libraries/launcher/org/prismlauncher/fix/skins/Handler.java index 71af341c5..26a3d205e 100644 --- a/libraries/launcher/org/prismlauncher/fix/skins/SkinFixUrlStreamHandler.java +++ b/libraries/launcher/org/prismlauncher/fix/skins/Handler.java @@ -46,9 +46,10 @@ import java.util.Map; import javax.xml.bind.DatatypeConverter; import org.prismlauncher.utils.JsonParser; +import org.prismlauncher.utils.UrlUtils; @SuppressWarnings("unchecked") -final class SkinFixUrlStreamHandler extends URLStreamHandler { +final class Handler extends URLStreamHandler { private URL redirect(URL address) throws IOException { String skinOwner = findSkinOwner(address); @@ -67,27 +68,13 @@ final class SkinFixUrlStreamHandler extends URLStreamHandler { @Override protected URLConnection openConnection(URL address) throws IOException { address = redirect(address); - - try { - return SkinFix.openConnection(address); - } catch (RuntimeException | Error e) { - throw e; - } catch (Throwable e) { - throw new IllegalStateException(e); - } + return UrlUtils.openHttpConnection(address); } @Override protected URLConnection openConnection(URL address, Proxy proxy) throws IOException { address = redirect(address); - - try { - return SkinFix.openConnection(address, proxy); - } catch (RuntimeException | Error e) { - throw e; - } catch (Throwable e) { - throw new IllegalStateException(e); - } + return UrlUtils.openHttpConnection(address, proxy); } private URL convertSkin(URL defaultUrl, String owner) throws IOException { diff --git a/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java b/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java index 8d0969a6d..80cf5323e 100644 --- a/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java +++ b/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java @@ -35,60 +35,55 @@ package org.prismlauncher.fix.skins; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.reflect.Method; -import java.net.Proxy; import java.net.URL; -import java.net.URLConnection; import java.net.URLStreamHandler; import java.net.URLStreamHandlerFactory; import org.prismlauncher.fix.Fix; import org.prismlauncher.utils.Parameters; +import org.prismlauncher.utils.UrlUtils; import org.prismlauncher.utils.logging.Log; +/** + * Fixes skins by redirecting to other URLs. + * + * @see {@link Handler} + * @see {@link UrlUtils} + */ public final class SkinFix implements Fix, URLStreamHandlerFactory { - private static URLStreamHandler http; - private static MethodHandle openConnection; - private static MethodHandle openConnection2; - - static { - try { - Method getURLStreamHandler = URL.class.getDeclaredMethod("getURLStreamHandler", String.class); - getURLStreamHandler.setAccessible(true); - http = (URLStreamHandler) getURLStreamHandler.invoke(null, "http"); - - Method openConnectionReflect = URLStreamHandler.class.getDeclaredMethod("openConnection", URL.class); - openConnectionReflect.setAccessible(true); - openConnection = MethodHandles.lookup().unreflect(openConnectionReflect); - - Method openConnectionReflect2 = URLStreamHandler.class.getDeclaredMethod("openConnection", URL.class, - Proxy.class); - openConnectionReflect2.setAccessible(true); - openConnection2 = MethodHandles.lookup().unreflect(openConnectionReflect2); - } catch (Throwable e) { - Log.error("Could not perform URL reflection; skin fix will not be availble", e); - } - } - - static URLConnection openConnection(URL url) throws Throwable { - return (URLConnection) openConnection.invokeExact(http, url); - } - - static URLConnection openConnection(URL url, Proxy proxy) throws Throwable { - return (URLConnection) openConnection2.invokeExact(http, url, proxy); - } - @Override public String getName() { return "legacySkinFix"; } @Override - public boolean isApplicable(Parameters parameters) { - return http != null && openConnection != null; + public boolean isApplicable(Parameters params) { + if (!isSupported()) { + Log.warning("Using Java 8 will probably fix this"); + Log.warning("Alternatively, turning off legacy skin fix in Settings > Miscellaneous will silence the warnings"); + return false; + } + + return true; + } + + private boolean isSupported() { + // check for DatatypeConverter first + // most users will just be annoyed by the big stacktrace + try { + Class.forName("javax.xml.bind.DatatypeConverter"); + } catch (ClassNotFoundException e) { + Log.warning("Cannot find DatatypeConverter - required for skin fix"); + return false; + } + + if (!UrlUtils.isSupported()) { + Log.warning("Cannot access the necessary Java internals for skin fix"); + return false; + } + + return true; } @Override @@ -99,7 +94,7 @@ public final class SkinFix implements Fix, URLStreamHandlerFactory { @Override public URLStreamHandler createURLStreamHandler(String protocol) { if ("http".equals(protocol)) - return new SkinFixUrlStreamHandler(); + return new Handler(); return null; } diff --git a/libraries/launcher/org/prismlauncher/utils/UrlUtils.java b/libraries/launcher/org/prismlauncher/utils/UrlUtils.java new file mode 100644 index 000000000..c73c7600b --- /dev/null +++ b/libraries/launcher/org/prismlauncher/utils/UrlUtils.java @@ -0,0 +1,121 @@ +// 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; + +import java.io.IOException; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Method; +import java.net.Proxy; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; + +import org.prismlauncher.utils.logging.Log; + +/** + * A utility class for URLs which uses reflection to access hidden methods. + * Unfortunately not supported on newer Java versions. + */ +public class UrlUtils { + + private static URLStreamHandler http; + private static MethodHandle openConnection; + private static MethodHandle openConnectionProxied; + + static { + try { + // invoke URL.getURLStreamHandler to obtain some of the default handlers before + // they are overridden + Method getURLStreamHandler = URL.class.getDeclaredMethod("getURLStreamHandler", String.class); + getURLStreamHandler.setAccessible(true); + http = (URLStreamHandler) getURLStreamHandler.invoke(null, "http"); + + // reflection is required due to not having access + // unreflect is used due to the potential frequency of calls + Method openConnectionReflect = URLStreamHandler.class.getDeclaredMethod("openConnection", URL.class); + openConnectionReflect.setAccessible(true); + openConnection = MethodHandles.lookup().unreflect(openConnectionReflect); + + // a second method which takes in a proxy is required for a fully-functional + // URLStreamHandler + Method openConnectionReflectProxied = URLStreamHandler.class.getDeclaredMethod("openConnection", URL.class, + Proxy.class); + openConnectionReflectProxied.setAccessible(true); + openConnectionProxied = MethodHandles.lookup().unreflect(openConnectionReflectProxied); + } catch (Throwable e) { + Log.error("URL reflection failed - some features may not work", e); + } + } + + /** + * Determines whether all the features of this class are available. + * + * @return true if all features can be used + */ + public static boolean isSupported() { + return http != null && openConnection != null && openConnectionProxied != null; + } + + public static URLConnection openHttpConnection(URL url) throws IOException { + return openConnection(http, url); + } + + public static URLConnection openHttpConnection(URL url, Proxy proxy) throws IOException { + return openConnection(http, url, proxy); + } + + public static URLConnection openConnection(URLStreamHandler handler, URL url) throws IOException { + try { + return (URLConnection) openConnection.invokeExact(handler, url); + } catch (IOException | Error | RuntimeException e) { + throw e; // rethrow if possible + } catch (Throwable e) { + throw new Error(e); // otherwise, wrap in Error + } + } + + public static URLConnection openConnection(URLStreamHandler handler, URL url, Proxy proxy) throws IOException { + try { + return (URLConnection) openConnectionProxied.invokeExact(handler, url, proxy); + } catch (IOException | Error | RuntimeException e) { + throw e; + } catch (Throwable e) { + throw new Error(e); + } + } + +} From 7534eaf006e030f2f446a3c793090d7640fb2361 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sat, 12 Nov 2022 14:48:42 +0000 Subject: [PATCH 003/268] Only use DatatypeConverter as a fallback Signed-off-by: TheKodeToad --- libraries/launcher/CMakeLists.txt | 2 + .../org/prismlauncher/fix/skins/Handler.java | 5 +- .../org/prismlauncher/fix/skins/SkinFix.java | 21 +------ .../org/prismlauncher/utils/Base64.java | 59 +++++++++++++++++++ .../org/prismlauncher/utils/JsonParser.java | 2 +- .../org/prismlauncher/utils/UrlUtils.java | 2 +- 6 files changed, 67 insertions(+), 24 deletions(-) create mode 100644 libraries/launcher/org/prismlauncher/utils/Base64.java diff --git a/libraries/launcher/CMakeLists.txt b/libraries/launcher/CMakeLists.txt index d022d2532..acc276218 100644 --- a/libraries/launcher/CMakeLists.txt +++ b/libraries/launcher/CMakeLists.txt @@ -19,6 +19,8 @@ set(SRC org/prismlauncher/fix/Fixes.java org/prismlauncher/fix/skins/SkinFix.java org/prismlauncher/fix/skins/Handler.java + org/prismlauncher/utils/Base64.java + org/prismlauncher/utils/JsonParser.java org/prismlauncher/utils/Parameters.java org/prismlauncher/utils/ReflectionUtils.java org/prismlauncher/utils/UrlUtils.java diff --git a/libraries/launcher/org/prismlauncher/fix/skins/Handler.java b/libraries/launcher/org/prismlauncher/fix/skins/Handler.java index 26a3d205e..2bffeae96 100644 --- a/libraries/launcher/org/prismlauncher/fix/skins/Handler.java +++ b/libraries/launcher/org/prismlauncher/fix/skins/Handler.java @@ -43,8 +43,7 @@ import java.net.URLConnection; import java.net.URLStreamHandler; import java.util.Map; -import javax.xml.bind.DatatypeConverter; - +import org.prismlauncher.utils.Base64; import org.prismlauncher.utils.JsonParser; import org.prismlauncher.utils.UrlUtils; @@ -115,7 +114,7 @@ final class Handler extends URLStreamHandler { for (Map property : (Iterable>) profile.get("properties")) { if (property.get("name").equals("textures")) { Map result = (Map) JsonParser - .parse(new String(DatatypeConverter.parseBase64Binary((String) property.get("value")))); + .parse(new String(Base64.decode((String) property.get("value")))); result = (Map) result.get("textures"); return result; } diff --git a/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java b/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java index 80cf5323e..f5a9e0df4 100644 --- a/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java +++ b/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java @@ -59,27 +59,10 @@ public final class SkinFix implements Fix, URLStreamHandlerFactory { @Override public boolean isApplicable(Parameters params) { - if (!isSupported()) { - Log.warning("Using Java 8 will probably fix this"); - Log.warning("Alternatively, turning off legacy skin fix in Settings > Miscellaneous will silence the warnings"); - return false; - } - - return true; - } - - private boolean isSupported() { - // check for DatatypeConverter first - // most users will just be annoyed by the big stacktrace - try { - Class.forName("javax.xml.bind.DatatypeConverter"); - } catch (ClassNotFoundException e) { - Log.warning("Cannot find DatatypeConverter - required for skin fix"); - return false; - } - if (!UrlUtils.isSupported()) { Log.warning("Cannot access the necessary Java internals for skin fix"); + Log.warning("Using an older Java version will probably fix this"); + Log.warning("Alternatively, turning off legacy skin fix in Settings > Miscellaneous will silence the warnings"); return false; } diff --git a/libraries/launcher/org/prismlauncher/utils/Base64.java b/libraries/launcher/org/prismlauncher/utils/Base64.java new file mode 100644 index 000000000..20a189f46 --- /dev/null +++ b/libraries/launcher/org/prismlauncher/utils/Base64.java @@ -0,0 +1,59 @@ +// 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; + +import java.nio.charset.StandardCharsets; + +import javax.xml.bind.DatatypeConverter; + +public final class Base64 { + + private static boolean legacy; + + public static byte[] decode(String input) { + if (!legacy) { + try { + return java.util.Base64.getDecoder().decode(input.getBytes(StandardCharsets.UTF_8)); + } catch (NoClassDefFoundError e) { + legacy = true; + } + } + + // support for Java versions < 8 + return DatatypeConverter.parseBase64Binary(input); + } + +} diff --git a/libraries/launcher/org/prismlauncher/utils/JsonParser.java b/libraries/launcher/org/prismlauncher/utils/JsonParser.java index e1703972b..d1803c896 100644 --- a/libraries/launcher/org/prismlauncher/utils/JsonParser.java +++ b/libraries/launcher/org/prismlauncher/utils/JsonParser.java @@ -420,7 +420,7 @@ public final class JsonParser { return character() == 'n' && read() == 'u' && read() == 'l' && read() == 'l'; } - public class JsonParseException extends IOException { + public static class JsonParseException extends IOException { private static final long serialVersionUID = 1L; diff --git a/libraries/launcher/org/prismlauncher/utils/UrlUtils.java b/libraries/launcher/org/prismlauncher/utils/UrlUtils.java index c73c7600b..362feb4e3 100644 --- a/libraries/launcher/org/prismlauncher/utils/UrlUtils.java +++ b/libraries/launcher/org/prismlauncher/utils/UrlUtils.java @@ -50,7 +50,7 @@ import org.prismlauncher.utils.logging.Log; * A utility class for URLs which uses reflection to access hidden methods. * Unfortunately not supported on newer Java versions. */ -public class UrlUtils { +public final class UrlUtils { private static URLStreamHandler http; private static MethodHandle openConnection; From cfeadf858edd05e11db67d7d3d217ed25a00537b Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sat, 12 Nov 2022 15:12:55 +0000 Subject: [PATCH 004/268] Add workaround to warning Signed-off-by: TheKodeToad --- libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java b/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java index f5a9e0df4..927c10142 100644 --- a/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java +++ b/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java @@ -61,7 +61,7 @@ public final class SkinFix implements Fix, URLStreamHandlerFactory { public boolean isApplicable(Parameters params) { if (!UrlUtils.isSupported()) { Log.warning("Cannot access the necessary Java internals for skin fix"); - Log.warning("Using an older Java version will probably fix this"); + Log.warning("Try adding '--add-opens java.base/java.net=ALL-UNNAMED' to your Java arguments"); Log.warning("Alternatively, turning off legacy skin fix in Settings > Miscellaneous will silence the warnings"); return false; } From ead59c0246639837bbdbfcc6a0ca443a1ab06d6c Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sun, 20 Nov 2022 09:16:30 +0000 Subject: [PATCH 005/268] Improve the skin fix code - Spoof 404 instead of keeping original URL. - Move JsonParseException to the package. - Pass proxy as null to reduce code duplication. Signed-off-by: TheKodeToad --- libraries/launcher/CMakeLists.txt | 3 +- .../exception/JsonParseException.java | 48 ++++++++++++++ .../launcher/org/prismlauncher/fix/Fixes.java | 6 +- .../org/prismlauncher/fix/skins/Handler.java | 60 +++++++---------- .../org/prismlauncher/fix/skins/SkinFix.java | 2 +- .../org/prismlauncher/utils/JsonParser.java | 12 +--- .../utils/url/NullConnection.java | 66 +++++++++++++++++++ .../utils/{ => url}/UrlUtils.java | 35 ++-------- 8 files changed, 153 insertions(+), 79 deletions(-) create mode 100644 libraries/launcher/org/prismlauncher/exception/JsonParseException.java create mode 100644 libraries/launcher/org/prismlauncher/utils/url/NullConnection.java rename libraries/launcher/org/prismlauncher/utils/{ => url}/UrlUtils.java (74%) diff --git a/libraries/launcher/CMakeLists.txt b/libraries/launcher/CMakeLists.txt index acc276218..65a8241eb 100644 --- a/libraries/launcher/CMakeLists.txt +++ b/libraries/launcher/CMakeLists.txt @@ -23,7 +23,8 @@ set(SRC org/prismlauncher/utils/JsonParser.java org/prismlauncher/utils/Parameters.java org/prismlauncher/utils/ReflectionUtils.java - org/prismlauncher/utils/UrlUtils.java + org/prismlauncher/utils/url/NullConnection.java + org/prismlauncher/utils/url/UrlUtils.java org/prismlauncher/utils/logging/Level.java org/prismlauncher/utils/logging/Log.java net/minecraft/Launcher.java diff --git a/libraries/launcher/org/prismlauncher/exception/JsonParseException.java b/libraries/launcher/org/prismlauncher/exception/JsonParseException.java new file mode 100644 index 000000000..5983eb883 --- /dev/null +++ b/libraries/launcher/org/prismlauncher/exception/JsonParseException.java @@ -0,0 +1,48 @@ +// 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.exception; + +import java.io.IOException; + +public class JsonParseException extends IOException { + + private static final long serialVersionUID = 1L; + + public JsonParseException(String message) { + super(message); + } + +} \ No newline at end of file diff --git a/libraries/launcher/org/prismlauncher/fix/Fixes.java b/libraries/launcher/org/prismlauncher/fix/Fixes.java index ad472fea5..e099b653c 100644 --- a/libraries/launcher/org/prismlauncher/fix/Fixes.java +++ b/libraries/launcher/org/prismlauncher/fix/Fixes.java @@ -45,11 +45,11 @@ public final class Fixes { private static final Fix[] FIXES = { new SkinFix() }; - public static void apply(Parameters parameters) { - List fixes = parameters.getList("fixes", Collections.emptyList()); + public static void apply(Parameters params) { + List fixes = params.getList("fixes", Collections.emptyList()); for (Fix fix : FIXES) - if (fixes.contains(fix.getName()) && fix.isApplicable(parameters)) + if (fixes.contains(fix.getName()) && fix.isApplicable(params)) fix.apply(); } diff --git a/libraries/launcher/org/prismlauncher/fix/skins/Handler.java b/libraries/launcher/org/prismlauncher/fix/skins/Handler.java index 2bffeae96..0f5b556f8 100644 --- a/libraries/launcher/org/prismlauncher/fix/skins/Handler.java +++ b/libraries/launcher/org/prismlauncher/fix/skins/Handler.java @@ -45,61 +45,51 @@ import java.util.Map; import org.prismlauncher.utils.Base64; import org.prismlauncher.utils.JsonParser; -import org.prismlauncher.utils.UrlUtils; +import org.prismlauncher.utils.url.NullConnection; +import org.prismlauncher.utils.url.UrlUtils; @SuppressWarnings("unchecked") final class Handler extends URLStreamHandler { private URL redirect(URL address) throws IOException { String skinOwner = findSkinOwner(address); - if (skinOwner != null) - return convertSkin(address, skinOwner); + return convert(skinOwner, "SKIN"); String capeOwner = findCapeOwner(address); - if (capeOwner != null) - return convertCape(address, capeOwner); + return convert(capeOwner, "CAPE"); return address; } @Override protected URLConnection openConnection(URL address) throws IOException { - address = redirect(address); - return UrlUtils.openHttpConnection(address); + return openConnection(address, null); } @Override protected URLConnection openConnection(URL address, Proxy proxy) throws IOException { address = redirect(address); + + if (address == null) + return NullConnection.INSTANCE; + return UrlUtils.openHttpConnection(address, proxy); } - private URL convertSkin(URL defaultUrl, String owner) throws IOException { + private URL convert(String owner, String name) throws IOException { Map textures = getTextures(owner); if (textures != null) { - textures = (Map) textures.get("SKIN"); - return new URL((String) textures.get("url")); - } - - return defaultUrl; - } - - private URL convertCape(URL defaultUrl, String owner) throws IOException { - Map textures = getTextures(owner); - - if (textures != null) { - textures = (Map) textures.get("CAPE"); - + textures = (Map) textures.get(name); if (textures == null) - return defaultUrl; + return null; return new URL((String) textures.get("url")); } - return defaultUrl; + return null; } private static Map getTextures(String owner) throws IOException { @@ -116,6 +106,7 @@ final class Handler extends URLStreamHandler { Map result = (Map) JsonParser .parse(new String(Base64.decode((String) property.get("value")))); result = (Map) result.get("textures"); + return result; } } @@ -128,11 +119,11 @@ final class Handler extends URLStreamHandler { private static String findSkinOwner(URL address) { switch (address.getHost()) { case "www.minecraft.net": - return stripPng(strip(address.getPath(), "/skin/")); + return stripIfPrefixed(address.getPath(), "/skin/"); case "s3.amazonaws.com": case "skins.minecraft.net": - return stripPng(strip(address.getPath(), "/MinecraftSkins/")); + return stripIfPrefixed(address.getPath(), "/MinecraftSkins/"); } return null; @@ -141,26 +132,25 @@ final class Handler extends URLStreamHandler { private static String findCapeOwner(URL address) { switch (address.getHost()) { case "www.minecraft.net": - return stripPng(strip(address.getQuery(), "user=")); + return stripIfPrefixed(address.getQuery(), "user="); case "s3.amazonaws.com": case "skins.minecraft.net": - return stripPng(strip(address.getPath(), "/MinecraftCloaks/")); + return stripIfPrefixed(address.getPath(), "/MinecraftCloaks/"); } return null; } - private static String stripPng(String string) { - if (string != null && string.endsWith(".png")) - return string.substring(0, string.lastIndexOf('.')); + private static String stripIfPrefixed(String string, String prefix) { + if (string != null && string.startsWith(prefix)) { + string = string.substring(prefix.length()); - return string; - } + if (string.endsWith(".png")) + string = string.substring(0, string.lastIndexOf('.')); - private static String strip(String string, String prefix) { - if (string != null && string.startsWith(prefix)) - return string.substring(prefix.length()); + return string; + } return null; } diff --git a/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java b/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java index 927c10142..e26dfb6f1 100644 --- a/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java +++ b/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java @@ -41,8 +41,8 @@ import java.net.URLStreamHandlerFactory; import org.prismlauncher.fix.Fix; import org.prismlauncher.utils.Parameters; -import org.prismlauncher.utils.UrlUtils; import org.prismlauncher.utils.logging.Log; +import org.prismlauncher.utils.url.UrlUtils; /** * Fixes skins by redirecting to other URLs. diff --git a/libraries/launcher/org/prismlauncher/utils/JsonParser.java b/libraries/launcher/org/prismlauncher/utils/JsonParser.java index d1803c896..378e58235 100644 --- a/libraries/launcher/org/prismlauncher/utils/JsonParser.java +++ b/libraries/launcher/org/prismlauncher/utils/JsonParser.java @@ -59,6 +59,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.prismlauncher.exception.JsonParseException; + /** * Single-file JSON parser to allow for usage in versions without GSON. */ @@ -420,14 +422,4 @@ public final class JsonParser { return character() == 'n' && read() == 'u' && read() == 'l' && read() == 'l'; } - public static class JsonParseException extends IOException { - - private static final long serialVersionUID = 1L; - - public JsonParseException(String message) { - super(message); - } - - } - } \ No newline at end of file diff --git a/libraries/launcher/org/prismlauncher/utils/url/NullConnection.java b/libraries/launcher/org/prismlauncher/utils/url/NullConnection.java new file mode 100644 index 000000000..628488e8d --- /dev/null +++ b/libraries/launcher/org/prismlauncher/utils/url/NullConnection.java @@ -0,0 +1,66 @@ +// 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.url; + +import java.io.IOException; +import java.net.HttpURLConnection; + +/** + * Spoof 404 response from server to avoid unnecessary requests. + */ +public final class NullConnection extends HttpURLConnection { + + public static final NullConnection INSTANCE = new NullConnection(); + + public NullConnection() { + super(null); + } + + @Override + public void connect() throws IOException { + responseCode = 404; + } + + @Override + public void disconnect() { + } + + @Override + public boolean usingProxy() { + return false; + } + +} diff --git a/libraries/launcher/org/prismlauncher/utils/UrlUtils.java b/libraries/launcher/org/prismlauncher/utils/url/UrlUtils.java similarity index 74% rename from libraries/launcher/org/prismlauncher/utils/UrlUtils.java rename to libraries/launcher/org/prismlauncher/utils/url/UrlUtils.java index 362feb4e3..751f833fd 100644 --- a/libraries/launcher/org/prismlauncher/utils/UrlUtils.java +++ b/libraries/launcher/org/prismlauncher/utils/url/UrlUtils.java @@ -33,7 +33,7 @@ * along with this program. If not, see . */ -package org.prismlauncher.utils; +package org.prismlauncher.utils.url; import java.io.IOException; import java.lang.invoke.MethodHandle; @@ -54,7 +54,6 @@ public final class UrlUtils { private static URLStreamHandler http; private static MethodHandle openConnection; - private static MethodHandle openConnectionProxied; static { try { @@ -64,18 +63,10 @@ public final class UrlUtils { getURLStreamHandler.setAccessible(true); http = (URLStreamHandler) getURLStreamHandler.invoke(null, "http"); - // reflection is required due to not having access - // unreflect is used due to the potential frequency of calls - Method openConnectionReflect = URLStreamHandler.class.getDeclaredMethod("openConnection", URL.class); + Method openConnectionReflect = URLStreamHandler.class.getDeclaredMethod("openConnection", URL.class, + Proxy.class); openConnectionReflect.setAccessible(true); openConnection = MethodHandles.lookup().unreflect(openConnectionReflect); - - // a second method which takes in a proxy is required for a fully-functional - // URLStreamHandler - Method openConnectionReflectProxied = URLStreamHandler.class.getDeclaredMethod("openConnection", URL.class, - Proxy.class); - openConnectionReflectProxied.setAccessible(true); - openConnectionProxied = MethodHandles.lookup().unreflect(openConnectionReflectProxied); } catch (Throwable e) { Log.error("URL reflection failed - some features may not work", e); } @@ -87,20 +78,16 @@ public final class UrlUtils { * @return true if all features can be used */ public static boolean isSupported() { - return http != null && openConnection != null && openConnectionProxied != null; - } - - public static URLConnection openHttpConnection(URL url) throws IOException { - return openConnection(http, url); + return http != null && openConnection != null; } public static URLConnection openHttpConnection(URL url, Proxy proxy) throws IOException { return openConnection(http, url, proxy); } - public static URLConnection openConnection(URLStreamHandler handler, URL url) throws IOException { + public static URLConnection openConnection(URLStreamHandler handler, URL url, Proxy proxy) throws IOException { try { - return (URLConnection) openConnection.invokeExact(handler, url); + return (URLConnection) openConnection.invokeExact(handler, url, proxy); } catch (IOException | Error | RuntimeException e) { throw e; // rethrow if possible } catch (Throwable e) { @@ -108,14 +95,4 @@ public final class UrlUtils { } } - public static URLConnection openConnection(URLStreamHandler handler, URL url, Proxy proxy) throws IOException { - try { - return (URLConnection) openConnectionProxied.invokeExact(handler, url, proxy); - } catch (IOException | Error | RuntimeException e) { - throw e; - } catch (Throwable e) { - throw new Error(e); - } - } - } From bfa5fe1598a6a60045fe01e589cc5f67cfe35f5a Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sun, 20 Nov 2022 09:55:30 +0000 Subject: [PATCH 006/268] Better skin fix error handling Signed-off-by: TheKodeToad --- libraries/launcher/org/prismlauncher/fix/Fixes.java | 13 ++++++++++--- .../org/prismlauncher/fix/skins/SkinFix.java | 8 +++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/libraries/launcher/org/prismlauncher/fix/Fixes.java b/libraries/launcher/org/prismlauncher/fix/Fixes.java index e099b653c..e85bcfd42 100644 --- a/libraries/launcher/org/prismlauncher/fix/Fixes.java +++ b/libraries/launcher/org/prismlauncher/fix/Fixes.java @@ -40,6 +40,7 @@ import java.util.List; import org.prismlauncher.fix.skins.SkinFix; import org.prismlauncher.utils.Parameters; +import org.prismlauncher.utils.logging.Log; public final class Fixes { @@ -48,9 +49,15 @@ public final class Fixes { public static void apply(Parameters params) { List fixes = params.getList("fixes", Collections.emptyList()); - for (Fix fix : FIXES) - if (fixes.contains(fix.getName()) && fix.isApplicable(params)) - fix.apply(); + for (Fix fix : FIXES) { + if (fixes.contains(fix.getName()) && fix.isApplicable(params)) { + try { + fix.apply(); + } catch (Throwable e) { + Log.error("Could not apply " + fix.getName(), e); + } + } + } } } diff --git a/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java b/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java index e26dfb6f1..ab978dc58 100644 --- a/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java +++ b/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java @@ -71,7 +71,13 @@ public final class SkinFix implements Fix, URLStreamHandlerFactory { @Override public void apply() { - URL.setURLStreamHandlerFactory(this); + try { + URL.setURLStreamHandlerFactory(this); + } catch (Error e) { + Log.warning("Cannot apply skin fix"); + Log.warning("URLStreamHandlerFactory is already set"); + Log.warning("Turning off legacy skin fix in Settings > Miscellaneous will silence the warnings"); + } } @Override From 8a6776731af94e768831440f27d3e9e8e81ab7bf Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Tue, 22 Nov 2022 16:56:58 +0000 Subject: [PATCH 007/268] Use reflection to access DatatypeConverter Signed-off-by: TheKodeToad --- .../org/prismlauncher/fix/skins/SkinFix.java | 3 +- .../org/prismlauncher/utils/Base64.java | 53 +++++++++++++++---- .../org/prismlauncher/utils/url/UrlUtils.java | 6 +++ 3 files changed, 52 insertions(+), 10 deletions(-) diff --git a/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java b/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java index ab978dc58..66b5fda62 100644 --- a/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java +++ b/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java @@ -40,6 +40,7 @@ import java.net.URLStreamHandler; import java.net.URLStreamHandlerFactory; import org.prismlauncher.fix.Fix; +import org.prismlauncher.utils.Base64; import org.prismlauncher.utils.Parameters; import org.prismlauncher.utils.logging.Log; import org.prismlauncher.utils.url.UrlUtils; @@ -59,7 +60,7 @@ public final class SkinFix implements Fix, URLStreamHandlerFactory { @Override public boolean isApplicable(Parameters params) { - if (!UrlUtils.isSupported()) { + if (!UrlUtils.isSupported() || !Base64.isSupported()) { Log.warning("Cannot access the necessary Java internals for skin fix"); Log.warning("Try adding '--add-opens java.base/java.net=ALL-UNNAMED' to your Java arguments"); Log.warning("Alternatively, turning off legacy skin fix in Settings > Miscellaneous will silence the warnings"); diff --git a/libraries/launcher/org/prismlauncher/utils/Base64.java b/libraries/launcher/org/prismlauncher/utils/Base64.java index 20a189f46..508df872d 100644 --- a/libraries/launcher/org/prismlauncher/utils/Base64.java +++ b/libraries/launcher/org/prismlauncher/utils/Base64.java @@ -35,25 +35,60 @@ package org.prismlauncher.utils; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.nio.charset.StandardCharsets; -import javax.xml.bind.DatatypeConverter; +import org.prismlauncher.utils.logging.Log; +/** + * Uses Base64 with Java 8 or later, otherwise DatatypeConverter. In the latter + * case, reflection is used to allow using newer compilers. + */ public final class Base64 { - private static boolean legacy; + private static boolean supported = true; + private static MethodHandle legacy; - public static byte[] decode(String input) { - if (!legacy) { + static { + try { + Class.forName("java.util.Base64"); + } catch (ClassNotFoundException e) { try { - return java.util.Base64.getDecoder().decode(input.getBytes(StandardCharsets.UTF_8)); - } catch (NoClassDefFoundError e) { - legacy = true; + Class datatypeConverter = Class.forName("javax.xml.bind.DatatypeConverter"); + legacy = MethodHandles.lookup().findStatic(datatypeConverter, "parseBase64Binary", + MethodType.methodType(byte[].class, String.class)); + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e1) { + Log.error("Base64 not supported", e1); + supported = false; } } + } - // support for Java versions < 8 - return DatatypeConverter.parseBase64Binary(input); + /** + * Determines whether base64 is supported. + * + * @return true if base64 can be parsed + */ + public static boolean isSupported() { + return supported; + } + + public static byte[] decode(String input) { + if (!isSupported()) + throw new UnsupportedOperationException(); + + if (legacy == null) + return java.util.Base64.getDecoder().decode(input.getBytes(StandardCharsets.UTF_8)); + + try { + return (byte[]) legacy.invokeExact(input); + } catch (Error | RuntimeException e) { + throw e; + } catch (Throwable e) { + throw new Error(e); + } } } diff --git a/libraries/launcher/org/prismlauncher/utils/url/UrlUtils.java b/libraries/launcher/org/prismlauncher/utils/url/UrlUtils.java index 751f833fd..05912d005 100644 --- a/libraries/launcher/org/prismlauncher/utils/url/UrlUtils.java +++ b/libraries/launcher/org/prismlauncher/utils/url/UrlUtils.java @@ -82,10 +82,16 @@ public final class UrlUtils { } public static URLConnection openHttpConnection(URL url, Proxy proxy) throws IOException { + if (http == null) + throw new UnsupportedOperationException(); + return openConnection(http, url, proxy); } public static URLConnection openConnection(URLStreamHandler handler, URL url, Proxy proxy) throws IOException { + if (openConnection == null) + throw new UnsupportedOperationException(); + try { return (URLConnection) openConnection.invokeExact(handler, url, proxy); } catch (IOException | Error | RuntimeException e) { From 87bcefd08ac5023a6b406bc6c909ddff7d6ca1bf Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Thu, 15 Dec 2022 15:28:17 +0000 Subject: [PATCH 008/268] Automatically add add-opens if Java version >= 9 Signed-off-by: TheKodeToad --- launcher/java/JavaVersion.cpp | 10 +++++----- launcher/java/JavaVersion.h | 2 ++ launcher/minecraft/MinecraftInstance.cpp | 10 +++++----- .../launcher/org/prismlauncher/fix/skins/SkinFix.java | 3 +-- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/launcher/java/JavaVersion.cpp b/launcher/java/JavaVersion.cpp index 0e4fc1d3c..cfb12fbf3 100644 --- a/launcher/java/JavaVersion.cpp +++ b/launcher/java/JavaVersion.cpp @@ -50,11 +50,11 @@ QString JavaVersion::toString() bool JavaVersion::requiresPermGen() { - if(m_parseable) - { - return m_major < 8; - } - return true; + return !m_parseable || m_major < 8; +} + +bool JavaVersion::isModular() { + return m_parseable && m_major >= 9; } bool JavaVersion::operator<(const JavaVersion &rhs) diff --git a/launcher/java/JavaVersion.h b/launcher/java/JavaVersion.h index 9bbf06425..0551214ad 100644 --- a/launcher/java/JavaVersion.h +++ b/launcher/java/JavaVersion.h @@ -25,6 +25,8 @@ public: bool requiresPermGen(); + bool isModular(); + QString toString(); int major() diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 91de955ad..4434936f0 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -440,15 +440,15 @@ QStringList MinecraftInstance::javaArguments() args << "-Duser.language=en"; + if (javaVersion.isModular() && traits().contains("legacySkins") && settings()->get("LegacySkinFix").toBool()) + args << "--add-opens" << "java.base/java.net=ALL-UNNAMED"; + return args; } -QString MinecraftInstance::getLauncher() -{ - auto profile = m_components->getProfile(); - +QString MinecraftInstance::getLauncher() { // use legacy launcher if the traits are set - if (profile->getTraits().contains("legacyLaunch") || profile->getTraits().contains("alphaLaunch")) + if (traits().contains("legacyLaunch") || traits().contains("alphaLaunch")) return "legacy"; return "standard"; diff --git a/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java b/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java index 66b5fda62..19b15605a 100644 --- a/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java +++ b/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java @@ -62,8 +62,7 @@ public final class SkinFix implements Fix, URLStreamHandlerFactory { public boolean isApplicable(Parameters params) { if (!UrlUtils.isSupported() || !Base64.isSupported()) { Log.warning("Cannot access the necessary Java internals for skin fix"); - Log.warning("Try adding '--add-opens java.base/java.net=ALL-UNNAMED' to your Java arguments"); - Log.warning("Alternatively, turning off legacy skin fix in Settings > Miscellaneous will silence the warnings"); + Log.warning("Turning off legacy skin fix in Settings > Miscellaneous will silence the warnings"); return false; } From 94067f34cf8a42c735c264ca30740bfec6f5af3a Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Wed, 21 Dec 2022 18:13:41 +0000 Subject: [PATCH 009/268] Try to make some of the suggested changes Signed-off-by: TheKodeToad --- launcher/minecraft/MinecraftInstance.cpp | 6 +- libraries/launcher/CMakeLists.txt | 3 +- .../org/prismlauncher/fix/skins/Handler.java | 7 +- .../org/prismlauncher/fix/skins/SkinFix.java | 2 +- .../utils/{url => }/UrlUtils.java | 14 ++-- .../utils/url/NullConnection.java | 66 ------------------- 6 files changed, 14 insertions(+), 84 deletions(-) rename libraries/launcher/org/prismlauncher/utils/{url => }/UrlUtils.java (91%) delete mode 100644 libraries/launcher/org/prismlauncher/utils/url/NullConnection.java diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 4434936f0..378af0e10 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -192,7 +192,7 @@ void MinecraftInstance::loadSpecificSettings() m_settings->registerSetting("JoinServerOnLaunch", false); m_settings->registerSetting("JoinServerOnLaunchAddress", ""); - m_settings->registerSetting("LegacySkinFix", true); + m_settings->registerSetting("LegacySkinFix", true); qDebug() << "Instance-type specific settings were loaded!"; @@ -441,7 +441,9 @@ QStringList MinecraftInstance::javaArguments() args << "-Duser.language=en"; if (javaVersion.isModular() && traits().contains("legacySkins") && settings()->get("LegacySkinFix").toBool()) - args << "--add-opens" << "java.base/java.net=ALL-UNNAMED"; + // allow reflective access to java.net - required by the skin fix + args << "--add-opens" + << "java.base/java.net=ALL-UNNAMED"; return args; } diff --git a/libraries/launcher/CMakeLists.txt b/libraries/launcher/CMakeLists.txt index 65a8241eb..acc276218 100644 --- a/libraries/launcher/CMakeLists.txt +++ b/libraries/launcher/CMakeLists.txt @@ -23,8 +23,7 @@ set(SRC org/prismlauncher/utils/JsonParser.java org/prismlauncher/utils/Parameters.java org/prismlauncher/utils/ReflectionUtils.java - org/prismlauncher/utils/url/NullConnection.java - org/prismlauncher/utils/url/UrlUtils.java + org/prismlauncher/utils/UrlUtils.java org/prismlauncher/utils/logging/Level.java org/prismlauncher/utils/logging/Log.java net/minecraft/Launcher.java diff --git a/libraries/launcher/org/prismlauncher/fix/skins/Handler.java b/libraries/launcher/org/prismlauncher/fix/skins/Handler.java index 0f5b556f8..fa306ed44 100644 --- a/libraries/launcher/org/prismlauncher/fix/skins/Handler.java +++ b/libraries/launcher/org/prismlauncher/fix/skins/Handler.java @@ -45,8 +45,7 @@ import java.util.Map; import org.prismlauncher.utils.Base64; import org.prismlauncher.utils.JsonParser; -import org.prismlauncher.utils.url.NullConnection; -import org.prismlauncher.utils.url.UrlUtils; +import org.prismlauncher.utils.UrlUtils; @SuppressWarnings("unchecked") final class Handler extends URLStreamHandler { @@ -71,10 +70,6 @@ final class Handler extends URLStreamHandler { @Override protected URLConnection openConnection(URL address, Proxy proxy) throws IOException { address = redirect(address); - - if (address == null) - return NullConnection.INSTANCE; - return UrlUtils.openHttpConnection(address, proxy); } diff --git a/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java b/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java index 19b15605a..3dd747376 100644 --- a/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java +++ b/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java @@ -42,8 +42,8 @@ import java.net.URLStreamHandlerFactory; import org.prismlauncher.fix.Fix; import org.prismlauncher.utils.Base64; import org.prismlauncher.utils.Parameters; +import org.prismlauncher.utils.UrlUtils; import org.prismlauncher.utils.logging.Log; -import org.prismlauncher.utils.url.UrlUtils; /** * Fixes skins by redirecting to other URLs. diff --git a/libraries/launcher/org/prismlauncher/utils/url/UrlUtils.java b/libraries/launcher/org/prismlauncher/utils/UrlUtils.java similarity index 91% rename from libraries/launcher/org/prismlauncher/utils/url/UrlUtils.java rename to libraries/launcher/org/prismlauncher/utils/UrlUtils.java index 05912d005..1ab0eb918 100644 --- a/libraries/launcher/org/prismlauncher/utils/url/UrlUtils.java +++ b/libraries/launcher/org/prismlauncher/utils/UrlUtils.java @@ -33,7 +33,7 @@ * along with this program. If not, see . */ -package org.prismlauncher.utils.url; +package org.prismlauncher.utils; import java.io.IOException; import java.lang.invoke.MethodHandle; @@ -47,8 +47,8 @@ import java.net.URLStreamHandler; import org.prismlauncher.utils.logging.Log; /** - * A utility class for URLs which uses reflection to access hidden methods. - * Unfortunately not supported on newer Java versions. + * A utility class for URLs which uses reflection to access constructors for + * internal classes. */ public final class UrlUtils { @@ -57,12 +57,12 @@ public final class UrlUtils { static { try { - // invoke URL.getURLStreamHandler to obtain some of the default handlers before - // they are overridden + // we first obtain the stock URLStreamHandler for http as we overwrite it later Method getURLStreamHandler = URL.class.getDeclaredMethod("getURLStreamHandler", String.class); getURLStreamHandler.setAccessible(true); http = (URLStreamHandler) getURLStreamHandler.invoke(null, "http"); + // we next find the openConnection method Method openConnectionReflect = URLStreamHandler.class.getDeclaredMethod("openConnection", URL.class, Proxy.class); openConnectionReflect.setAccessible(true); @@ -97,8 +97,8 @@ public final class UrlUtils { } catch (IOException | Error | RuntimeException e) { throw e; // rethrow if possible } catch (Throwable e) { - throw new Error(e); // otherwise, wrap in Error + throw new AssertionError(e); // oh dear! this isn't meant to happen } } -} +} \ No newline at end of file diff --git a/libraries/launcher/org/prismlauncher/utils/url/NullConnection.java b/libraries/launcher/org/prismlauncher/utils/url/NullConnection.java deleted file mode 100644 index 628488e8d..000000000 --- a/libraries/launcher/org/prismlauncher/utils/url/NullConnection.java +++ /dev/null @@ -1,66 +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.url; - -import java.io.IOException; -import java.net.HttpURLConnection; - -/** - * Spoof 404 response from server to avoid unnecessary requests. - */ -public final class NullConnection extends HttpURLConnection { - - public static final NullConnection INSTANCE = new NullConnection(); - - public NullConnection() { - super(null); - } - - @Override - public void connect() throws IOException { - responseCode = 404; - } - - @Override - public void disconnect() { - } - - @Override - public boolean usingProxy() { - return false; - } - -} From 884bd8549537cb67a9e59c83ab4ca761491ff27b Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 23 Dec 2022 09:35:29 +0000 Subject: [PATCH 010/268] Skin fix -> online fixes Signed-off-by: TheKodeToad --- launcher/minecraft/MinecraftInstance.cpp | 8 +-- .../pages/instance/InstanceSettingsPage.cpp | 8 +-- .../ui/pages/instance/InstanceSettingsPage.ui | 6 +- libraries/launcher/CMakeLists.txt | 6 +- .../launcher/org/prismlauncher/fix/Fixes.java | 21 +------ .../fix/{Fix.java => online/Handler.java} | 38 ++++++------ .../SkinFix.java => online/OnlineFixes.java} | 26 ++------- .../Handler.java => online/SkinFix.java} | 58 ++----------------- 8 files changed, 44 insertions(+), 127 deletions(-) rename libraries/launcher/org/prismlauncher/fix/{Fix.java => online/Handler.java} (73%) rename libraries/launcher/org/prismlauncher/fix/{skins/SkinFix.java => online/OnlineFixes.java} (82%) rename libraries/launcher/org/prismlauncher/fix/{skins/Handler.java => online/SkinFix.java} (56%) diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 378af0e10..ec916f26f 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -192,7 +192,7 @@ void MinecraftInstance::loadSpecificSettings() m_settings->registerSetting("JoinServerOnLaunch", false); m_settings->registerSetting("JoinServerOnLaunchAddress", ""); - m_settings->registerSetting("LegacySkinFix", true); + m_settings->registerSetting("OnlineFixes", true); qDebug() << "Instance-type specific settings were loaded!"; @@ -440,7 +440,7 @@ QStringList MinecraftInstance::javaArguments() args << "-Duser.language=en"; - if (javaVersion.isModular() && traits().contains("legacySkins") && settings()->get("LegacySkinFix").toBool()) + if (javaVersion.isModular() && traits().contains("legacyServices") && settings()->get("OnlineFixes").toBool()) // allow reflective access to java.net - required by the skin fix args << "--add-opens" << "java.base/java.net=ALL-UNNAMED"; @@ -665,8 +665,8 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS launchScript += "traits " + trait + "\n"; } - if (profile->getTraits().contains("legacySkins") && settings()->get("LegacySkinFix").toBool()) - launchScript += "fixes legacySkinFix\n"; + if (profile->getTraits().contains("legacyServices") && settings()->get("OnlineFixes").toBool()) + launchScript += "onlineFixes true\n"; launchScript += "launcher " + getLauncher() + "\n"; diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index 537271a2c..23299b1dc 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -276,8 +276,8 @@ void InstanceSettingsPage::applySettings() m_settings->reset("JoinServerOnLaunchAddress"); } - bool legacySkinFix = ui->legacySkinFix->isChecked(); - m_settings->set("LegacySkinFix", legacySkinFix); + bool onlineFixes = ui->onlineFixes->isChecked(); + m_settings->set("OnlineFixes", onlineFixes); // FIXME: This should probably be called by a signal instead m_instance->updateRuntimeContext(); @@ -377,8 +377,8 @@ void InstanceSettingsPage::loadSettings() ui->serverJoinGroupBox->setChecked(m_settings->get("JoinServerOnLaunch").toBool()); ui->serverJoinAddress->setText(m_settings->get("JoinServerOnLaunchAddress").toString()); - ui->legacySkinFix->setChecked(m_settings->get("LegacySkinFix").toBool()); - ui->legacySkinFix->setVisible(m_instance->traits().contains("legacySkins")); + ui->onlineFixes->setChecked(m_settings->get("OnlineFixes").toBool()); + ui->onlineFixes->setVisible(m_instance->traits().contains("legacyServices")); } void InstanceSettingsPage::on_javaDetectBtn_clicked() diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index ca49cbbe5..7b42b8397 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -609,12 +609,12 @@ - + - Enables support for modern skins on old versions. + <html><head/><body><p>Fixes usages of old online services which are no longer operating by emulating them or redirecting to their modern counterparts.</p><p>This currently only allows modern skins to be used.</p></body></html> - Enable legacy skin fix + Enable online fixes diff --git a/libraries/launcher/CMakeLists.txt b/libraries/launcher/CMakeLists.txt index acc276218..71996ae33 100644 --- a/libraries/launcher/CMakeLists.txt +++ b/libraries/launcher/CMakeLists.txt @@ -15,10 +15,10 @@ set(SRC org/prismlauncher/launcher/impl/legacy/LegacyFrame.java org/prismlauncher/exception/ParameterNotFoundException.java org/prismlauncher/exception/ParseException.java - org/prismlauncher/fix/Fix.java org/prismlauncher/fix/Fixes.java - org/prismlauncher/fix/skins/SkinFix.java - org/prismlauncher/fix/skins/Handler.java + org/prismlauncher/fix/online/OnlineFixes.java + org/prismlauncher/fix/online/SkinFix.java + org/prismlauncher/fix/online/Handler.java org/prismlauncher/utils/Base64.java org/prismlauncher/utils/JsonParser.java org/prismlauncher/utils/Parameters.java diff --git a/libraries/launcher/org/prismlauncher/fix/Fixes.java b/libraries/launcher/org/prismlauncher/fix/Fixes.java index e85bcfd42..429964ad9 100644 --- a/libraries/launcher/org/prismlauncher/fix/Fixes.java +++ b/libraries/launcher/org/prismlauncher/fix/Fixes.java @@ -35,29 +35,14 @@ package org.prismlauncher.fix; -import java.util.Collections; -import java.util.List; - -import org.prismlauncher.fix.skins.SkinFix; +import org.prismlauncher.fix.online.OnlineFixes; import org.prismlauncher.utils.Parameters; -import org.prismlauncher.utils.logging.Log; public final class Fixes { - private static final Fix[] FIXES = { new SkinFix() }; - public static void apply(Parameters params) { - List fixes = params.getList("fixes", Collections.emptyList()); - - for (Fix fix : FIXES) { - if (fixes.contains(fix.getName()) && fix.isApplicable(params)) { - try { - fix.apply(); - } catch (Throwable e) { - Log.error("Could not apply " + fix.getName(), e); - } - } - } + if ("true".equalsIgnoreCase(params.getString("onlineFixes", null))) + OnlineFixes.apply(); } } diff --git a/libraries/launcher/org/prismlauncher/fix/Fix.java b/libraries/launcher/org/prismlauncher/fix/online/Handler.java similarity index 73% rename from libraries/launcher/org/prismlauncher/fix/Fix.java rename to libraries/launcher/org/prismlauncher/fix/online/Handler.java index 3ecd2e90a..b5b4de28e 100644 --- a/libraries/launcher/org/prismlauncher/fix/Fix.java +++ b/libraries/launcher/org/prismlauncher/fix/online/Handler.java @@ -33,31 +33,27 @@ * along with this program. If not, see . */ -package org.prismlauncher.fix; +package org.prismlauncher.fix.online; -import org.prismlauncher.utils.Parameters; +import java.io.IOException; +import java.net.Proxy; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; -public interface Fix { +import org.prismlauncher.utils.UrlUtils; - /** - * Gets the name of the fix. If the name isn't passed into the program, the fix - * won't run. - * - * @return The name - */ - String getName(); +final class Handler extends URLStreamHandler { - /** - * Determines whether the fix will be run. This is additional to the name check. - * - * @param params The parameters - * @return true to proceed to applying the fix - */ - boolean isApplicable(Parameters params); + @Override + protected URLConnection openConnection(URL address) throws IOException { + return openConnection(address, null); + } - /** - * Applies the fix. - */ - void apply(); + @Override + protected URLConnection openConnection(URL address, Proxy proxy) throws IOException { + address = SkinFix.redirect(address); + return UrlUtils.openHttpConnection(address, proxy); + } } diff --git a/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java b/libraries/launcher/org/prismlauncher/fix/online/OnlineFixes.java similarity index 82% rename from libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java rename to libraries/launcher/org/prismlauncher/fix/online/OnlineFixes.java index 3dd747376..c344a8e73 100644 --- a/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java +++ b/libraries/launcher/org/prismlauncher/fix/online/OnlineFixes.java @@ -33,15 +33,13 @@ * along with this program. If not, see . */ -package org.prismlauncher.fix.skins; +package org.prismlauncher.fix.online; import java.net.URL; import java.net.URLStreamHandler; import java.net.URLStreamHandlerFactory; -import org.prismlauncher.fix.Fix; import org.prismlauncher.utils.Base64; -import org.prismlauncher.utils.Parameters; import org.prismlauncher.utils.UrlUtils; import org.prismlauncher.utils.logging.Log; @@ -51,31 +49,19 @@ import org.prismlauncher.utils.logging.Log; * @see {@link Handler} * @see {@link UrlUtils} */ -public final class SkinFix implements Fix, URLStreamHandlerFactory { +public final class OnlineFixes implements URLStreamHandlerFactory { - @Override - public String getName() { - return "legacySkinFix"; - } - - @Override - public boolean isApplicable(Parameters params) { + public static void apply() { if (!UrlUtils.isSupported() || !Base64.isSupported()) { Log.warning("Cannot access the necessary Java internals for skin fix"); Log.warning("Turning off legacy skin fix in Settings > Miscellaneous will silence the warnings"); - return false; + return; } - return true; - } - - @Override - public void apply() { try { - URL.setURLStreamHandlerFactory(this); + URL.setURLStreamHandlerFactory(new OnlineFixes()); } catch (Error e) { - Log.warning("Cannot apply skin fix"); - Log.warning("URLStreamHandlerFactory is already set"); + Log.warning("Cannot apply skin fix: URLStreamHandlerFactory is already set"); Log.warning("Turning off legacy skin fix in Settings > Miscellaneous will silence the warnings"); } } diff --git a/libraries/launcher/org/prismlauncher/fix/skins/Handler.java b/libraries/launcher/org/prismlauncher/fix/online/SkinFix.java similarity index 56% rename from libraries/launcher/org/prismlauncher/fix/skins/Handler.java rename to libraries/launcher/org/prismlauncher/fix/online/SkinFix.java index fa306ed44..f1e480776 100644 --- a/libraries/launcher/org/prismlauncher/fix/skins/Handler.java +++ b/libraries/launcher/org/prismlauncher/fix/online/SkinFix.java @@ -1,56 +1,17 @@ -// 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.fix.skins; +package org.prismlauncher.fix.online; import java.io.IOException; import java.io.InputStream; -import java.net.Proxy; import java.net.URL; -import java.net.URLConnection; -import java.net.URLStreamHandler; import java.util.Map; import org.prismlauncher.utils.Base64; import org.prismlauncher.utils.JsonParser; -import org.prismlauncher.utils.UrlUtils; @SuppressWarnings("unchecked") -final class Handler extends URLStreamHandler { +final class SkinFix { - private URL redirect(URL address) throws IOException { + static URL redirect(URL address) throws IOException { String skinOwner = findSkinOwner(address); if (skinOwner != null) return convert(skinOwner, "SKIN"); @@ -62,18 +23,7 @@ final class Handler extends URLStreamHandler { return address; } - @Override - protected URLConnection openConnection(URL address) throws IOException { - return openConnection(address, null); - } - - @Override - protected URLConnection openConnection(URL address, Proxy proxy) throws IOException { - address = redirect(address); - return UrlUtils.openHttpConnection(address, proxy); - } - - private URL convert(String owner, String name) throws IOException { + private static URL convert(String owner, String name) throws IOException { Map textures = getTextures(owner); if (textures != null) { From 5c96b1c6289d0f31b073153668140e84066e87f2 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sun, 25 Dec 2022 09:45:42 +0000 Subject: [PATCH 011/268] Remove MIT license from JsonParser I don't mind, and it's my code. Signed-off-by: TheKodeToad --- .../org/prismlauncher/utils/JsonParser.java | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/libraries/launcher/org/prismlauncher/utils/JsonParser.java b/libraries/launcher/org/prismlauncher/utils/JsonParser.java index 378e58235..5f10e9a7b 100644 --- a/libraries/launcher/org/prismlauncher/utils/JsonParser.java +++ b/libraries/launcher/org/prismlauncher/utils/JsonParser.java @@ -31,19 +31,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: - * - * MIT License - * - * Copyright (c) 2022 TheKodeToad - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 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. */ package org.prismlauncher.utils; @@ -422,4 +409,4 @@ public final class JsonParser { return character() == 'n' && read() == 'u' && read() == 'l' && read() == 'l'; } -} \ No newline at end of file +} From cb32711077af62b527abdc4af5aabce44f4e5d21 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sun, 25 Dec 2022 11:40:49 +0000 Subject: [PATCH 012/268] Slim skin fix - thanks to @craftycodie and @DelofJ ! Signed-off-by: TheKodeToad --- libraries/launcher/CMakeLists.txt | 5 +- .../org/prismlauncher/fix/online/Handler.java | 12 ++- .../prismlauncher/fix/online/OnlineFixes.java | 2 +- .../org/prismlauncher/fix/online/SkinFix.java | 94 +++++++++------- .../prismlauncher/utils/api/MojangApi.java | 101 ++++++++++++++++++ .../org/prismlauncher/utils/api/Texture.java | 61 +++++++++++ .../utils/url/CustomUrlConnection.java | 79 ++++++++++++++ .../utils/{ => url}/UrlUtils.java | 12 ++- 8 files changed, 322 insertions(+), 44 deletions(-) create mode 100644 libraries/launcher/org/prismlauncher/utils/api/MojangApi.java create mode 100644 libraries/launcher/org/prismlauncher/utils/api/Texture.java create mode 100644 libraries/launcher/org/prismlauncher/utils/url/CustomUrlConnection.java rename libraries/launcher/org/prismlauncher/utils/{ => url}/UrlUtils.java (89%) diff --git a/libraries/launcher/CMakeLists.txt b/libraries/launcher/CMakeLists.txt index 71996ae33..bf8f0baa2 100644 --- a/libraries/launcher/CMakeLists.txt +++ b/libraries/launcher/CMakeLists.txt @@ -23,9 +23,12 @@ set(SRC org/prismlauncher/utils/JsonParser.java org/prismlauncher/utils/Parameters.java org/prismlauncher/utils/ReflectionUtils.java - org/prismlauncher/utils/UrlUtils.java + org/prismlauncher/utils/api/MojangApi.java + org/prismlauncher/utils/api/Texture.java org/prismlauncher/utils/logging/Level.java org/prismlauncher/utils/logging/Log.java + org/prismlauncher/utils/url/UrlUtils.java + org/prismlauncher/utils/url/CustomUrlConnection.java net/minecraft/Launcher.java ) add_jar(NewLaunch ${SRC}) diff --git a/libraries/launcher/org/prismlauncher/fix/online/Handler.java b/libraries/launcher/org/prismlauncher/fix/online/Handler.java index b5b4de28e..618683119 100644 --- a/libraries/launcher/org/prismlauncher/fix/online/Handler.java +++ b/libraries/launcher/org/prismlauncher/fix/online/Handler.java @@ -41,7 +41,7 @@ import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandler; -import org.prismlauncher.utils.UrlUtils; +import org.prismlauncher.utils.url.UrlUtils; final class Handler extends URLStreamHandler { @@ -52,8 +52,14 @@ final class Handler extends URLStreamHandler { @Override protected URLConnection openConnection(URL address, Proxy proxy) throws IOException { - address = SkinFix.redirect(address); - return UrlUtils.openHttpConnection(address, proxy); + URLConnection result; + + // try skin fix + result = SkinFix.openConnection(address, proxy); + if (result != null) + return result; + + return UrlUtils.openConnection(address, proxy); } } diff --git a/libraries/launcher/org/prismlauncher/fix/online/OnlineFixes.java b/libraries/launcher/org/prismlauncher/fix/online/OnlineFixes.java index c344a8e73..5f9ff1014 100644 --- a/libraries/launcher/org/prismlauncher/fix/online/OnlineFixes.java +++ b/libraries/launcher/org/prismlauncher/fix/online/OnlineFixes.java @@ -40,8 +40,8 @@ import java.net.URLStreamHandler; import java.net.URLStreamHandlerFactory; import org.prismlauncher.utils.Base64; -import org.prismlauncher.utils.UrlUtils; import org.prismlauncher.utils.logging.Log; +import org.prismlauncher.utils.url.UrlUtils; /** * Fixes skins by redirecting to other URLs. diff --git a/libraries/launcher/org/prismlauncher/fix/online/SkinFix.java b/libraries/launcher/org/prismlauncher/fix/online/SkinFix.java index f1e480776..b05e1f208 100644 --- a/libraries/launcher/org/prismlauncher/fix/online/SkinFix.java +++ b/libraries/launcher/org/prismlauncher/fix/online/SkinFix.java @@ -1,63 +1,85 @@ package org.prismlauncher.fix.online; +import java.awt.AlphaComposite; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.net.Proxy; import java.net.URL; -import java.util.Map; +import java.net.URLConnection; -import org.prismlauncher.utils.Base64; -import org.prismlauncher.utils.JsonParser; +import javax.imageio.ImageIO; + +import org.prismlauncher.utils.api.MojangApi; +import org.prismlauncher.utils.api.Texture; +import org.prismlauncher.utils.url.CustomUrlConnection; +import org.prismlauncher.utils.url.UrlUtils; -@SuppressWarnings("unchecked") final class SkinFix { - static URL redirect(URL address) throws IOException { + static URLConnection openConnection(URL address, Proxy proxy) throws IOException { String skinOwner = findSkinOwner(address); if (skinOwner != null) - return convert(skinOwner, "SKIN"); + // we need to correct the skin + return getSkinConnection(skinOwner, proxy); String capeOwner = findCapeOwner(address); - if (capeOwner != null) - return convert(capeOwner, "CAPE"); - - return address; - } - - private static URL convert(String owner, String name) throws IOException { - Map textures = getTextures(owner); - - if (textures != null) { - textures = (Map) textures.get(name); - if (textures == null) + if (capeOwner != null) { + // since we do not need to process the image, open a direct connection bypassing Handler + Texture texture = MojangApi.getTexture(MojangApi.getUuid(capeOwner), "CAPE"); + if (texture == null) return null; - return new URL((String) textures.get("url")); + return UrlUtils.openConnection(texture.getUrl(), proxy); } return null; } - private static Map getTextures(String owner) throws IOException { - try (InputStream in = new URL("https://api.mojang.com/users/profiles/minecraft/" + owner).openStream()) { - Map map = (Map) JsonParser.parse(in); - String id = (String) map.get("id"); + private static URLConnection getSkinConnection(String owner, Proxy proxy) throws IOException { + Texture texture = MojangApi.getTexture(MojangApi.getUuid(owner), "SKIN"); + if (texture == null) + return null; - try (InputStream profileIn = new URL("https://sessionserver.mojang.com/session/minecraft/profile/" + id) - .openStream()) { - Map profile = (Map) JsonParser.parse(profileIn); + URLConnection connection = UrlUtils.openConnection(texture.getUrl(), proxy); + try (InputStream in = connection.getInputStream()) { + // thank you craftycodie! + // this is heavily based on + // https://github.com/Mojang/LegacyLauncher/pull/33/files#diff-b61023785a9260651ca0a223573ea9acb5be5eec478bff626dafb7abe13ffebaR99 + BufferedImage image = ImageIO.read(in); + Graphics2D graphics = image.createGraphics(); + graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); - for (Map property : (Iterable>) profile.get("properties")) { - if (property.get("name").equals("textures")) { - Map result = (Map) JsonParser - .parse(new String(Base64.decode((String) property.get("value")))); - result = (Map) result.get("textures"); + BufferedImage subimage; - return result; - } - } - - return null; + if (image.getHeight() > 32) { + // flatten second layers + subimage = image.getSubimage(0, 32, 56, 16); + graphics.drawImage(subimage, 0, 16, null); } + + if (texture.isSlim()) { + // convert slim to wide + subimage = image.getSubimage(45, 16, 9, 16); + graphics.drawImage(subimage, 46, 16, null); + + subimage = image.getSubimage(49, 16, 2, 4); + graphics.drawImage(subimage, 50, 16, null); + + subimage = image.getSubimage(53, 20, 2, 12); + graphics.drawImage(subimage, 54, 20, null); + } + + graphics.dispose(); + + // crop the image + ByteArrayOutputStream out = new ByteArrayOutputStream(); + image = image.getSubimage(0, 0, 64, 32); + ImageIO.write(image, "png", out); + + return new CustomUrlConnection(out.toByteArray()); } } diff --git a/libraries/launcher/org/prismlauncher/utils/api/MojangApi.java b/libraries/launcher/org/prismlauncher/utils/api/MojangApi.java new file mode 100644 index 000000000..dc2794107 --- /dev/null +++ b/libraries/launcher/org/prismlauncher/utils/api/MojangApi.java @@ -0,0 +1,101 @@ +// 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.api; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Map; + +import org.prismlauncher.utils.Base64; +import org.prismlauncher.utils.JsonParser; + +/** + * Basic access to Mojang's Minecraft API. + */ +@SuppressWarnings("unchecked") +public final class MojangApi { + + public static String getUuid(String username) throws IOException { + try (InputStream in = new URL("https://api.mojang.com/users/profiles/minecraft/" + username).openStream()) { + Map map = (Map) JsonParser.parse(in); + return (String) map.get("id"); + } + } + + public static Texture getTexture(String player, String name) throws IOException { + Map map = getTextures(player); + + if (map != null) { + map = (Map) map.get(name); + if (map == null) + return null; + + URL url = new URL((String) map.get("url")); + boolean slim = false; + + if (name.equals("SKIN")) { + map = (Map) map.get("metadata"); + if (map != null && "slim".equals(map.get("model"))) + slim = true; + } + + return new Texture(url, slim); + } + + return null; + } + + public static Map getTextures(String player) throws IOException { + try (InputStream profileIn = new URL("https://sessionserver.mojang.com/session/minecraft/profile/" + player) + .openStream()) { + Map profile = (Map) JsonParser.parse(profileIn); + + for (Map property : (Iterable>) profile.get("properties")) { + if (property.get("name").equals("textures")) { + Map result = (Map) JsonParser + .parse(new String(Base64.decode((String) property.get("value")))); + result = (Map) result.get("textures"); + + return result; + } + } + + return null; + } + } + +} diff --git a/libraries/launcher/org/prismlauncher/utils/api/Texture.java b/libraries/launcher/org/prismlauncher/utils/api/Texture.java new file mode 100644 index 000000000..1b5c2204e --- /dev/null +++ b/libraries/launcher/org/prismlauncher/utils/api/Texture.java @@ -0,0 +1,61 @@ +// 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.api; + +import java.net.URL; + +/** + * Represents a texture from the Mojang API. + */ +public final class Texture { + + private final URL url; + private final boolean slim; + + public Texture(URL url, boolean slim) { + this.url = url; + this.slim = slim; + } + + public URL getUrl() { + return url; + } + + public boolean isSlim() { + return slim; + } + +} diff --git a/libraries/launcher/org/prismlauncher/utils/url/CustomUrlConnection.java b/libraries/launcher/org/prismlauncher/utils/url/CustomUrlConnection.java new file mode 100644 index 000000000..8bc0cff11 --- /dev/null +++ b/libraries/launcher/org/prismlauncher/utils/url/CustomUrlConnection.java @@ -0,0 +1,79 @@ +// 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.url; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; + +public class CustomUrlConnection extends HttpURLConnection { + + private InputStream in; + + public CustomUrlConnection(byte[] data) { + this(new ByteArrayInputStream(data)); + } + + public CustomUrlConnection(InputStream in) { + super(null); + this.in = in; + } + + @Override + public void connect() throws IOException { + responseCode = 200; + } + + @Override + public void disconnect() { + try { + in.close(); + } catch (IOException e) { + } + } + + @Override + public InputStream getInputStream() throws IOException { + return in; + } + + @Override + public boolean usingProxy() { + return false; + } + +} diff --git a/libraries/launcher/org/prismlauncher/utils/UrlUtils.java b/libraries/launcher/org/prismlauncher/utils/url/UrlUtils.java similarity index 89% rename from libraries/launcher/org/prismlauncher/utils/UrlUtils.java rename to libraries/launcher/org/prismlauncher/utils/url/UrlUtils.java index 1ab0eb918..69ebbd50b 100644 --- a/libraries/launcher/org/prismlauncher/utils/UrlUtils.java +++ b/libraries/launcher/org/prismlauncher/utils/url/UrlUtils.java @@ -33,7 +33,7 @@ * along with this program. If not, see . */ -package org.prismlauncher.utils; +package org.prismlauncher.utils.url; import java.io.IOException; import java.lang.invoke.MethodHandle; @@ -81,11 +81,17 @@ public final class UrlUtils { return http != null && openConnection != null; } - public static URLConnection openHttpConnection(URL url, Proxy proxy) throws IOException { + public static URLConnection openConnection(URL url, Proxy proxy) throws IOException { if (http == null) throw new UnsupportedOperationException(); - return openConnection(http, url, proxy); + if (url.getProtocol().equals("http")) + return openConnection(http, url, proxy); + + // fall back to Java's default method + // at this point, this should not cause a StackOverflowError unless we've missed + // a protocol out from the if statements + return url.openConnection(); } public static URLConnection openConnection(URLStreamHandler handler, URL url, Proxy proxy) throws IOException { From 17317ea308d3523a4ead9c926e8f2213d7ea53ea Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 6 Jan 2023 09:21:09 +0000 Subject: [PATCH 013/268] Move legacy support classes to another jar Signed-off-by: TheKodeToad --- launcher/minecraft/MinecraftInstance.cpp | 12 +++- launcher/minecraft/MinecraftInstance.h | 1 + .../minecraft/launch/LauncherPartLaunch.cpp | 16 +++++ libraries/launcher/CMakeLists.txt | 34 +++++++---- .../{ => legacy}/net/minecraft/Launcher.java | 0 .../prismlauncher}/legacy/LegacyFrame.java | 4 +- .../prismlauncher}/legacy/LegacyLauncher.java | 49 ++++++++++++--- .../org/prismlauncher/legacy/LegacyProxy.java | 17 ++++++ .../legacy}/fix/online/Handler.java | 4 +- .../legacy}/fix/online/OnlineFixes.java | 12 ++-- .../legacy}/fix/online/SkinFix.java | 8 +-- .../prismlauncher/legacy}/utils/Base64.java | 2 +- .../legacy/utils}/JsonParseException.java | 2 +- .../legacy}/utils/JsonParser.java | 4 +- .../legacy}/utils/api/MojangApi.java | 5 +- .../legacy}/utils/api/Texture.java | 2 +- .../utils/url/CustomUrlConnection.java | 2 +- .../legacy}/utils/url/UrlUtils.java | 2 +- .../org/prismlauncher/EntryPoint.java | 7 +-- .../launcher/org/prismlauncher/fix/Fixes.java | 48 --------------- .../org/prismlauncher/legacy/LegacyProxy.java | 17 ++++++ .../prismlauncher/utils/ReflectionUtils.java | 61 ------------------- .../org/prismlauncher/utils/logging/Log.java | 2 +- 23 files changed, 150 insertions(+), 161 deletions(-) rename libraries/launcher/{ => legacy}/net/minecraft/Launcher.java (100%) rename libraries/launcher/{org/prismlauncher/launcher/impl => legacy/org/prismlauncher}/legacy/LegacyFrame.java (98%) rename libraries/launcher/{org/prismlauncher/launcher/impl => legacy/org/prismlauncher}/legacy/LegacyLauncher.java (76%) create mode 100644 libraries/launcher/legacy/org/prismlauncher/legacy/LegacyProxy.java rename libraries/launcher/{org/prismlauncher => legacy/org/prismlauncher/legacy}/fix/online/Handler.java (96%) rename libraries/launcher/{org/prismlauncher => legacy/org/prismlauncher/legacy}/fix/online/OnlineFixes.java (89%) rename libraries/launcher/{org/prismlauncher => legacy/org/prismlauncher/legacy}/fix/online/SkinFix.java (94%) rename libraries/launcher/{org/prismlauncher => legacy/org/prismlauncher/legacy}/utils/Base64.java (98%) rename libraries/launcher/{org/prismlauncher/exception => legacy/org/prismlauncher/legacy/utils}/JsonParseException.java (97%) rename libraries/launcher/{org/prismlauncher => legacy/org/prismlauncher/legacy}/utils/JsonParser.java (99%) rename libraries/launcher/{org/prismlauncher => legacy/org/prismlauncher/legacy}/utils/api/MojangApi.java (96%) rename libraries/launcher/{org/prismlauncher => legacy/org/prismlauncher/legacy}/utils/api/Texture.java (97%) rename libraries/launcher/{org/prismlauncher => legacy/org/prismlauncher/legacy}/utils/url/CustomUrlConnection.java (98%) rename libraries/launcher/{org/prismlauncher => legacy/org/prismlauncher/legacy}/utils/url/UrlUtils.java (98%) delete mode 100644 libraries/launcher/org/prismlauncher/fix/Fixes.java create mode 100644 libraries/launcher/org/prismlauncher/legacy/LegacyProxy.java diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index ec916f26f..9aa2db0be 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -440,7 +440,7 @@ QStringList MinecraftInstance::javaArguments() args << "-Duser.language=en"; - if (javaVersion.isModular() && traits().contains("legacyServices") && settings()->get("OnlineFixes").toBool()) + if (javaVersion.isModular() && shouldApplyOnlineFixes()) // allow reflective access to java.net - required by the skin fix args << "--add-opens" << "java.base/java.net=ALL-UNNAMED"; @@ -448,7 +448,8 @@ QStringList MinecraftInstance::javaArguments() return args; } -QString MinecraftInstance::getLauncher() { +QString MinecraftInstance::getLauncher() +{ // use legacy launcher if the traits are set if (traits().contains("legacyLaunch") || traits().contains("alphaLaunch")) return "legacy"; @@ -456,6 +457,11 @@ QString MinecraftInstance::getLauncher() { return "standard"; } +bool MinecraftInstance::shouldApplyOnlineFixes() +{ + return traits().contains("legacyServices") && settings()->get("OnlineFixes").toBool(); +} + QMap MinecraftInstance::getVariables() { QMap out; @@ -665,7 +671,7 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS launchScript += "traits " + trait + "\n"; } - if (profile->getTraits().contains("legacyServices") && settings()->get("OnlineFixes").toBool()) + if (shouldApplyOnlineFixes()) launchScript += "onlineFixes true\n"; launchScript += "launcher " + getLauncher() + "\n"; diff --git a/launcher/minecraft/MinecraftInstance.h b/launcher/minecraft/MinecraftInstance.h index 1bbd7b832..9826bfd9e 100644 --- a/launcher/minecraft/MinecraftInstance.h +++ b/launcher/minecraft/MinecraftInstance.h @@ -132,6 +132,7 @@ public: /// get arguments passed to java QStringList javaArguments(); QString getLauncher(); + bool shouldApplyOnlineFixes(); /// get variables for launch command variable substitution/environment QMap getVariables() override; diff --git a/launcher/minecraft/launch/LauncherPartLaunch.cpp b/launcher/minecraft/launch/LauncherPartLaunch.cpp index 1d8d70833..71280cb8d 100644 --- a/launcher/minecraft/launch/LauncherPartLaunch.cpp +++ b/launcher/minecraft/launch/LauncherPartLaunch.cpp @@ -107,6 +107,19 @@ void LauncherPartLaunch::executeTask() auto instance = m_parent->instance(); std::shared_ptr minecraftInstance = std::dynamic_pointer_cast(instance); + QString legacyJarPath; + if (minecraftInstance->getLauncher() == "legacy" || minecraftInstance->shouldApplyOnlineFixes()) + { + legacyJarPath = APPLICATION->getJarPath("NewLaunchLegacy.jar"); + if (legacyJarPath.isEmpty()) + { + const char *reason = QT_TR_NOOP("Legacy launcher library could not be found. Please check your installation."); + emit logLine(tr(reason), MessageLevel::Fatal); + emitFailed(tr(reason)); + return; + } + } + m_launchScript = minecraftInstance->createLaunchScript(m_session, m_serverToJoin); QStringList args = minecraftInstance->javaArguments(); QString allArgs = args.join(", "); @@ -122,6 +135,9 @@ void LauncherPartLaunch::executeTask() auto classPath = minecraftInstance->getClassPath(); classPath.prepend(jarPath); + if (!legacyJarPath.isEmpty()) + classPath.prepend(legacyJarPath); + auto natPath = minecraftInstance->getNativePath(); #ifdef Q_OS_WIN if (!fitsInLocal8bit(natPath)) diff --git a/libraries/launcher/CMakeLists.txt b/libraries/launcher/CMakeLists.txt index bf8f0baa2..d835dc9a3 100644 --- a/libraries/launcher/CMakeLists.txt +++ b/libraries/launcher/CMakeLists.txt @@ -11,25 +11,33 @@ set(SRC org/prismlauncher/launcher/Launcher.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/fix/Fixes.java - org/prismlauncher/fix/online/OnlineFixes.java - org/prismlauncher/fix/online/SkinFix.java - org/prismlauncher/fix/online/Handler.java - org/prismlauncher/utils/Base64.java - org/prismlauncher/utils/JsonParser.java org/prismlauncher/utils/Parameters.java org/prismlauncher/utils/ReflectionUtils.java - org/prismlauncher/utils/api/MojangApi.java - org/prismlauncher/utils/api/Texture.java org/prismlauncher/utils/logging/Level.java org/prismlauncher/utils/logging/Log.java - org/prismlauncher/utils/url/UrlUtils.java - org/prismlauncher/utils/url/CustomUrlConnection.java - net/minecraft/Launcher.java + org/prismlauncher/legacy/LegacyProxy.java ) + +set(LEGACY_SRC + legacy/org/prismlauncher/legacy/LegacyFrame.java + legacy/org/prismlauncher/legacy/LegacyLauncher.java + legacy/org/prismlauncher/legacy/fix/online/Handler.java + legacy/org/prismlauncher/legacy/fix/online/OnlineFixes.java + legacy/org/prismlauncher/legacy/fix/online/SkinFix.java + legacy/org/prismlauncher/legacy/utils/Base64.java + legacy/org/prismlauncher/legacy/utils/JsonParseException.java + legacy/org/prismlauncher/legacy/utils/JsonParser.java + legacy/org/prismlauncher/legacy/utils/api/MojangApi.java + legacy/org/prismlauncher/legacy/utils/api/Texture.java + legacy/org/prismlauncher/legacy/utils/url/CustomUrlConnection.java + legacy/org/prismlauncher/legacy/utils/url/UrlUtils.java + legacy/net/minecraft/Launcher.java + legacy/org/prismlauncher/legacy/LegacyProxy.java +) + add_jar(NewLaunch ${SRC}) +add_jar(NewLaunchLegacy ${LEGACY_SRC} INCLUDE_JARS NewLaunch) install_jar(NewLaunch "${JARS_DEST_DIR}") +install_jar(NewLaunchLegacy "${JARS_DEST_DIR}") diff --git a/libraries/launcher/net/minecraft/Launcher.java b/libraries/launcher/legacy/net/minecraft/Launcher.java similarity index 100% rename from libraries/launcher/net/minecraft/Launcher.java rename to libraries/launcher/legacy/net/minecraft/Launcher.java diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java b/libraries/launcher/legacy/org/prismlauncher/legacy/LegacyFrame.java similarity index 98% rename from libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java rename to libraries/launcher/legacy/org/prismlauncher/legacy/LegacyFrame.java index c215e7fe0..553b07eb1 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/LegacyFrame.java @@ -52,7 +52,7 @@ * limitations under the License. */ -package org.prismlauncher.launcher.impl.legacy; +package org.prismlauncher.legacy; import java.applet.Applet; import java.awt.Dimension; @@ -74,7 +74,7 @@ import org.prismlauncher.utils.logging.Log; import net.minecraft.Launcher; -public final class LegacyFrame extends JFrame { +final class LegacyFrame extends JFrame { private static final long serialVersionUID = 1L; diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java b/libraries/launcher/legacy/org/prismlauncher/legacy/LegacyLauncher.java similarity index 76% rename from libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java rename to libraries/launcher/legacy/org/prismlauncher/legacy/LegacyLauncher.java index d349177b2..dae737224 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/LegacyLauncher.java @@ -53,11 +53,15 @@ * limitations under the License. */ -package org.prismlauncher.launcher.impl.legacy; +package org.prismlauncher.legacy; +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; @@ -69,7 +73,7 @@ import org.prismlauncher.utils.logging.Log; /** * Used to launch old versions that support applets. */ -public final class LegacyLauncher extends AbstractLauncher { +final class LegacyLauncher extends AbstractLauncher { private final String user, session; private final String title; @@ -94,11 +98,9 @@ public final class LegacyLauncher extends AbstractLauncher { @Override public void launch() throws Throwable { Class main = ClassLoader.getSystemClassLoader().loadClass(mainClassName); - Field gameDirField = ReflectionUtils.findMinecraftGameDirField(main); + Field gameDirField = findMinecraftGameDirField(main); - if (gameDirField == null) - Log.warning("Could not find Minecraft folder field"); - else { + if (gameDirField != null) { gameDirField.setAccessible(true); gameDirField.set(null, new File(gameDir)); } @@ -107,7 +109,7 @@ public final class LegacyLauncher extends AbstractLauncher { System.setProperty("minecraft.applet.TargetDirectory", gameDir); try { - LegacyFrame window = new LegacyFrame(title, ReflectionUtils.createAppletClass(appletClass)); + LegacyFrame window = new LegacyFrame(title, createAppletClass(appletClass)); window.start(user, session, width, height, maximize, serverAddress, serverPort, gameArgs.contains("--demo")); @@ -123,4 +125,37 @@ public final class LegacyLauncher extends AbstractLauncher { method.invokeExact(gameArgs.toArray(new String[0])); } + private static Applet createAppletClass(String clazz) throws Throwable { + Class appletClass = ClassLoader.getSystemClassLoader().loadClass(clazz); + + MethodHandle appletConstructor = MethodHandles.lookup().findConstructor(appletClass, MethodType.methodType(void.class)); + return (Applet) appletConstructor.invoke(); + } + + private static Field findMinecraftGameDirField(Class clazz) { + Log.debug("Resolving minecraft game directory field"); + + // search for private static File + for (Field field : clazz.getDeclaredFields()) { + if (field.getType() != File.class) { + continue; + } + + int fieldModifiers = field.getModifiers(); + + if (!Modifier.isStatic(fieldModifiers)) + continue; + + if (!Modifier.isPrivate(fieldModifiers)) + continue; + + if (Modifier.isFinal(fieldModifiers)) + continue; + + return field; + } + + return null; + } + } diff --git a/libraries/launcher/legacy/org/prismlauncher/legacy/LegacyProxy.java b/libraries/launcher/legacy/org/prismlauncher/legacy/LegacyProxy.java new file mode 100644 index 000000000..f2e65087f --- /dev/null +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/LegacyProxy.java @@ -0,0 +1,17 @@ +package org.prismlauncher.legacy; + +import org.prismlauncher.launcher.Launcher; +import org.prismlauncher.legacy.fix.online.OnlineFixes; +import org.prismlauncher.utils.Parameters; + +public final class LegacyProxy { + + public static Launcher createLauncher(Parameters params) { + return new LegacyLauncher(params); + } + + public static void applyOnlineFixes(Parameters parameters) { + OnlineFixes.apply(parameters); + } + +} diff --git a/libraries/launcher/org/prismlauncher/fix/online/Handler.java b/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/Handler.java similarity index 96% rename from libraries/launcher/org/prismlauncher/fix/online/Handler.java rename to libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/Handler.java index 618683119..02a325e37 100644 --- a/libraries/launcher/org/prismlauncher/fix/online/Handler.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/Handler.java @@ -33,7 +33,7 @@ * along with this program. If not, see . */ -package org.prismlauncher.fix.online; +package org.prismlauncher.legacy.fix.online; import java.io.IOException; import java.net.Proxy; @@ -41,7 +41,7 @@ import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandler; -import org.prismlauncher.utils.url.UrlUtils; +import org.prismlauncher.legacy.utils.url.UrlUtils; final class Handler extends URLStreamHandler { diff --git a/libraries/launcher/org/prismlauncher/fix/online/OnlineFixes.java b/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/OnlineFixes.java similarity index 89% rename from libraries/launcher/org/prismlauncher/fix/online/OnlineFixes.java rename to libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/OnlineFixes.java index 5f9ff1014..d4e4c27d5 100644 --- a/libraries/launcher/org/prismlauncher/fix/online/OnlineFixes.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/OnlineFixes.java @@ -33,15 +33,16 @@ * along with this program. If not, see . */ -package org.prismlauncher.fix.online; +package org.prismlauncher.legacy.fix.online; import java.net.URL; import java.net.URLStreamHandler; import java.net.URLStreamHandlerFactory; -import org.prismlauncher.utils.Base64; +import org.prismlauncher.legacy.utils.Base64; +import org.prismlauncher.legacy.utils.url.UrlUtils; +import org.prismlauncher.utils.Parameters; import org.prismlauncher.utils.logging.Log; -import org.prismlauncher.utils.url.UrlUtils; /** * Fixes skins by redirecting to other URLs. @@ -51,7 +52,10 @@ import org.prismlauncher.utils.url.UrlUtils; */ public final class OnlineFixes implements URLStreamHandlerFactory { - public static void apply() { + public static void apply(Parameters params) { + if (!"true".equals(params.getString("onlineFixes", null))) + return; + if (!UrlUtils.isSupported() || !Base64.isSupported()) { Log.warning("Cannot access the necessary Java internals for skin fix"); Log.warning("Turning off legacy skin fix in Settings > Miscellaneous will silence the warnings"); diff --git a/libraries/launcher/org/prismlauncher/fix/online/SkinFix.java b/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/SkinFix.java similarity index 94% rename from libraries/launcher/org/prismlauncher/fix/online/SkinFix.java rename to libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/SkinFix.java index b05e1f208..1c4888312 100644 --- a/libraries/launcher/org/prismlauncher/fix/online/SkinFix.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/SkinFix.java @@ -1,4 +1,4 @@ -package org.prismlauncher.fix.online; +package org.prismlauncher.legacy.fix.online; import java.awt.AlphaComposite; import java.awt.Graphics2D; @@ -12,10 +12,8 @@ import java.net.URLConnection; import javax.imageio.ImageIO; -import org.prismlauncher.utils.api.MojangApi; -import org.prismlauncher.utils.api.Texture; -import org.prismlauncher.utils.url.CustomUrlConnection; -import org.prismlauncher.utils.url.UrlUtils; +import org.prismlauncher.legacy.utils.api.*; +import org.prismlauncher.legacy.utils.url.*; final class SkinFix { diff --git a/libraries/launcher/org/prismlauncher/utils/Base64.java b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/Base64.java similarity index 98% rename from libraries/launcher/org/prismlauncher/utils/Base64.java rename to libraries/launcher/legacy/org/prismlauncher/legacy/utils/Base64.java index 508df872d..3f7852e74 100644 --- a/libraries/launcher/org/prismlauncher/utils/Base64.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/Base64.java @@ -33,7 +33,7 @@ * along with this program. If not, see . */ -package org.prismlauncher.utils; +package org.prismlauncher.legacy.utils; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; diff --git a/libraries/launcher/org/prismlauncher/exception/JsonParseException.java b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/JsonParseException.java similarity index 97% rename from libraries/launcher/org/prismlauncher/exception/JsonParseException.java rename to libraries/launcher/legacy/org/prismlauncher/legacy/utils/JsonParseException.java index 5983eb883..6ba8d10b6 100644 --- a/libraries/launcher/org/prismlauncher/exception/JsonParseException.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/JsonParseException.java @@ -33,7 +33,7 @@ * along with this program. If not, see . */ -package org.prismlauncher.exception; +package org.prismlauncher.legacy.utils; import java.io.IOException; diff --git a/libraries/launcher/org/prismlauncher/utils/JsonParser.java b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/JsonParser.java similarity index 99% rename from libraries/launcher/org/prismlauncher/utils/JsonParser.java rename to libraries/launcher/legacy/org/prismlauncher/legacy/utils/JsonParser.java index 5f10e9a7b..4339f98e6 100644 --- a/libraries/launcher/org/prismlauncher/utils/JsonParser.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/JsonParser.java @@ -33,7 +33,7 @@ * along with this program. If not, see . */ -package org.prismlauncher.utils; +package org.prismlauncher.legacy.utils; import java.io.IOException; import java.io.InputStream; @@ -46,8 +46,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.prismlauncher.exception.JsonParseException; - /** * Single-file JSON parser to allow for usage in versions without GSON. */ diff --git a/libraries/launcher/org/prismlauncher/utils/api/MojangApi.java b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/api/MojangApi.java similarity index 96% rename from libraries/launcher/org/prismlauncher/utils/api/MojangApi.java rename to libraries/launcher/legacy/org/prismlauncher/legacy/utils/api/MojangApi.java index dc2794107..6159a4e72 100644 --- a/libraries/launcher/org/prismlauncher/utils/api/MojangApi.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/api/MojangApi.java @@ -33,15 +33,14 @@ * along with this program. If not, see . */ -package org.prismlauncher.utils.api; +package org.prismlauncher.legacy.utils.api; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Map; -import org.prismlauncher.utils.Base64; -import org.prismlauncher.utils.JsonParser; +import org.prismlauncher.legacy.utils.*; /** * Basic access to Mojang's Minecraft API. diff --git a/libraries/launcher/org/prismlauncher/utils/api/Texture.java b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/api/Texture.java similarity index 97% rename from libraries/launcher/org/prismlauncher/utils/api/Texture.java rename to libraries/launcher/legacy/org/prismlauncher/legacy/utils/api/Texture.java index 1b5c2204e..838a2758e 100644 --- a/libraries/launcher/org/prismlauncher/utils/api/Texture.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/api/Texture.java @@ -33,7 +33,7 @@ * along with this program. If not, see . */ -package org.prismlauncher.utils.api; +package org.prismlauncher.legacy.utils.api; import java.net.URL; diff --git a/libraries/launcher/org/prismlauncher/utils/url/CustomUrlConnection.java b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/url/CustomUrlConnection.java similarity index 98% rename from libraries/launcher/org/prismlauncher/utils/url/CustomUrlConnection.java rename to libraries/launcher/legacy/org/prismlauncher/legacy/utils/url/CustomUrlConnection.java index 8bc0cff11..36ac235f3 100644 --- a/libraries/launcher/org/prismlauncher/utils/url/CustomUrlConnection.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/url/CustomUrlConnection.java @@ -33,7 +33,7 @@ * along with this program. If not, see . */ -package org.prismlauncher.utils.url; +package org.prismlauncher.legacy.utils.url; import java.io.ByteArrayInputStream; import java.io.IOException; diff --git a/libraries/launcher/org/prismlauncher/utils/url/UrlUtils.java b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/url/UrlUtils.java similarity index 98% rename from libraries/launcher/org/prismlauncher/utils/url/UrlUtils.java rename to libraries/launcher/legacy/org/prismlauncher/legacy/utils/url/UrlUtils.java index 69ebbd50b..85c35eb17 100644 --- a/libraries/launcher/org/prismlauncher/utils/url/UrlUtils.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/url/UrlUtils.java @@ -33,7 +33,7 @@ * along with this program. If not, see . */ -package org.prismlauncher.utils.url; +package org.prismlauncher.legacy.utils.url; import java.io.IOException; import java.lang.invoke.MethodHandle; diff --git a/libraries/launcher/org/prismlauncher/EntryPoint.java b/libraries/launcher/org/prismlauncher/EntryPoint.java index 88ecb48ce..a2d9a5f78 100644 --- a/libraries/launcher/org/prismlauncher/EntryPoint.java +++ b/libraries/launcher/org/prismlauncher/EntryPoint.java @@ -59,10 +59,9 @@ import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import org.prismlauncher.exception.ParseException; -import org.prismlauncher.fix.Fixes; import org.prismlauncher.launcher.Launcher; import org.prismlauncher.launcher.impl.StandardLauncher; -import org.prismlauncher.launcher.impl.legacy.LegacyLauncher; +import org.prismlauncher.legacy.LegacyProxy; import org.prismlauncher.utils.Parameters; import org.prismlauncher.utils.logging.Log; @@ -108,7 +107,7 @@ public final class EntryPoint { } try { - Fixes.apply(params); + LegacyProxy.applyOnlineFixes(params); Launcher launcher; String type = params.getString("launcher"); @@ -119,7 +118,7 @@ public final class EntryPoint { break; case "legacy": - launcher = new LegacyLauncher(params); + launcher = LegacyProxy.createLauncher(params); break; default: diff --git a/libraries/launcher/org/prismlauncher/fix/Fixes.java b/libraries/launcher/org/prismlauncher/fix/Fixes.java deleted file mode 100644 index 429964ad9..000000000 --- a/libraries/launcher/org/prismlauncher/fix/Fixes.java +++ /dev/null @@ -1,48 +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.fix; - -import org.prismlauncher.fix.online.OnlineFixes; -import org.prismlauncher.utils.Parameters; - -public final class Fixes { - - public static void apply(Parameters params) { - if ("true".equalsIgnoreCase(params.getString("onlineFixes", null))) - OnlineFixes.apply(); - } - -} diff --git a/libraries/launcher/org/prismlauncher/legacy/LegacyProxy.java b/libraries/launcher/org/prismlauncher/legacy/LegacyProxy.java new file mode 100644 index 000000000..9b96d7179 --- /dev/null +++ b/libraries/launcher/org/prismlauncher/legacy/LegacyProxy.java @@ -0,0 +1,17 @@ +package org.prismlauncher.legacy; + +import org.prismlauncher.launcher.Launcher; +import org.prismlauncher.utils.Parameters; + +// used as a fallback if NewLaunchLegacy is not on the classpath +// if it is, this class will be replaced +public final class LegacyProxy { + + public static Launcher createLauncher(Parameters params) { + throw new AssertionError("NewLaunchLegacy is not loaded"); + } + + public static void applyOnlineFixes(Parameters params) { + } + +} diff --git a/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java b/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java index dd212ef93..1925bf049 100644 --- a/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java +++ b/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java @@ -54,76 +54,15 @@ 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 org.prismlauncher.utils.logging.Log; public final class ReflectionUtils { private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); private static final ClassLoader LOADER = ClassLoader.getSystemClassLoader(); - /** - * Construct a Java applet by its class name. - * - * @param clazz The class name - * @return The applet instance - * @throws Throwable - */ - public static Applet createAppletClass(String clazz) throws Throwable { - Class appletClass = LOADER.loadClass(clazz); - - MethodHandle appletConstructor = LOOKUP.findConstructor(appletClass, MethodType.methodType(void.class)); - return (Applet) appletConstructor.invoke(); - } - - /** - * 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 clazz The class - * @return The first field matching criteria - */ - public static Field findMinecraftGameDirField(Class clazz) { - Log.debug("Resolving minecraft game directory field"); - - // search for private static File - for (Field field : clazz.getDeclaredFields()) { - if (field.getType() != File.class) { - continue; - } - - int fieldModifiers = field.getModifiers(); - - if (!Modifier.isStatic(fieldModifiers)) { - Log.debug("Rejecting field " + field.getName() + " because it is not static"); - continue; - } - - if (!Modifier.isPrivate(fieldModifiers)) { - Log.debug("Rejecting field " + field.getName() + " because it is not private"); - continue; - } - - 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 game directory field"); - - return field; - } - - return null; - } - /** * Gets the main method within a class. * diff --git a/libraries/launcher/org/prismlauncher/utils/logging/Log.java b/libraries/launcher/org/prismlauncher/utils/logging/Log.java index e3aa538b2..0c5dc3009 100644 --- a/libraries/launcher/org/prismlauncher/utils/logging/Log.java +++ b/libraries/launcher/org/prismlauncher/utils/logging/Log.java @@ -45,7 +45,7 @@ import java.io.PrintStream; public final class Log { // original before possibly overridden by MC - private static final PrintStream OUT = new PrintStream(System.out), ERR = new PrintStream(System.err); + 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) { From be7f81421ab98b439a4b285dd47bd293b61dc860 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 6 Jan 2023 10:45:59 +0000 Subject: [PATCH 014/268] Finishing touches Signed-off-by: TheKodeToad --- .../launcher/legacy/net/minecraft/Launcher.java | 5 ++--- .../org/prismlauncher/legacy/LegacyFrame.java | 2 +- .../prismlauncher/legacy/LegacyLauncher.java | 14 ++++++-------- .../org/prismlauncher/legacy/LegacyProxy.java | 1 + .../legacy/fix/online/OnlineFixes.java | 8 ++++---- .../legacy/fix/online/SkinFix.java | 15 +++++++++------ .../prismlauncher/legacy/utils/JsonParser.java | 17 +++++++++-------- .../legacy/utils/api/MojangApi.java | 11 ++++++----- 8 files changed, 38 insertions(+), 35 deletions(-) diff --git a/libraries/launcher/legacy/net/minecraft/Launcher.java b/libraries/launcher/legacy/net/minecraft/Launcher.java index 646e2e3ea..4c7fcb6ee 100644 --- a/libraries/launcher/legacy/net/minecraft/Launcher.java +++ b/libraries/launcher/legacy/net/minecraft/Launcher.java @@ -93,12 +93,11 @@ public final class Launcher extends Applet implements AppletStub { try { if (documentBase == null) { - if (applet.getClass().getPackage().getName().startsWith("com.mojang.")) { + if (applet.getClass().getPackage().getName().startsWith("com.mojang")) // Special case only for Classic versions documentBase = new URL("http://www.minecraft.net:80/game/"); - } else { + else documentBase = new URL("http://www.minecraft.net/game/"); - } } } catch (MalformedURLException e) { throw new AssertionError(e); diff --git a/libraries/launcher/legacy/org/prismlauncher/legacy/LegacyFrame.java b/libraries/launcher/legacy/org/prismlauncher/legacy/LegacyFrame.java index 553b07eb1..49a126e7f 100644 --- a/libraries/launcher/legacy/org/prismlauncher/legacy/LegacyFrame.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/LegacyFrame.java @@ -131,7 +131,7 @@ final class LegacyFrame extends JFrame { 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("stand-alone", true); // Show the quit button. This often doesn't seem to work. launcher.setParameter("haspaid", true); // Some old versions need this for world saves to work. launcher.setParameter("demo", demo); launcher.setParameter("fullscreen", false); diff --git a/libraries/launcher/legacy/org/prismlauncher/legacy/LegacyLauncher.java b/libraries/launcher/legacy/org/prismlauncher/legacy/LegacyLauncher.java index dae737224..f84b971cb 100644 --- a/libraries/launcher/legacy/org/prismlauncher/legacy/LegacyLauncher.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/LegacyLauncher.java @@ -71,7 +71,7 @@ import org.prismlauncher.utils.ReflectionUtils; import org.prismlauncher.utils.logging.Log; /** - * Used to launch old versions that support applets. + * Used to launch old versions which support applets. */ final class LegacyLauncher extends AbstractLauncher { @@ -119,8 +119,8 @@ final class LegacyLauncher extends AbstractLauncher { } } - // find and invoke the main method, this time without size parameters - // in all versions that support applets, these are ignored + // 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])); } @@ -128,18 +128,16 @@ final class LegacyLauncher extends AbstractLauncher { private static Applet createAppletClass(String clazz) throws Throwable { Class appletClass = ClassLoader.getSystemClassLoader().loadClass(clazz); - MethodHandle appletConstructor = MethodHandles.lookup().findConstructor(appletClass, MethodType.methodType(void.class)); + MethodHandle appletConstructor = MethodHandles.lookup().findConstructor(appletClass, + MethodType.methodType(void.class)); return (Applet) appletConstructor.invoke(); } private static Field findMinecraftGameDirField(Class clazz) { - Log.debug("Resolving minecraft game directory field"); - // search for private static File for (Field field : clazz.getDeclaredFields()) { - if (field.getType() != File.class) { + if (field.getType() != File.class) continue; - } int fieldModifiers = field.getModifiers(); diff --git a/libraries/launcher/legacy/org/prismlauncher/legacy/LegacyProxy.java b/libraries/launcher/legacy/org/prismlauncher/legacy/LegacyProxy.java index f2e65087f..11c9d6692 100644 --- a/libraries/launcher/legacy/org/prismlauncher/legacy/LegacyProxy.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/LegacyProxy.java @@ -4,6 +4,7 @@ import org.prismlauncher.launcher.Launcher; import org.prismlauncher.legacy.fix.online.OnlineFixes; import org.prismlauncher.utils.Parameters; +// implementation of LegacyProxy public final class LegacyProxy { public static Launcher createLauncher(Parameters params) { diff --git a/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/OnlineFixes.java b/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/OnlineFixes.java index d4e4c27d5..56849cdd9 100644 --- a/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/OnlineFixes.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/OnlineFixes.java @@ -62,12 +62,12 @@ public final class OnlineFixes implements URLStreamHandlerFactory { return; } - try { + try { URL.setURLStreamHandlerFactory(new OnlineFixes()); - } catch (Error e) { + } catch (Error e) { Log.warning("Cannot apply skin fix: URLStreamHandlerFactory is already set"); - Log.warning("Turning off legacy skin fix in Settings > Miscellaneous will silence the warnings"); - } + Log.warning("Turning off legacy skin fix in Settings > Miscellaneous will silence the warnings"); + } } @Override diff --git a/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/SkinFix.java b/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/SkinFix.java index 1c4888312..7cfd297aa 100644 --- a/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/SkinFix.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/SkinFix.java @@ -12,8 +12,10 @@ import java.net.URLConnection; import javax.imageio.ImageIO; -import org.prismlauncher.legacy.utils.api.*; -import org.prismlauncher.legacy.utils.url.*; +import org.prismlauncher.legacy.utils.api.MojangApi; +import org.prismlauncher.legacy.utils.api.Texture; +import org.prismlauncher.legacy.utils.url.CustomUrlConnection; +import org.prismlauncher.legacy.utils.url.UrlUtils; final class SkinFix { @@ -25,7 +27,8 @@ final class SkinFix { String capeOwner = findCapeOwner(address); if (capeOwner != null) { - // since we do not need to process the image, open a direct connection bypassing Handler + // since we do not need to process the image, open a direct connection bypassing + // Handler Texture texture = MojangApi.getTexture(MojangApi.getUuid(capeOwner), "CAPE"); if (texture == null) return null; @@ -45,7 +48,7 @@ final class SkinFix { try (InputStream in = connection.getInputStream()) { // thank you craftycodie! // this is heavily based on - // https://github.com/Mojang/LegacyLauncher/pull/33/files#diff-b61023785a9260651ca0a223573ea9acb5be5eec478bff626dafb7abe13ffebaR99 + // https://github.com/craftycodie/MineOnline/blob/4f4f86f9d051e0a6fd7ff0b95b2a05f7437683d7/src/main/java/gg/codie/mineonline/gui/textures/TextureHelper.java#L17 BufferedImage image = ImageIO.read(in); Graphics2D graphics = image.createGraphics(); graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); @@ -59,7 +62,7 @@ final class SkinFix { } if (texture.isSlim()) { - // convert slim to wide + // convert slim to classic subimage = image.getSubimage(45, 16, 9, 16); graphics.drawImage(subimage, 46, 16, null); @@ -72,7 +75,7 @@ final class SkinFix { graphics.dispose(); - // crop the image + // crop the image - old versions disregard all secondary layers besides the hat ByteArrayOutputStream out = new ByteArrayOutputStream(); image = image.getSubimage(0, 0, 64, 32); ImageIO.write(image, "png", out); diff --git a/libraries/launcher/legacy/org/prismlauncher/legacy/utils/JsonParser.java b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/JsonParser.java index 4339f98e6..73258fd09 100644 --- a/libraries/launcher/legacy/org/prismlauncher/legacy/utils/JsonParser.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/JsonParser.java @@ -47,7 +47,8 @@ import java.util.List; import java.util.Map; /** - * Single-file JSON parser to allow for usage in versions without GSON. + * A lightweight portable JSON parser used instead of GSON since it is not + * available in a lot of versions. */ public final class JsonParser { @@ -55,15 +56,15 @@ public final class JsonParser { private char[] buffer; private int pos, length; - public static Object parse(String in) throws JsonParseException, IOException { + public static Object parse(String in) throws IOException { return parse(new StringReader(in)); } - public static Object parse(InputStream in) throws JsonParseException, IOException { + public static Object parse(InputStream in) throws IOException { return parse(new InputStreamReader(in, StandardCharsets.UTF_8)); } - public static Object parse(Reader in) throws JsonParseException, IOException { + public static Object parse(Reader in) throws IOException { return new JsonParser(in).readSingleValue(); } @@ -336,6 +337,7 @@ public final class JsonParser { if (character() == '0') { result.append((char) character()); read(); + if (isDigit()) throw new JsonParseException("Found superfluous leading zero"); } else if (!isDigit()) @@ -395,11 +397,10 @@ public final class JsonParser { if (read() == 'r' && read() == 'u' && read() == 'e') { return true; } - } else if (character() == 'f') { - if (read() == 'a' && read() == 'l' && read() == 's' && read() == 'e') { - return false; - } + } else if (character() == 'f' && read() == 'a' && read() == 'l' && read() == 's' && read() == 'e') { + return false; } + return null; } diff --git a/libraries/launcher/legacy/org/prismlauncher/legacy/utils/api/MojangApi.java b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/api/MojangApi.java index 6159a4e72..e15ebe7f5 100644 --- a/libraries/launcher/legacy/org/prismlauncher/legacy/utils/api/MojangApi.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/api/MojangApi.java @@ -40,10 +40,11 @@ import java.io.InputStream; import java.net.URL; import java.util.Map; -import org.prismlauncher.legacy.utils.*; +import org.prismlauncher.legacy.utils.Base64; +import org.prismlauncher.legacy.utils.JsonParser; /** - * Basic access to Mojang's Minecraft API. + * Basic wrapper for Mojang's Minecraft API. */ @SuppressWarnings("unchecked") public final class MojangApi { @@ -55,18 +56,18 @@ public final class MojangApi { } } - public static Texture getTexture(String player, String name) throws IOException { + public static Texture getTexture(String player, String id) throws IOException { Map map = getTextures(player); if (map != null) { - map = (Map) map.get(name); + map = (Map) map.get(id); if (map == null) return null; URL url = new URL((String) map.get("url")); boolean slim = false; - if (name.equals("SKIN")) { + if (id.equals("SKIN")) { map = (Map) map.get("metadata"); if (map != null && "slim".equals(map.get("model"))) slim = true; From 52b3a54e4bdbe0be48429ae3744fa6a0cb2fa8f2 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sun, 8 Jan 2023 15:36:36 +0000 Subject: [PATCH 015/268] Fix cloaks conflicting with join server endpoint Signed-off-by: TheKodeToad --- .../legacy/org/prismlauncher/legacy/fix/online/SkinFix.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/SkinFix.java b/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/SkinFix.java index 7cfd297aa..1d23c6d14 100644 --- a/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/SkinFix.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/SkinFix.java @@ -100,6 +100,9 @@ final class SkinFix { private static String findCapeOwner(URL address) { switch (address.getHost()) { case "www.minecraft.net": + if (!address.getPath().equals("/cloak/get.jsp")) + return null; + return stripIfPrefixed(address.getQuery(), "user="); case "s3.amazonaws.com": From d4b346b7ae7201af3c67d7450ac7144eb486638c Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Mon, 9 Jan 2023 10:48:46 +0000 Subject: [PATCH 016/268] Fix oversights Signed-off-by: TheKodeToad --- libraries/launcher/CMakeLists.txt | 4 +- .../org/prismlauncher/legacy/LegacyProxy.java | 52 +++++++++++++++++++ .../legacy/fix/online/SkinFix.java | 52 +++++++++++++++++++ .../legacy/utils/api/MojangApi.java | 2 +- .../utils/{ => json}/JsonParseException.java | 4 +- .../legacy/utils/{ => json}/JsonParser.java | 2 +- .../legacy/utils/url/CustomUrlConnection.java | 4 +- .../org/prismlauncher/legacy/LegacyProxy.java | 52 +++++++++++++++++++ 8 files changed, 164 insertions(+), 8 deletions(-) rename libraries/launcher/legacy/org/prismlauncher/legacy/utils/{ => json}/JsonParseException.java (94%) rename libraries/launcher/legacy/org/prismlauncher/legacy/utils/{ => json}/JsonParser.java (99%) diff --git a/libraries/launcher/CMakeLists.txt b/libraries/launcher/CMakeLists.txt index d835dc9a3..7bf160760 100644 --- a/libraries/launcher/CMakeLists.txt +++ b/libraries/launcher/CMakeLists.txt @@ -27,10 +27,10 @@ set(LEGACY_SRC legacy/org/prismlauncher/legacy/fix/online/OnlineFixes.java legacy/org/prismlauncher/legacy/fix/online/SkinFix.java legacy/org/prismlauncher/legacy/utils/Base64.java - legacy/org/prismlauncher/legacy/utils/JsonParseException.java - legacy/org/prismlauncher/legacy/utils/JsonParser.java legacy/org/prismlauncher/legacy/utils/api/MojangApi.java legacy/org/prismlauncher/legacy/utils/api/Texture.java + legacy/org/prismlauncher/legacy/utils/json/JsonParseException.java + legacy/org/prismlauncher/legacy/utils/json/JsonParser.java legacy/org/prismlauncher/legacy/utils/url/CustomUrlConnection.java legacy/org/prismlauncher/legacy/utils/url/UrlUtils.java legacy/net/minecraft/Launcher.java diff --git a/libraries/launcher/legacy/org/prismlauncher/legacy/LegacyProxy.java b/libraries/launcher/legacy/org/prismlauncher/legacy/LegacyProxy.java index 11c9d6692..34460db10 100644 --- a/libraries/launcher/legacy/org/prismlauncher/legacy/LegacyProxy.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/LegacyProxy.java @@ -1,3 +1,55 @@ +// 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 . + * + * 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.legacy; import org.prismlauncher.launcher.Launcher; diff --git a/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/SkinFix.java b/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/SkinFix.java index 1d23c6d14..37dd125a5 100644 --- a/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/SkinFix.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/SkinFix.java @@ -1,3 +1,55 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 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 . + * + * 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.legacy.fix.online; import java.awt.AlphaComposite; diff --git a/libraries/launcher/legacy/org/prismlauncher/legacy/utils/api/MojangApi.java b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/api/MojangApi.java index e15ebe7f5..e774ee3e1 100644 --- a/libraries/launcher/legacy/org/prismlauncher/legacy/utils/api/MojangApi.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/api/MojangApi.java @@ -41,7 +41,7 @@ import java.net.URL; import java.util.Map; import org.prismlauncher.legacy.utils.Base64; -import org.prismlauncher.legacy.utils.JsonParser; +import org.prismlauncher.legacy.utils.json.JsonParser; /** * Basic wrapper for Mojang's Minecraft API. diff --git a/libraries/launcher/legacy/org/prismlauncher/legacy/utils/JsonParseException.java b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/json/JsonParseException.java similarity index 94% rename from libraries/launcher/legacy/org/prismlauncher/legacy/utils/JsonParseException.java rename to libraries/launcher/legacy/org/prismlauncher/legacy/utils/json/JsonParseException.java index 6ba8d10b6..222427016 100644 --- a/libraries/launcher/legacy/org/prismlauncher/legacy/utils/JsonParseException.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/json/JsonParseException.java @@ -33,11 +33,11 @@ * along with this program. If not, see . */ -package org.prismlauncher.legacy.utils; +package org.prismlauncher.legacy.utils.json; import java.io.IOException; -public class JsonParseException extends IOException { +public final class JsonParseException extends IOException { private static final long serialVersionUID = 1L; diff --git a/libraries/launcher/legacy/org/prismlauncher/legacy/utils/JsonParser.java b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/json/JsonParser.java similarity index 99% rename from libraries/launcher/legacy/org/prismlauncher/legacy/utils/JsonParser.java rename to libraries/launcher/legacy/org/prismlauncher/legacy/utils/json/JsonParser.java index 73258fd09..db2efc6cd 100644 --- a/libraries/launcher/legacy/org/prismlauncher/legacy/utils/JsonParser.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/json/JsonParser.java @@ -33,7 +33,7 @@ * along with this program. If not, see . */ -package org.prismlauncher.legacy.utils; +package org.prismlauncher.legacy.utils.json; import java.io.IOException; import java.io.InputStream; diff --git a/libraries/launcher/legacy/org/prismlauncher/legacy/utils/url/CustomUrlConnection.java b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/url/CustomUrlConnection.java index 36ac235f3..8a8e3416c 100644 --- a/libraries/launcher/legacy/org/prismlauncher/legacy/utils/url/CustomUrlConnection.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/url/CustomUrlConnection.java @@ -40,9 +40,9 @@ import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; -public class CustomUrlConnection extends HttpURLConnection { +public final class CustomUrlConnection extends HttpURLConnection { - private InputStream in; + private final InputStream in; public CustomUrlConnection(byte[] data) { this(new ByteArrayInputStream(data)); diff --git a/libraries/launcher/org/prismlauncher/legacy/LegacyProxy.java b/libraries/launcher/org/prismlauncher/legacy/LegacyProxy.java index 9b96d7179..ec9a1fcc9 100644 --- a/libraries/launcher/org/prismlauncher/legacy/LegacyProxy.java +++ b/libraries/launcher/org/prismlauncher/legacy/LegacyProxy.java @@ -1,3 +1,55 @@ +// 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 . + * + * 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.legacy; import org.prismlauncher.launcher.Launcher; From bb62c62a03df0431cbc8cad983f0b176a5640264 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sun, 19 Feb 2023 11:37:59 +0000 Subject: [PATCH 017/268] Fix cmark again :facepalm: Signed-off-by: TheKodeToad --- libraries/cmark | 1 + 1 file changed, 1 insertion(+) create mode 160000 libraries/cmark diff --git a/libraries/cmark b/libraries/cmark new file mode 160000 index 000000000..5ba25ff40 --- /dev/null +++ b/libraries/cmark @@ -0,0 +1 @@ +Subproject commit 5ba25ff40eba44c811f79ab6a792baf945b8307c From 6a5e9a59dff7601907f354ff7585dddf1c35206c Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sun, 19 Feb 2023 12:00:18 +0000 Subject: [PATCH 018/268] Fix screwed up ui file Signed-off-by: TheKodeToad --- launcher/ui/pages/instance/InstanceSettingsPage.ui | 3 +++ 1 file changed, 3 insertions(+) diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index 6a73a84c0..f16802c0a 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -648,6 +648,9 @@ + + + <html><head/><body><p>Fixes usages of old online services which are no longer operating by emulating them or redirecting to their modern counterparts.</p><p>This currently only allows modern skins to be used.</p></body></html> From 83c526459886f5d8a2498443cafe5b6823cea2eb Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 10 Mar 2023 11:04:09 +0000 Subject: [PATCH 019/268] Stupidly small skinfixfix as requested by DioEgizio Signed-off-by: TheKodeToad --- .../org/prismlauncher/legacy/utils/json/JsonParseException.java | 2 +- .../legacy/org/prismlauncher/legacy/utils/url/UrlUtils.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/launcher/legacy/org/prismlauncher/legacy/utils/json/JsonParseException.java b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/json/JsonParseException.java index 222427016..bb35cb165 100644 --- a/libraries/launcher/legacy/org/prismlauncher/legacy/utils/json/JsonParseException.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/json/JsonParseException.java @@ -45,4 +45,4 @@ public final class JsonParseException extends IOException { super(message); } -} \ No newline at end of file +} diff --git a/libraries/launcher/legacy/org/prismlauncher/legacy/utils/url/UrlUtils.java b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/url/UrlUtils.java index 85c35eb17..de9358b16 100644 --- a/libraries/launcher/legacy/org/prismlauncher/legacy/utils/url/UrlUtils.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/url/UrlUtils.java @@ -107,4 +107,4 @@ public final class UrlUtils { } } -} \ No newline at end of file +} From dfc2b9c76ac0dfeae55759665033539df3f27c2f Mon Sep 17 00:00:00 2001 From: Kode Date: Fri, 21 Apr 2023 20:49:47 +0100 Subject: [PATCH 020/268] thanks flowln! :P Signed-off-by: Kode --- .../org/prismlauncher/legacy/fix/online/OnlineFixes.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/OnlineFixes.java b/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/OnlineFixes.java index 56849cdd9..cf35b75d7 100644 --- a/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/OnlineFixes.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/OnlineFixes.java @@ -58,7 +58,7 @@ public final class OnlineFixes implements URLStreamHandlerFactory { if (!UrlUtils.isSupported() || !Base64.isSupported()) { Log.warning("Cannot access the necessary Java internals for skin fix"); - Log.warning("Turning off legacy skin fix in Settings > Miscellaneous will silence the warnings"); + Log.warning("Turning off online fixes in the settings will silence the warnings"); return; } @@ -66,7 +66,7 @@ public final class OnlineFixes implements URLStreamHandlerFactory { URL.setURLStreamHandlerFactory(new OnlineFixes()); } catch (Error e) { Log.warning("Cannot apply skin fix: URLStreamHandlerFactory is already set"); - Log.warning("Turning off legacy skin fix in Settings > Miscellaneous will silence the warnings"); + Log.warning("Turning off online fixes in the settings will silence the warnings"); } } From c15603406907383c58786cb1dc235ac79c7f9626 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 26 May 2023 13:50:22 -0700 Subject: [PATCH 021/268] feat: add verion_type / release_type to IndexedVersion Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/modplatform/ModIndex.cpp | 50 +++++++++++++++++++ launcher/modplatform/ModIndex.h | 24 +++++++++ launcher/modplatform/flame/FlameAPI.cpp | 5 +- launcher/modplatform/flame/FlameModIndex.cpp | 8 ++- .../modrinth/ModrinthPackIndex.cpp | 9 +++- 5 files changed, 93 insertions(+), 3 deletions(-) diff --git a/launcher/modplatform/ModIndex.cpp b/launcher/modplatform/ModIndex.cpp index 6a507caf4..7fdca3b37 100644 --- a/launcher/modplatform/ModIndex.cpp +++ b/launcher/modplatform/ModIndex.cpp @@ -24,6 +24,56 @@ namespace ModPlatform { +static const QMap s_indexed_version_type_names = { + {"release", IndexedVersionType::Enum::Release}, + {"beta", IndexedVersionType::Enum::Beta}, + {"alpha", IndexedVersionType::Enum::Alpha} +}; + +IndexedVersionType::IndexedVersionType(const QString& type): IndexedVersionType(enumFromString(type)) +{} + +IndexedVersionType::IndexedVersionType(int type) +{ + m_type = static_cast(type); +} + +IndexedVersionType::IndexedVersionType(const IndexedVersionType::Enum& type) +{ + m_type = type; +} + +IndexedVersionType::IndexedVersionType(const IndexedVersionType& other) +{ + m_type = other.m_type; +} + +const QString IndexedVersionType::toString (const IndexedVersionType::Enum& type) +{ + switch (type) { + case IndexedVersionType::Enum::Release: + return "release"; + case IndexedVersionType::Enum::Beta: + return "beta"; + case IndexedVersionType::Enum::Alpha: + return "alpha"; + case IndexedVersionType::Enum::UNKNOWN: + default: + return "unknown"; + + } +} + +const IndexedVersionType::Enum IndexedVersionType::enumFromString(const QString& type) +{ + auto found = s_indexed_version_type_names.constFind(type); + if (found != s_indexed_version_type_names.constEnd()) { + return *found; + } else { + return IndexedVersionType::Enum::UNKNOWN; + } +} + auto ProviderCapabilities::name(ResourceProvider p) -> const char* { switch (p) { diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index 40f1efc4e..f15b296af 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -23,6 +23,7 @@ #include #include #include +#include class QIODevice; @@ -51,11 +52,34 @@ struct DonationData { QString url; }; +struct IndexedVersionType { + enum class Enum { + Release = 1, + Beta, + Alpha, + UNKNOWN + }; + IndexedVersionType(const QString& type); + IndexedVersionType(int type); + IndexedVersionType(const IndexedVersionType::Enum& type); + IndexedVersionType(const IndexedVersionType& type); + static const QString toString (const IndexedVersionType::Enum& type); + static const IndexedVersionType::Enum enumFromString(const QString& type); + bool isValid() const {return m_type != IndexedVersionType::Enum::UNKNOWN; } + bool operator==(const IndexedVersionType& other) const { return m_type == other.m_type; } + bool operator==(const IndexedVersionType::Enum& type) const { return m_type == type; } + bool operator<(const IndexedVersionType& other) const { return m_type < other.m_type; } + bool operator<(const IndexedVersionType::Enum& type) const { return m_type < type; } + + IndexedVersionType::Enum m_type; +}; + struct IndexedVersion { QVariant addonId; QVariant fileId; QString version; QString version_number = {}; + std::optional verison_type = {}; QStringList mcVersion; QString downloadUrl; QString date; diff --git a/launcher/modplatform/flame/FlameAPI.cpp b/launcher/modplatform/flame/FlameAPI.cpp index 5ef9a4090..0f26efefe 100644 --- a/launcher/modplatform/flame/FlameAPI.cpp +++ b/launcher/modplatform/flame/FlameAPI.cpp @@ -144,7 +144,10 @@ auto FlameAPI::getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::Indexe for (auto file : arr) { auto file_obj = Json::requireObject(file); auto file_tmp = FlameMod::loadIndexedPackVersion(file_obj); - if(file_tmp.date > ver_tmp.date) { + bool better_release = true; + if (file_tmp.verison_type.has_value() && ver_tmp.verison_type.has_value()) + better_release = file_tmp.verison_type.value() < ver_tmp.verison_type.value(); + if(file_tmp.date > ver_tmp.date && better_release) { ver_tmp = file_tmp; latest_file_obj = file_obj; } diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index 7498e8302..5866cfbd4 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -94,8 +94,11 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, } auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool { + bool a_better_release = true; + if (a.verison_type.has_value() && b.verison_type.has_value()) + a_better_release = a.verison_type.value() < b.verison_type.value(); // dates are in RFC 3339 format - return a.date > b.date; + return a.date > b.date && a_better_release; }; std::sort(unsortedVersions.begin(), unsortedVersions.end(), orderSortPredicate); pack.versions = unsortedVersions; @@ -123,6 +126,9 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) -> file.version = Json::requireString(obj, "displayName"); file.downloadUrl = Json::ensureString(obj, "downloadUrl"); file.fileName = Json::requireString(obj, "fileName"); + auto version_type = ModPlatform::IndexedVersionType(Json::requireInteger(obj, "releaseType")); + if (version_type.isValid()) + file.verison_type = version_type; auto hash_list = Json::ensureArray(obj, "hashes"); for (auto h : hash_list) { diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index 7ade131e4..c87fa3029 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -109,8 +109,11 @@ void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, unsortedVersions.append(file); } auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool { + bool a_better_release = true; + if (a.verison_type.has_value() && b.verison_type.has_value()) + a_better_release = a.verison_type.value() < b.verison_type.value(); // dates are in RFC 3339 format - return a.date > b.date; + return a.date > b.date && a_better_release; }; std::sort(unsortedVersions.begin(), unsortedVersions.end(), orderSortPredicate); pack.versions = unsortedVersions; @@ -138,6 +141,10 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t } file.version = Json::requireString(obj, "name"); file.version_number = Json::requireString(obj, "version_number"); + auto verison_type = ModPlatform::IndexedVersionType(Json::requireString(obj, "version_type")); + if (verison_type.isValid()) + file.verison_type = verison_type; + file.changelog = Json::requireString(obj, "changelog"); auto files = Json::requireArray(obj, "files"); From cf4df19986fe53357ef04b2a4407f69a2f4fa196 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 26 May 2023 16:23:43 -0700 Subject: [PATCH 022/268] feat: display release type Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/modplatform/CheckUpdateTask.h | 28 +++++++++++++++---- launcher/modplatform/ModIndex.cpp | 14 +++++++++- launcher/modplatform/ModIndex.h | 4 ++- launcher/modplatform/flame/FlameAPI.cpp | 4 +-- .../modplatform/flame/FlameCheckUpdate.cpp | 2 +- launcher/modplatform/flame/FlameModIndex.cpp | 8 ++---- launcher/modplatform/flame/FlamePackIndex.cpp | 1 + launcher/modplatform/flame/FlamePackIndex.h | 3 ++ .../modrinth/ModrinthCheckUpdate.cpp | 4 +-- .../modrinth/ModrinthPackIndex.cpp | 8 ++---- .../modrinth/ModrinthPackManifest.cpp | 4 ++- .../modrinth/ModrinthPackManifest.h | 3 ++ launcher/ui/dialogs/ModUpdateDialog.cpp | 5 ++++ launcher/ui/pages/modplatform/ModPage.cpp | 6 ++-- .../ui/pages/modplatform/ResourcePage.cpp | 4 +-- .../ui/pages/modplatform/flame/FlamePage.cpp | 3 +- .../modplatform/modrinth/ModrinthPage.cpp | 6 ++-- 17 files changed, 73 insertions(+), 34 deletions(-) diff --git a/launcher/modplatform/CheckUpdateTask.h b/launcher/modplatform/CheckUpdateTask.h index f7582b8f1..fbafedd77 100644 --- a/launcher/modplatform/CheckUpdateTask.h +++ b/launcher/modplatform/CheckUpdateTask.h @@ -1,8 +1,8 @@ #pragma once #include "minecraft/mod/Mod.h" -#include "modplatform/ResourceAPI.h" #include "modplatform/ModIndex.h" +#include "modplatform/ResourceAPI.h" #include "tasks/Task.h" class ResourceDownloadTask; @@ -12,21 +12,39 @@ class CheckUpdateTask : public Task { Q_OBJECT public: - CheckUpdateTask(QList& mods, std::list& mcVersions, std::optional loaders, std::shared_ptr mods_folder) - : Task(nullptr), m_mods(mods), m_game_versions(mcVersions), m_loaders(loaders), m_mods_folder(mods_folder) {}; + CheckUpdateTask(QList& mods, + std::list& mcVersions, + std::optional loaders, + std::shared_ptr mods_folder) + : Task(nullptr), m_mods(mods), m_game_versions(mcVersions), m_loaders(loaders), m_mods_folder(mods_folder){}; struct UpdatableMod { QString name; QString old_hash; QString old_version; QString new_version; + std::optional new_verison_type; QString changelog; ModPlatform::ResourceProvider provider; shared_qobject_ptr download; public: - UpdatableMod(QString name, QString old_h, QString old_v, QString new_v, QString changelog, ModPlatform::ResourceProvider p, shared_qobject_ptr t) - : name(name), old_hash(old_h), old_version(old_v), new_version(new_v), changelog(changelog), provider(p), download(t) + UpdatableMod(QString name, + QString old_h, + QString old_v, + QString new_v, + std::optional new_v_type, + QString changelog, + ModPlatform::ResourceProvider p, + shared_qobject_ptr t) + : name(name) + , old_hash(old_h) + , old_version(old_v) + , new_version(new_v) + , new_verison_type(new_v_type) + , changelog(changelog) + , provider(p) + , download(t) {} }; diff --git a/launcher/modplatform/ModIndex.cpp b/launcher/modplatform/ModIndex.cpp index 7fdca3b37..607c26e61 100644 --- a/launcher/modplatform/ModIndex.cpp +++ b/launcher/modplatform/ModIndex.cpp @@ -35,7 +35,19 @@ IndexedVersionType::IndexedVersionType(const QString& type): IndexedVersionType( IndexedVersionType::IndexedVersionType(int type) { - m_type = static_cast(type); + switch (type) { + case 1: + m_type = IndexedVersionType::Enum::Release; + break; + case 2: + m_type = IndexedVersionType::Enum::Beta; + break; + case 3: + m_type = IndexedVersionType::Enum::Alpha; + break; + default: + m_type = IndexedVersionType::Enum::UNKNOWN; + } } IndexedVersionType::IndexedVersionType(const IndexedVersionType::Enum& type) diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index f15b296af..10b61b167 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -63,6 +63,7 @@ struct IndexedVersionType { IndexedVersionType(int type); IndexedVersionType(const IndexedVersionType::Enum& type); IndexedVersionType(const IndexedVersionType& type); + IndexedVersionType() : IndexedVersionType(IndexedVersionType::Enum::UNKNOWN) {} static const QString toString (const IndexedVersionType::Enum& type); static const IndexedVersionType::Enum enumFromString(const QString& type); bool isValid() const {return m_type != IndexedVersionType::Enum::UNKNOWN; } @@ -70,6 +71,7 @@ struct IndexedVersionType { bool operator==(const IndexedVersionType::Enum& type) const { return m_type == type; } bool operator<(const IndexedVersionType& other) const { return m_type < other.m_type; } bool operator<(const IndexedVersionType::Enum& type) const { return m_type < type; } + QString toString() const { return toString(m_type); } IndexedVersionType::Enum m_type; }; @@ -79,7 +81,7 @@ struct IndexedVersion { QVariant fileId; QString version; QString version_number = {}; - std::optional verison_type = {}; + IndexedVersionType verison_type; QStringList mcVersion; QString downloadUrl; QString date; diff --git a/launcher/modplatform/flame/FlameAPI.cpp b/launcher/modplatform/flame/FlameAPI.cpp index 0f26efefe..ad3ab16ac 100644 --- a/launcher/modplatform/flame/FlameAPI.cpp +++ b/launcher/modplatform/flame/FlameAPI.cpp @@ -144,9 +144,7 @@ auto FlameAPI::getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::Indexe for (auto file : arr) { auto file_obj = Json::requireObject(file); auto file_tmp = FlameMod::loadIndexedPackVersion(file_obj); - bool better_release = true; - if (file_tmp.verison_type.has_value() && ver_tmp.verison_type.has_value()) - better_release = file_tmp.verison_type.value() < ver_tmp.verison_type.value(); + bool better_release = file_tmp.verison_type < ver_tmp.verison_type; if(file_tmp.date > ver_tmp.date && better_release) { ver_tmp = file_tmp; latest_file_obj = file_obj; diff --git a/launcher/modplatform/flame/FlameCheckUpdate.cpp b/launcher/modplatform/flame/FlameCheckUpdate.cpp index 06a895027..1e8cc66d9 100644 --- a/launcher/modplatform/flame/FlameCheckUpdate.cpp +++ b/launcher/modplatform/flame/FlameCheckUpdate.cpp @@ -173,7 +173,7 @@ void FlameCheckUpdate::executeTask() } auto download_task = makeShared(pack, latest_ver, m_mods_folder); - m_updatable.emplace_back(pack.name, mod->metadata()->hash, old_version, latest_ver.version, + m_updatable.emplace_back(pack.name, mod->metadata()->hash, old_version, latest_ver.version, latest_ver.verison_type, api.getModFileChangelog(latest_ver.addonId.toInt(), latest_ver.fileId.toInt()), ModPlatform::ResourceProvider::FLAME, download_task); } diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index 5866cfbd4..cb7177d2e 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -94,9 +94,7 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, } auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool { - bool a_better_release = true; - if (a.verison_type.has_value() && b.verison_type.has_value()) - a_better_release = a.verison_type.value() < b.verison_type.value(); + bool a_better_release = a.verison_type < b.verison_type; // dates are in RFC 3339 format return a.date > b.date && a_better_release; }; @@ -126,9 +124,7 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) -> file.version = Json::requireString(obj, "displayName"); file.downloadUrl = Json::ensureString(obj, "downloadUrl"); file.fileName = Json::requireString(obj, "fileName"); - auto version_type = ModPlatform::IndexedVersionType(Json::requireInteger(obj, "releaseType")); - if (version_type.isValid()) - file.verison_type = version_type; + file.verison_type = ModPlatform::IndexedVersionType(Json::requireInteger(obj, "releaseType")); auto hash_list = Json::ensureArray(obj, "hashes"); for (auto h : hash_list) { diff --git a/launcher/modplatform/flame/FlamePackIndex.cpp b/launcher/modplatform/flame/FlamePackIndex.cpp index ad48b7b6a..e643c96b0 100644 --- a/launcher/modplatform/flame/FlamePackIndex.cpp +++ b/launcher/modplatform/flame/FlamePackIndex.cpp @@ -90,6 +90,7 @@ void Flame::loadIndexedPackVersions(Flame::IndexedPack& pack, QJsonArray& arr) // pick the latest version supported file.mcVersion = versionArray[0].toString(); file.version = Json::requireString(version, "displayName"); + file.version_type = ModPlatform::IndexedVersionType(Json::requireInteger(version, "releaseType")); file.downloadUrl = Json::ensureString(version, "downloadUrl"); // only add if we have a download URL (third party distribution is enabled) diff --git a/launcher/modplatform/flame/FlamePackIndex.h b/launcher/modplatform/flame/FlamePackIndex.h index 1ca0fc0e5..0abcadb36 100644 --- a/launcher/modplatform/flame/FlamePackIndex.h +++ b/launcher/modplatform/flame/FlamePackIndex.h @@ -5,6 +5,8 @@ #include #include +#include "modplatform/ModIndex.h" + namespace Flame { struct ModpackAuthor { @@ -16,6 +18,7 @@ struct IndexedVersion { int addonId; int fileId; QString version; + ModPlatform::IndexedVersionType version_type; QString mcVersion; QString downloadUrl; }; diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp index d1be72099..76ad70157 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp @@ -161,8 +161,8 @@ void ModrinthCheckUpdate::executeTask() auto download_task = makeShared(pack, project_ver, m_mods_folder); - m_updatable.emplace_back(pack.name, hash, mod->version(), project_ver.version_number, project_ver.changelog, - ModPlatform::ResourceProvider::MODRINTH, download_task); + m_updatable.emplace_back(pack.name, hash, mod->version(), project_ver.version_number, project_ver.verison_type, + project_ver.changelog, ModPlatform::ResourceProvider::MODRINTH, download_task); } } } catch (Json::JsonException& e) { diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index c87fa3029..2e9d14575 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -109,9 +109,7 @@ void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, unsortedVersions.append(file); } auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool { - bool a_better_release = true; - if (a.verison_type.has_value() && b.verison_type.has_value()) - a_better_release = a.verison_type.value() < b.verison_type.value(); + bool a_better_release = a.verison_type < b.verison_type; // dates are in RFC 3339 format return a.date > b.date && a_better_release; }; @@ -141,9 +139,7 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t } file.version = Json::requireString(obj, "name"); file.version_number = Json::requireString(obj, "version_number"); - auto verison_type = ModPlatform::IndexedVersionType(Json::requireString(obj, "version_type")); - if (verison_type.isValid()) - file.verison_type = verison_type; + file.verison_type = ModPlatform::IndexedVersionType(Json::requireString(obj, "version_type")); file.changelog = Json::requireString(obj, "changelog"); diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp index 4dca786f0..04ed4fa23 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp @@ -111,8 +111,9 @@ void loadIndexedVersions(Modpack& pack, QJsonDocument& doc) unsortedVersions.append(file); } auto orderSortPredicate = [](const ModpackVersion& a, const ModpackVersion& b) -> bool { + bool a_better_release = a.version_type < b.version_type; // dates are in RFC 3339 format - return a.date > b.date; + return a.date > b.date && a_better_release; }; std::sort(unsortedVersions.begin(), unsortedVersions.end(), orderSortPredicate); @@ -128,6 +129,7 @@ auto loadIndexedVersion(QJsonObject &obj) -> ModpackVersion file.name = Json::requireString(obj, "name"); file.version = Json::requireString(obj, "version_number"); + file.version_type = ModPlatform::IndexedVersionType(Json::requireString(obj, "version_type")); file.changelog = Json::ensureString(obj, "changelog"); file.id = Json::requireString(obj, "id"); diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.h b/launcher/modplatform/modrinth/ModrinthPackManifest.h index 2973dfba2..5f8c50a1a 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.h +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.h @@ -45,6 +45,8 @@ #include #include +#include "modplatform/ModIndex.h" + class MinecraftInstance; namespace Modrinth { @@ -80,6 +82,7 @@ struct ModpackExtra { struct ModpackVersion { QString name; QString version; + ModPlatform::IndexedVersionType version_type; QString changelog; QString id; diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index 8618b9240..9e010444c 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -362,6 +362,11 @@ void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info) auto new_version_item = new QTreeWidgetItem(item_top); new_version_item->setText(0, tr("New version: %1").arg(info.new_version)); + if (info.new_verison_type.has_value()) { + auto new_version_type_itme = new QTreeWidgetItem(item_top); + new_version_type_itme->setText(0, tr("New Version Type: %1").arg(info.new_verison_type.value().toString())); + } + auto changelog_item = new QTreeWidgetItem(item_top); changelog_item->setText(0, tr("Changelog of the latest version")); diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index 04be43ada..e156f5009 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -137,8 +137,10 @@ void ModPage::updateVersionList() } // Only add the version if it's valid or using the 'Any' filter, but never if the version is opted out - if ((valid || m_filter->versions.empty()) && !optedOut(version)) - m_ui->versionSelectionBox->addItem(version.version, QVariant(i)); + if ((valid || m_filter->versions.empty()) && !optedOut(version)) { + auto release_type = version.verison_type.isValid() ? QString(" : %1").arg(version.verison_type.toString()) : ""; + m_ui->versionSelectionBox->addItem(QString("%1%2").arg(version.version, release_type), QVariant(i)); + } } if (m_ui->versionSelectionBox->count() == 0) { m_ui->versionSelectionBox->addItem(tr("No valid version found!"), QVariant(-1)); diff --git a/launcher/ui/pages/modplatform/ResourcePage.cpp b/launcher/ui/pages/modplatform/ResourcePage.cpp index bbd465bc1..72565eccc 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.cpp +++ b/launcher/ui/pages/modplatform/ResourcePage.cpp @@ -256,8 +256,8 @@ void ResourcePage::updateVersionList() auto& version = current_pack.versions[i]; if (optedOut(version)) continue; - - m_ui->versionSelectionBox->addItem(current_pack.versions[i].version, QVariant(i)); + auto release_type = current_pack.versions[i].verison_type.isValid() ? QString(" : %1").arg(current_pack.versions[i].verison_type.toString()) : ""; + m_ui->versionSelectionBox->addItem(QString("%1%2").arg(current_pack.versions[i].version, release_type), QVariant(i)); } if (m_ui->versionSelectionBox->count() == 0) { diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.cpp b/launcher/ui/pages/modplatform/flame/FlamePage.cpp index f9ac4a789..95bb9d0f3 100644 --- a/launcher/ui/pages/modplatform/flame/FlamePage.cpp +++ b/launcher/ui/pages/modplatform/flame/FlamePage.cpp @@ -155,7 +155,8 @@ void FlamePage::onSelectionChanged(QModelIndex curr, QModelIndex prev) } for (auto version : current.versions) { - ui->versionSelectionBox->addItem(version.version, QVariant(version.downloadUrl)); + auto release_type = version.version_type.isValid() ? QString(" : %1").arg(version.version_type.toString()) : ""; + ui->versionSelectionBox->addItem(QString("%1%2").arg(version.version, release_type), QVariant(version.downloadUrl)); } QVariant current_updated; diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index 0bb11d834..725af9b0c 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -201,12 +201,12 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, QModelIndex prev) qDebug() << *response; qWarning() << "Error while reading modrinth modpack version: " << e.cause(); } - for (auto version : current.versions) { + auto release_type = version.version_type.isValid() ? QString(" : %1").arg(version.version_type.toString()) : ""; if (!version.name.contains(version.version)) - ui->versionSelectionBox->addItem(QString("%1 — %2").arg(version.name, version.version), QVariant(version.id)); + ui->versionSelectionBox->addItem(QString("%1 — %2%3").arg(version.name, version.version, release_type), QVariant(version.id)); else - ui->versionSelectionBox->addItem(version.name, QVariant(version.id)); + ui->versionSelectionBox->addItem(QString("%1%2").arg(version.name, release_type), QVariant(version.id)); } QVariant current_updated; From 832a61f88674ee1ba01d7618e1021184cc851e3a Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 26 May 2023 16:34:30 -0700 Subject: [PATCH 023/268] fix: make codeQl happy Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/modplatform/ModIndex.cpp | 8 +++++++- launcher/modplatform/ModIndex.h | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/launcher/modplatform/ModIndex.cpp b/launcher/modplatform/ModIndex.cpp index 607c26e61..3e1f473e9 100644 --- a/launcher/modplatform/ModIndex.cpp +++ b/launcher/modplatform/ModIndex.cpp @@ -60,6 +60,12 @@ IndexedVersionType::IndexedVersionType(const IndexedVersionType& other) m_type = other.m_type; } +IndexedVersionType& IndexedVersionType::operator=(const IndexedVersionType& other) +{ + m_type = other.m_type; + return *this; +} + const QString IndexedVersionType::toString (const IndexedVersionType::Enum& type) { switch (type) { @@ -76,7 +82,7 @@ const QString IndexedVersionType::toString (const IndexedVersionType::Enum& type } } -const IndexedVersionType::Enum IndexedVersionType::enumFromString(const QString& type) +IndexedVersionType::Enum IndexedVersionType::enumFromString(const QString& type) { auto found = s_indexed_version_type_names.constFind(type); if (found != s_indexed_version_type_names.constEnd()) { diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index 10b61b167..f317d29e8 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -65,8 +65,9 @@ struct IndexedVersionType { IndexedVersionType(const IndexedVersionType& type); IndexedVersionType() : IndexedVersionType(IndexedVersionType::Enum::UNKNOWN) {} static const QString toString (const IndexedVersionType::Enum& type); - static const IndexedVersionType::Enum enumFromString(const QString& type); + static IndexedVersionType::Enum enumFromString(const QString& type); bool isValid() const {return m_type != IndexedVersionType::Enum::UNKNOWN; } + IndexedVersionType& operator=(const IndexedVersionType& other); bool operator==(const IndexedVersionType& other) const { return m_type == other.m_type; } bool operator==(const IndexedVersionType::Enum& type) const { return m_type == type; } bool operator<(const IndexedVersionType& other) const { return m_type < other.m_type; } From 2680dba0aa74abefea58903dadad6578381101cb Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 26 May 2023 17:16:50 -0700 Subject: [PATCH 024/268] fix: use <= when compareing release types Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/modplatform/ModIndex.h | 9 +++++++++ launcher/modplatform/flame/FlameAPI.cpp | 2 +- launcher/modplatform/flame/FlameModIndex.cpp | 2 +- launcher/modplatform/modrinth/ModrinthPackIndex.cpp | 2 +- launcher/modplatform/modrinth/ModrinthPackManifest.cpp | 2 +- 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index f317d29e8..652e21ef0 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -70,8 +70,17 @@ struct IndexedVersionType { IndexedVersionType& operator=(const IndexedVersionType& other); bool operator==(const IndexedVersionType& other) const { return m_type == other.m_type; } bool operator==(const IndexedVersionType::Enum& type) const { return m_type == type; } + bool operator!=(const IndexedVersionType& other) const { return m_type != other.m_type; } + bool operator!=(const IndexedVersionType::Enum& type) const { return m_type != type; } bool operator<(const IndexedVersionType& other) const { return m_type < other.m_type; } bool operator<(const IndexedVersionType::Enum& type) const { return m_type < type; } + bool operator<=(const IndexedVersionType& other) const { return m_type <= other.m_type; } + bool operator<=(const IndexedVersionType::Enum& type) const { return m_type <= type; } + bool operator>(const IndexedVersionType& other) const { return m_type > other.m_type; } + bool operator>(const IndexedVersionType::Enum& type) const { return m_type > type; } + bool operator>=(const IndexedVersionType& other) const { return m_type >= other.m_type; } + bool operator>=(const IndexedVersionType::Enum& type) const { return m_type >= type; } + QString toString() const { return toString(m_type); } IndexedVersionType::Enum m_type; diff --git a/launcher/modplatform/flame/FlameAPI.cpp b/launcher/modplatform/flame/FlameAPI.cpp index ad3ab16ac..b5155dd8c 100644 --- a/launcher/modplatform/flame/FlameAPI.cpp +++ b/launcher/modplatform/flame/FlameAPI.cpp @@ -144,7 +144,7 @@ auto FlameAPI::getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::Indexe for (auto file : arr) { auto file_obj = Json::requireObject(file); auto file_tmp = FlameMod::loadIndexedPackVersion(file_obj); - bool better_release = file_tmp.verison_type < ver_tmp.verison_type; + bool better_release = file_tmp.verison_type <= ver_tmp.verison_type; if(file_tmp.date > ver_tmp.date && better_release) { ver_tmp = file_tmp; latest_file_obj = file_obj; diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index cb7177d2e..389ee03b3 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -94,7 +94,7 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, } auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool { - bool a_better_release = a.verison_type < b.verison_type; + bool a_better_release = a.verison_type <= b.verison_type; // dates are in RFC 3339 format return a.date > b.date && a_better_release; }; diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index 2e9d14575..e643df677 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -109,7 +109,7 @@ void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, unsortedVersions.append(file); } auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool { - bool a_better_release = a.verison_type < b.verison_type; + bool a_better_release = a.verison_type <= b.verison_type; // dates are in RFC 3339 format return a.date > b.date && a_better_release; }; diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp index 04ed4fa23..aff29fb8c 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp @@ -111,7 +111,7 @@ void loadIndexedVersions(Modpack& pack, QJsonDocument& doc) unsortedVersions.append(file); } auto orderSortPredicate = [](const ModpackVersion& a, const ModpackVersion& b) -> bool { - bool a_better_release = a.version_type < b.version_type; + bool a_better_release = a.version_type <= b.version_type; // dates are in RFC 3339 format return a.date > b.date && a_better_release; }; From 1c91d2f24255272a5526d70ff06f8907f65012a6 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Tue, 30 May 2023 23:03:44 -0700 Subject: [PATCH 025/268] feat: paliminary updater - can check for need to update - can select a version to update to - perform update: TODO Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/Application.cpp | 2 +- launcher/CMakeLists.txt | 59 +- launcher/Version.h | 1 + launcher/filelink/FileLink.cpp | 3 + launcher/filelink/FileLink.h | 11 + launcher/filelink/main.cpp | 14 +- launcher/updater/windows/GitHubRelease.h | 34 + .../updater/windows/SelectReleaseDialog.ui | 89 +++ launcher/updater/windows/UpdaterDialogs.cpp | 78 +++ launcher/updater/windows/UpdaterDialogs.h | 33 + launcher/updater/windows/WindowsUpdater.cpp | 647 ++++++++++++++++++ launcher/updater/windows/WindowsUpdater.h | 110 +++ launcher/updater/windows/windows_main.cpp | 44 ++ 13 files changed, 1122 insertions(+), 3 deletions(-) create mode 100644 launcher/updater/windows/GitHubRelease.h create mode 100644 launcher/updater/windows/SelectReleaseDialog.ui create mode 100644 launcher/updater/windows/UpdaterDialogs.cpp create mode 100644 launcher/updater/windows/UpdaterDialogs.h create mode 100644 launcher/updater/windows/WindowsUpdater.cpp create mode 100644 launcher/updater/windows/WindowsUpdater.h create mode 100644 launcher/updater/windows/windows_main.cpp diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 1659eb445..4b0ba368b 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -469,7 +469,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) { - qDebug() << BuildConfig.LAUNCHER_DISPLAYNAME << ", (c) 2013-2021 " << BuildConfig.LAUNCHER_COPYRIGHT; + qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME) << ", (c) 2022-2023 " << qPrintable(QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", ")); qDebug() << "Version : " << BuildConfig.printableVersionString(); qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT; qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC; diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 273b5449a..5a979b8d9 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -567,6 +567,25 @@ set(LINKEXE_SOURCES DesktopServices.cpp ) +set(WINDOWSUPDATEREXE_SOURCES + updater/windows/WindowsUpdater.h + updater/windows/WindowsUpdater.cpp + updater/windows/UpdaterDialogs.h + updater/windows/UpdaterDialogs.cpp + updater/windows/GitHubRelease.h + Json.h + Json.cpp + FileSystem.h + FileSystem.cpp + StringUtils.h + StringUtils.cpp + DesktopServices.h + DesktopServices.cpp + Version.h + Version.cpp + Markdown.h +) + ######## Logging categories ######## ecm_qt_declare_logging_category(CORE_SOURCES @@ -1075,6 +1094,10 @@ qt_add_resources(LAUNCHER_RESOURCES ../${Launcher_Branding_LogoQRC} ) +qt_wrap_ui(WINDOWSUPDATER_UI + updater/windows/SelectReleaseDialog.ui +) + ######## Windows resource files ######## if(WIN32) set(LAUNCHER_RCS ${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_WindowsRC}) @@ -1158,7 +1181,41 @@ install(TARGETS ${Launcher_Name} FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime ) -if(WIN32) +if(WIN32 OR (DEFINED Launcher_BUILD_UPDATER AND Launcher_BUILD_UPDATER) ) + # Updater + add_library(windows_updater_logic STATIC ${WINDOWSUPDATEREXE_SOURCES} ${WINDOWSUPDATER_UI}) + target_include_directories(windows_updater_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + target_link_libraries(windows_updater_logic + systeminfo + BuildConfig + ghcFilesystem::ghc_filesystem + Qt${QT_VERSION_MAJOR}::Widgets + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Network + ${Launcher_QT_LIBS} + cmark::cmark + ) + + add_executable("${Launcher_Name}_updater" WIN32 updater/windows/windows_main.cpp) + target_link_libraries("${Launcher_Name}_updater" windows_updater_logic) + + if(DEFINED Launcher_APP_BINARY_NAME) + set_target_properties("${Launcher_Name}_updater" PROPERTIES OUTPUT_NAME "${Launcher_APP_BINARY_NAME}_updater") + endif() + if(DEFINED Launcher_BINARY_RPATH) + SET_TARGET_PROPERTIES("${Launcher_Name}_updater" PROPERTIES INSTALL_RPATH "${Launcher_BINARY_RPATH}") + endif() + + install(TARGETS "${Launcher_Name}_updater" + BUNDLE DESTINATION "." COMPONENT Runtime + LIBRARY DESTINATION ${LIBRARY_DEST_DIR} COMPONENT Runtime + RUNTIME DESTINATION ${BINARY_DEST_DIR} COMPONENT Runtime + FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime + ) +endif() + +if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER)) + # File link add_library(filelink_logic STATIC ${LINKEXE_SOURCES}) target_include_directories(filelink_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(filelink_logic diff --git a/launcher/Version.h b/launcher/Version.h index 659f8e54e..1bba44126 100644 --- a/launcher/Version.h +++ b/launcher/Version.h @@ -56,6 +56,7 @@ class Version { bool operator!=(const Version &other) const; QString toString() const { return m_string; } + bool isEmpty() const { return m_string.isEmpty(); } friend QDebug operator<<(QDebug debug, const Version& v); diff --git a/launcher/filelink/FileLink.cpp b/launcher/filelink/FileLink.cpp index c9599b820..79b30c665 100644 --- a/launcher/filelink/FileLink.cpp +++ b/launcher/filelink/FileLink.cpp @@ -111,6 +111,7 @@ FileLinkApp::FileLinkApp(int& argc, char** argv) : QCoreApplication(argc, argv), joinServer(serverToJoin); } else { qDebug() << "no server to join"; + m_status = Failed; exit(); } } @@ -126,6 +127,7 @@ void FileLinkApp::joinServer(QString server) connect(&socket, &QLocalSocket::readyRead, this, &FileLinkApp::readPathPairs); connect(&socket, &QLocalSocket::errorOccurred, this, [&](QLocalSocket::LocalSocketError socketError) { + m_status = Failed; switch (socketError) { case QLocalSocket::ServerNotFoundError: qDebug() @@ -150,6 +152,7 @@ void FileLinkApp::joinServer(QString server) connect(&socket, &QLocalSocket::disconnected, this, [&]() { qDebug() << "disconnected from server, should exit"; + m_status = Succeeded; exit(); }); diff --git a/launcher/filelink/FileLink.h b/launcher/filelink/FileLink.h index 4c47d9bbb..ab3e9d360 100644 --- a/launcher/filelink/FileLink.h +++ b/launcher/filelink/FileLink.h @@ -41,8 +41,17 @@ class FileLinkApp : public QCoreApplication { // friends for the purpose of limiting access to deprecated stuff Q_OBJECT public: + enum Status { + Starting, + Failed, + Succeeded, + Initialized + }; FileLinkApp(int& argc, char** argv); virtual ~FileLinkApp(); + Status status() const { + return m_status; + } private: void joinServer(QString server); @@ -50,6 +59,8 @@ class FileLinkApp : public QCoreApplication { void runLink(); void sendResults(); + Status m_status = Status::Starting; + bool m_useHardLinks = false; QDateTime m_startTime; diff --git a/launcher/filelink/main.cpp b/launcher/filelink/main.cpp index 83566a3c6..a656a9c96 100644 --- a/launcher/filelink/main.cpp +++ b/launcher/filelink/main.cpp @@ -26,5 +26,17 @@ int main(int argc, char* argv[]) { FileLinkApp ldh(argc, argv); - return ldh.exec(); + switch(ldh.status()) { + case FileLinkApp::Starting: + case FileLinkApp::Initialized: + { + return ldh.exec(); + } + case FileLinkApp::Failed: + return 1; + case FileLinkApp::Succeeded: + return 0; + default: + return -1; + } } diff --git a/launcher/updater/windows/GitHubRelease.h b/launcher/updater/windows/GitHubRelease.h new file mode 100644 index 000000000..1326a69f1 --- /dev/null +++ b/launcher/updater/windows/GitHubRelease.h @@ -0,0 +1,34 @@ +#pragma once +#include +#include +#include + +#include "Version.h" + +struct GitHubReleaseAsset { + int id = -1; + QString name; + QString label; + QString content_type; + int size; + QDateTime created_at; + QDateTime updated_at; + QString browser_download_url; + + bool isValid() { return id > 0; } +}; + +struct GitHubRelease { + int id = -1; + QString name; + QString tag_name; + QDateTime created_at; + QDateTime published_at; + bool prerelease; + bool draft; + QString body; + QList assets; + Version version; + + bool isValid() const { return id > 0; } +}; diff --git a/launcher/updater/windows/SelectReleaseDialog.ui b/launcher/updater/windows/SelectReleaseDialog.ui new file mode 100644 index 000000000..9d3613727 --- /dev/null +++ b/launcher/updater/windows/SelectReleaseDialog.ui @@ -0,0 +1,89 @@ + + + SelectReleaseDialog + + + + 0 + 0 + 478 + 517 + + + + Select Release to Install + + + true + + + + + + Please select the release you wish to update to. + + + + + + + true + + + + 1 + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + SelectReleaseDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + SelectReleaseDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/launcher/updater/windows/UpdaterDialogs.cpp b/launcher/updater/windows/UpdaterDialogs.cpp new file mode 100644 index 000000000..5949194b2 --- /dev/null +++ b/launcher/updater/windows/UpdaterDialogs.cpp @@ -0,0 +1,78 @@ +#include "UpdaterDialogs.h" + +#include "ui_SelectReleaseDialog.h" + +#include +#include "Markdown.h" + +SelectReleaseDialog::SelectReleaseDialog(const Version& current_version, const QList& releases, QWidget* parent) + : QDialog(parent), m_releases(releases), m_currentVersion(current_version), ui(new Ui::SelectReleaseDialog) +{ + ui->setupUi(this); + + ui->changelogTextBrowser->setOpenExternalLinks(true); + ui->changelogTextBrowser->setLineWrapMode(QTextBrowser::LineWrapMode::WidgetWidth); + ui->changelogTextBrowser->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded); + + ui->versionsTree->setColumnCount(2); + + ui->versionsTree->header()->setSectionResizeMode(0, QHeaderView::Stretch); + ui->versionsTree->header()->setSectionResizeMode(1, QHeaderView::Stretch); + ui->versionsTree->setHeaderLabels({tr("Verison"), tr("Published Date")}); + ui->versionsTree->header()->setStretchLastSection(false); + + ui->eplainLabel->setText(tr("Select a version to install.\n" + "\n" + "Currently installed version: %1") + .arg(m_currentVersion.toString())); + + loadReleases(); + + connect(ui->versionsTree, &QTreeWidget::currentItemChanged, this, &SelectReleaseDialog::selectionChanged); + + connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &SelectReleaseDialog::accept); + connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &SelectReleaseDialog::reject); +} + +SelectReleaseDialog::~SelectReleaseDialog() +{ + delete ui; +} + +void SelectReleaseDialog::loadReleases() +{ + for (auto rls : m_releases) { + appendRelease(rls); + } +} + +void SelectReleaseDialog::appendRelease(GitHubRelease const& release) +{ + auto rls_item = new QTreeWidgetItem(ui->versionsTree); + rls_item->setText(0, release.tag_name); + rls_item->setExpanded(true); + rls_item->setText(1, release.published_at.toString()); + rls_item->setData(0, Qt::UserRole, QVariant(release.id)); + + ui->versionsTree->addTopLevelItem(rls_item); +} + +GitHubRelease SelectReleaseDialog::getRelease(QTreeWidgetItem* item) { + int id = item->data(0, Qt::UserRole).toInt(); + GitHubRelease release; + for (auto rls: m_releases) { + if (rls.id == id) + release = rls; + } + return release; +} + +void SelectReleaseDialog::selectionChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous) +{ + GitHubRelease release = getRelease(current); + QString body = markdownToHTML(release.body.toUtf8()); + m_selectedRelease = release; + + ui->changelogTextBrowser->setHtml(body); +} + diff --git a/launcher/updater/windows/UpdaterDialogs.h b/launcher/updater/windows/UpdaterDialogs.h new file mode 100644 index 000000000..7481e499a --- /dev/null +++ b/launcher/updater/windows/UpdaterDialogs.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include + +#include "Version.h" +#include "updater/windows/GitHubRelease.h" + +namespace Ui { +class SelectReleaseDialog; +} + +class SelectReleaseDialog : public QDialog { + Q_OBJECT + + public: + explicit SelectReleaseDialog(const Version& cur_version, const QList& releases, QWidget* parent = 0); + ~SelectReleaseDialog(); + + void loadReleases(); + void appendRelease(GitHubRelease const& release); + GitHubRelease selectedRelease() { return m_selectedRelease; } + private slots: + GitHubRelease getRelease(QTreeWidgetItem* item); + void selectionChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous); + + protected: + QList m_releases; + GitHubRelease m_selectedRelease; + Version m_currentVersion; + + Ui::SelectReleaseDialog* ui; +}; diff --git a/launcher/updater/windows/WindowsUpdater.cpp b/launcher/updater/windows/WindowsUpdater.cpp new file mode 100644 index 000000000..f0ea9998c --- /dev/null +++ b/launcher/updater/windows/WindowsUpdater.cpp @@ -0,0 +1,647 @@ +// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> +// +// SPDX-License-Identifier: GPL-3.0-only + +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "WindowsUpdater.h" +#include "BuildConfig.h" + +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#if defined Q_OS_WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include +#endif + +// Snippet from https://github.com/gulrak/filesystem#using-it-as-single-file-header + +#ifdef __APPLE__ +#include // for deployment target to support pre-catalina targets without std::fs +#endif // __APPLE__ + +#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include) +#if __has_include() && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) +#define GHC_USE_STD_FS +#include +namespace fs = std::filesystem; +#endif // MacOS min version check +#endif // Other OSes version check + +#ifndef GHC_USE_STD_FS +#include +namespace fs = ghc::filesystem; +#endif + +#include + +#include "UpdaterDialogs.h" + +#include "FileSystem.h" +#include "Json.h" +#include "StringUtils.h" + +/** output to the log file */ +void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg) +{ + static std::mutex loggerMutex; + const std::lock_guard lock(loggerMutex); // synchronized, QFile logFile is not thread-safe + + QString out = qFormatLogMessage(type, context, msg); + out += QChar::LineFeed; + + WindowsUpdaterApp* app = static_cast(QCoreApplication::instance()); + app->logFile->write(out.toUtf8()); + app->logFile->flush(); + if (app->logToConsole) { + QTextStream(stderr) << out.toLocal8Bit(); + fflush(stderr); + } +} + +WindowsUpdaterApp::WindowsUpdaterApp(int& argc, char** argv) : QApplication(argc, argv) +{ +#if defined Q_OS_WIN32 + // attach the parent console + if (AttachConsole(ATTACH_PARENT_PROCESS)) { + // if attach succeeds, reopen and sync all the i/o + if (freopen("CON", "w", stdout)) { + std::cout.sync_with_stdio(); + } + if (freopen("CON", "w", stderr)) { + std::cerr.sync_with_stdio(); + } + if (freopen("CON", "r", stdin)) { + std::cin.sync_with_stdio(); + } + auto out = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD written; + const char* endline = "\n"; + WriteConsole(out, endline, strlen(endline), &written, NULL); + consoleAttached = true; + } +#endif + setOrganizationName(BuildConfig.LAUNCHER_NAME); + setOrganizationDomain(BuildConfig.LAUNCHER_DOMAIN); + setApplicationName(BuildConfig.LAUNCHER_NAME + "Updater"); + setApplicationVersion(BuildConfig.printableVersionString() + "\n" + BuildConfig.GIT_COMMIT); + + // Commandline parsing + QCommandLineParser parser; + parser.setApplicationDescription(QObject::tr("An auto-updater for Prism Launcher")); + + parser.addOptions({ { { "d", "dir" }, "Use a custom path as application root (use '.' for current directory).", "directory" }, + { { "I", "install-version" }, "Install a spesfic version.", "version name" }, + { { "U", "update-url" }, "Update from the spesified repo.", "github repo url" }, + { { "e", "executable" }, "Path to the prismluancher executable.", "path" }, + { { "c", "check-only" }, + "Only check if an update is needed. Exit status 100 if true, 0 if false (or non 0 if there was an error)." }, + { { "F", "force" }, "Force an update, even if one is not needed." }, + { { "l", "list" }, "List avalible releases." }, + { "debug", "Log debug to console." }, + { { "L", "latest" }, "Update to the latest avalible version." }, + { { "D", "allow-downgrade" }, "Allow the updater to downgrade to previous verisons." } }); + + parser.addHelpOption(); + parser.addVersionOption(); + parser.process(arguments()); + + logToConsole = parser.isSet("debug"); + + auto prism_executable = parser.value("executable"); + if (prism_executable.isEmpty()) { + prism_executable = QCoreApplication::applicationDirPath() + "/" + BuildConfig.LAUNCHER_APP_BINARY_NAME; +#if defined(Q_OS_WIN32) + prism_executable += ".exe"; +#endif + } + m_prismExecutable = prism_executable; + + auto prism_update_url = parser.value("update-url"); + if (prism_update_url.isEmpty()) + prism_update_url = "https://github.com/PrismLauncher/PrismLauncher"; + m_prismRepoUrl = QUrl::fromUserInput(prism_update_url); + + m_checkOnly = parser.isSet("check-only"); + m_forceUpdate = parser.isSet("force"); + m_printOnly = parser.isSet("list"); + auto user_version = parser.value("install-version"); + if (!user_version.isEmpty()) { + m_userSelectedVersion = Version(user_version); + } + m_updateLatest = parser.isSet("latest"); + m_allowDowngrade = parser.isSet("allow-downgrade"); + + QString origcwdPath = QDir::currentPath(); + QString binPath = applicationDirPath(); + + { // find data director + // Root path is used for updates and portable data +#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) + QDir foo(FS::PathCombine(binPath, "..")); // typically portable-root or /usr + m_rootPath = foo.absolutePath(); +#elif defined(Q_OS_WIN32) + m_rootPath = binPath; +#elif defined(Q_OS_MAC) + QDir foo(FS::PathCombine(binPath, "../..")); + m_rootPath = foo.absolutePath(); + // on macOS, touch the root to force Finder to reload the .app metadata (and fix any icon change issues) + FS::updateTimestamp(m_rootPath); +#endif + } + + QString adjustedBy; + QString dataPath; + // change folder + QString dirParam = parser.value("dir"); + if (!dirParam.isEmpty()) { + // the dir param. it makes multimc data path point to whatever the user specified + // on command line + adjustedBy = "Command line"; + dataPath = dirParam; + } else { + QDir foo(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "..")); + dataPath = foo.absolutePath(); + adjustedBy = "Persistent data path"; + +#ifndef Q_OS_MACOS + if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) { + dataPath = m_rootPath; + adjustedBy = "Portable data path"; + m_portable = true; + } +#endif + } + m_network = new QNetworkAccessManager(); + + { // setup logging + static const QString logBase = BuildConfig.LAUNCHER_NAME + "Updater" + (m_checkOnly ? "-CheckOnly" : "") + "-%0.log"; + auto moveFile = [](const QString& oldName, const QString& newName) { + QFile::remove(newName); + QFile::copy(oldName, newName); + QFile::remove(oldName); + }; + + moveFile(logBase.arg(3), logBase.arg(4)); + moveFile(logBase.arg(2), logBase.arg(3)); + moveFile(logBase.arg(1), logBase.arg(2)); + moveFile(logBase.arg(0), logBase.arg(1)); + + logFile = std::unique_ptr(new QFile(logBase.arg(0))); + if (!logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { + showFatalErrorMessage("The launcher data folder is not writable!", + QString("The launcher couldn't create a log file - the data folder is not writable.\n" + "\n" + "Make sure you have write permissions to the data folder.\n" + "(%1)\n" + "\n" + "The launcher cannot continue until you fix this problem.") + .arg(dataPath)); + return; + } + qInstallMessageHandler(appDebugOutput); + + qSetMessagePattern( + "%{time process}" + " " + "%{if-debug}D%{endif}" + "%{if-info}I%{endif}" + "%{if-warning}W%{endif}" + "%{if-critical}C%{endif}" + "%{if-fatal}F%{endif}" + " " + "|" + " " + "%{if-category}[%{category}]: %{endif}" + "%{message}"); + + bool foundLoggingRules = false; + + auto logRulesFile = QStringLiteral("qtlogging.ini"); + auto logRulesPath = FS::PathCombine(dataPath, logRulesFile); + + qDebug() << "Testing" << logRulesPath << "..."; + foundLoggingRules = QFile::exists(logRulesPath); + + // search the dataPath() + // seach app data standard path + if (!foundLoggingRules && !isPortable() && dirParam.isEmpty()) { + logRulesPath = QStandardPaths::locate(QStandardPaths::AppDataLocation, FS::PathCombine("..", logRulesFile)); + if (!logRulesPath.isEmpty()) { + qDebug() << "Found" << logRulesPath << "..."; + foundLoggingRules = true; + } + } + // seach root path + if (!foundLoggingRules) { + logRulesPath = FS::PathCombine(m_rootPath, logRulesFile); + qDebug() << "Testing" << logRulesPath << "..."; + foundLoggingRules = QFile::exists(logRulesPath); + } + + if (foundLoggingRules) { + // load and set logging rules + qDebug() << "Loading logging rules from:" << logRulesPath; + QSettings loggingRules(logRulesPath, QSettings::IniFormat); + loggingRules.beginGroup("Rules"); + QStringList rule_names = loggingRules.childKeys(); + QStringList rules; + qDebug() << "Setting log rules:"; + for (auto rule_name : rule_names) { + auto rule = QString("%1=%2").arg(rule_name).arg(loggingRules.value(rule_name).toString()); + rules.append(rule); + qDebug() << " " << rule; + } + auto rules_str = rules.join("\n"); + QLoggingCategory::setFilterRules(rules_str); + } + + qDebug() << "<> Log initialized."; + } + + { // log debug program info + qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME) << "Updater" + << ", (c) 2022-2023 " << qPrintable(QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", ")); + qDebug() << "Version : " << BuildConfig.printableVersionString(); + qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT; + qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC; + if (adjustedBy.size()) { + qDebug() << "Work dir before adjustment : " << origcwdPath; + qDebug() << "Work dir after adjustment : " << QDir::currentPath(); + qDebug() << "Adjusted by : " << adjustedBy; + } else { + qDebug() << "Work dir : " << QDir::currentPath(); + } + qDebug() << "Binary path : " << binPath; + qDebug() << "Application root path : " << m_rootPath; + qDebug() << "<> Paths set."; + } + + loadReleaseList(); +} + +WindowsUpdaterApp::~WindowsUpdaterApp() +{ + qDebug() << "updater shutting down"; + // Shut down logger by setting the logger function to nothing + qInstallMessageHandler(nullptr); + +#if defined Q_OS_WIN32 + // Detach from Windows console + if (consoleAttached) { + fclose(stdout); + fclose(stdin); + fclose(stderr); + FreeConsole(); + } +#endif + + m_network->deleteLater(); + if (m_reply) + m_reply->deleteLater(); +} + +void WindowsUpdaterApp::fail(const QString& reason) +{ + qCritical() << qPrintable(reason); + m_status = Failed; + exit(1); +} + +void WindowsUpdaterApp::abort(const QString& reason) +{ + qCritical() << qPrintable(reason); + m_status = Aborted; + exit(2); +} + +void WindowsUpdaterApp::showFatalErrorMessage(const QString& title, const QString& content) +{ + m_status = Failed; + auto msgBox = new QMessageBox(); + msgBox->setWindowTitle(title); + msgBox->setText(content); + msgBox->setStandardButtons(QMessageBox::Ok); + msgBox->setDefaultButton(QMessageBox::Ok); + msgBox->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextBrowserInteraction); + msgBox->setIcon(QMessageBox::Critical); + msgBox->exec(); + exit(1); +} + +void WindowsUpdaterApp::run() +{ + qDebug() << "found" << m_releases.length() << "releases on github"; + qDebug() << "loading exe at " << m_prismExecutable; + + if (m_printOnly) { + printReleases(); + m_status = Succeeded; + return exit(0); + } + + loadPrismVersionFromExe(m_prismExecutable); + m_status = Succeeded; + + qDebug() << "Executable reports as:" << m_prismBinaryName << "version:" << m_prismVerison; + qDebug() << "Version major:" << m_prismVersionMajor; + qDebug() << "Verison minor:" << m_prismVersionMinor; + qDebug() << "Verison channel:" << m_prsimVersionChannel; + qDebug() << "Git Commit:" << m_prismGitCommit; + + auto latest = getLatestRelease(); + qDebug() << "Latest release" << latest.version; + auto need_update = needUpdate(latest); + + if (m_checkOnly) { + if (need_update) + return exit(100); + else + return exit(0); + } + + if (need_update || m_forceUpdate || !m_userSelectedVersion.isEmpty()) { + GitHubRelease update_release = latest; + if (!m_userSelectedVersion.isEmpty()) { + bool found = false; + for (auto rls : m_releases) { + if (rls.version == m_userSelectedVersion) { + found = true; + update_release = rls; + break; + } + } + if (!found) { + showFatalErrorMessage( + "No release for version!", + QString("Can not find a github relase for user spesified verison %1").arg(m_userSelectedVersion.toString())); + return; + } + } else if (!m_updateLatest) { + update_release = selectRelease(); + if (!update_release.isValid()) { + showFatalErrorMessage("No version selected.", "No version was selected."); + return; + } + } + + performUpdate(update_release); + } + + exit(0); +} + +void WindowsUpdaterApp::printReleases() +{ + for (auto release : m_releases) { + std::cout << release.name.toStdString() << " Version: " << release.tag_name.toStdString() << std::endl; + } +} + +QList WindowsUpdaterApp::nonDraftReleases() +{ + QList nonDraft; + for (auto rls : m_releases) { + if (rls.isValid() && !rls.draft) + nonDraft.append(rls); + } + return nonDraft; +} + +QList WindowsUpdaterApp::newerReleases() +{ + QList newer; + for (auto rls : nonDraftReleases()) { + if (rls.version > m_prismVerison) + newer.append(rls); + } + return newer; +} + +GitHubRelease WindowsUpdaterApp::selectRelease() +{ + QList releases; + + if (m_allowDowngrade) { + releases = nonDraftReleases(); + } else { + releases = newerReleases(); + } + + if (releases.isEmpty()) + return {}; + + SelectReleaseDialog dlg(Version(m_prismVerison), releases); + auto result = dlg.exec(); + + GitHubRelease release = dlg.selectedRelease(); + if (result == QDialog::Rejected) { + return {}; + } + + return release; +} + +void WindowsUpdaterApp::performUpdate(const GitHubRelease& release) +{ + qDebug() << "Updating to" << release.tag_name; +} + +void WindowsUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) +{ + QProcess proc = QProcess(); + proc.start(exe_path, { "-v" }); + proc.waitForFinished(); + auto out = proc.readAll(); + auto lines = out.split('\n'); + if (lines.length() < 2) + return; + auto first = lines.takeFirst(); + auto first_parts = first.split(' '); + if (first_parts.length() < 2) + return; + m_prismBinaryName = first_parts.takeFirst(); + auto version = first_parts.takeFirst(); + m_prismVerison = version; + if (version.contains('-')) { + auto index = version.indexOf('-'); + m_prsimVersionChannel = version.mid(index + 1); + version = version.left(index); + } else { + m_prsimVersionChannel = "stable"; + } + auto version_parts = version.split('.'); + m_prismVersionMajor = version_parts.takeFirst().toInt(); + m_prismVersionMinor = version_parts.takeFirst().toInt(); + m_prismGitCommit = lines.takeFirst().simplified(); +} + +void WindowsUpdaterApp::loadReleaseList() +{ + auto github_repo = m_prismRepoUrl; + if (github_repo.host() != "github.com") + return fail("updating from a non github url is not supported"); + + auto path_parts = github_repo.path().split('/'); + path_parts.removeFirst(); // empty segment from leading / + auto repo_owner = path_parts.takeFirst(); + auto repo_name = path_parts.takeFirst(); + auto api_url = QString("https://api.github.com/repos/%1/%2/releases").arg(repo_owner, repo_name); + + qDebug() << "Fetching release list from" << api_url; + + downloadReleasePage(api_url, 1); +} + +void WindowsUpdaterApp::downloadReleasePage(const QString& api_url, int page) +{ + int per_page = 30; + auto page_url = QString("%1?per_page=%2&page=%3").arg(api_url).arg(QString::number(per_page)).arg(QString::number(page)); + QNetworkRequest request(page_url); + request.setRawHeader("Accept", "application/vnd.github+json"); + request.setRawHeader("X-GitHub-Api-Version", "2022-11-28"); + + QNetworkReply* rep = m_network->get(request); + m_reply = rep; + auto responce = new QByteArray(); + + connect(rep, &QNetworkReply::finished, this, [this, responce, per_page, api_url, page]() { + int num_found = parseReleasePage(responce); + delete responce; + + if (!(num_found < per_page)) { // there may be more, fetch next page + downloadReleasePage(api_url, page + 1); + } else { + run(); + } + }); +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 + connect(rep, &QNetworkReply::errorOccurred, this, &WindowsUpdaterApp::downloadError); +#else + connect(rep, QOverload::of(&QNetworkReply::error), this, &WindowsUpdaterApp::downloadError); +#endif + connect(rep, &QNetworkReply::sslErrors, this, &WindowsUpdaterApp::sslErrors); + connect(rep, &QNetworkReply::readyRead, this, [this, responce]() { + auto data = m_reply->readAll(); + responce->append(data); + }); +} + +int WindowsUpdaterApp::parseReleasePage(const QByteArray* responce) +{ + if (responce->isEmpty()) // empty page + return 0; + int num_releases = 0; + try { + auto doc = Json::requireDocument(*responce); + auto release_list = Json::requireArray(doc); + for (auto release_json : release_list) { + auto release_obj = Json::requireObject(release_json); + + GitHubRelease release = {}; + release.id = Json::requireInteger(release_obj, "id"); + release.name = Json::ensureString(release_obj, "name"); + release.tag_name = Json::requireString(release_obj, "tag_name"); + release.created_at = QDateTime::fromString(Json::requireString(release_obj, "created_at"), Qt::ISODate); + release.published_at = QDateTime::fromString(Json::ensureString(release_obj, "published_at"), Qt::ISODate); + release.draft = Json::requireBoolean(release_obj, "draft"); + release.prerelease = Json::requireBoolean(release_obj, "prerelease"); + release.body = Json::ensureString(release_obj, "body"); + release.version = Version(release.tag_name); + + auto release_assets_obj = Json::requireArray(release_obj, "assets"); + for (auto asset_json : release_assets_obj) { + auto asset_obj = Json::requireObject(asset_json); + GitHubReleaseAsset asset = {}; + asset.id = Json::requireInteger(asset_obj, "id"); + asset.name = Json::requireString(asset_obj, "name"); + asset.label = Json::ensureString(asset_obj, "label"); + asset.content_type = Json::requireString(asset_obj, "content_type"); + asset.size = Json::requireInteger(asset_obj, "size"); + asset.created_at = QDateTime::fromString(Json::requireString(asset_obj, "created_at"), Qt::ISODate); + asset.updated_at = QDateTime::fromString(Json::requireString(asset_obj, "updated_at"), Qt::ISODate); + asset.browser_download_url = Json::requireString(asset_obj, "browser_download_url"); + release.assets.append(asset); + } + m_releases.append(release); + num_releases++; + } + } catch (Json::JsonException& e) { + auto err_msg = + QString("Failed to parse releases from github: %1\n%2").arg(e.what()).arg(QString::fromStdString(responce->toStdString())); + fail(err_msg); + } + return num_releases; +} + +GitHubRelease WindowsUpdaterApp::getLatestRelease() +{ + GitHubRelease latest; + for (auto release : m_releases) { + if (!latest.isValid() || (!release.draft && release.version > latest.version)) { + latest = release; + } + } + return latest; +} + +bool WindowsUpdaterApp::needUpdate(const GitHubRelease& release) +{ + auto current_ver = Version(QString("%1.%2").arg(QString::number(m_prismVersionMajor)).arg(QString::number(m_prismVersionMinor))); + return current_ver < release.version; +} + +void WindowsUpdaterApp::downloadError(QNetworkReply::NetworkError error) +{ + if (error == QNetworkReply::OperationCanceledError) { + abort(QString("Aborted %1").arg(m_reply->url().toString())); + } else { + fail(QString("Network request Failed: %1 with reason %2").arg(m_reply->url().toString()).arg(error)); + } +} + +void WindowsUpdaterApp::sslErrors(const QList& errors) +{ + int i = 1; + QString err_msg; + for (auto error : errors) { + err_msg.append(QString("Network request %1 SSL Error %2: %3\n").arg(m_reply->url().toString()).arg(i).arg(error.errorString())); + auto cert = error.certificate(); + err_msg.append(QString("Certificate in question:\n%1").arg(cert.toText())); + i++; + } + fail(err_msg); +} diff --git a/launcher/updater/windows/WindowsUpdater.h b/launcher/updater/windows/WindowsUpdater.h new file mode 100644 index 000000000..b79a86271 --- /dev/null +++ b/launcher/updater/windows/WindowsUpdater.h @@ -0,0 +1,110 @@ +// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> +// +// SPDX-License-Identifier: GPL-3.0-only + +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PRISM_EXTERNAL_EXE +#include "FileSystem.h" + +#include "updater/windows/GitHubRelease.h" + +class WindowsUpdaterApp : public QApplication { + // friends for the purpose of limiting access to deprecated stuff + Q_OBJECT + public: + enum Status { Starting, Failed, Succeeded, Initialized, Aborted }; + WindowsUpdaterApp(int& argc, char** argv); + virtual ~WindowsUpdaterApp(); + void loadReleaseList(); + void run(); + Status status() const { return m_status; } + + private: + void fail(const QString& reason); + void abort(const QString& reason); + void showFatalErrorMessage(const QString& title, const QString& content); + + void loadPrismVersionFromExe(const QString& exe_path); + + void downloadReleasePage(const QString& api_url, int page); + int parseReleasePage(const QByteArray* responce); + GitHubRelease getLatestRelease(); + bool needUpdate(const GitHubRelease& release); + GitHubRelease selectRelease(); + void performUpdate(const GitHubRelease& release); + void printReleases(); + QList newerReleases(); + QList nonDraftReleases(); + + void downloadError(QNetworkReply::NetworkError error); + void sslErrors(const QList& errors); + + const QString& root() { return m_rootPath; } + + bool isPortable() { return m_portable; } + + QString m_rootPath; + bool m_portable = false; + QString m_prismExecutable; + QUrl m_prismRepoUrl; + Version m_userSelectedVersion; + bool m_checkOnly; + bool m_forceUpdate; + bool m_printOnly; + bool m_updateLatest; + bool m_allowDowngrade; + + QString m_prismBinaryName; + QString m_prismVerison; + int m_prismVersionMajor; + int m_prismVersionMinor; + QString m_prsimVersionChannel; + QString m_prismGitCommit; + + Status m_status = Status::Starting; + QNetworkAccessManager* m_network; + QNetworkReply* m_reply; + QList m_releases; + + public: + std::unique_ptr logFile; + bool logToConsole = false; + +#if defined Q_OS_WIN32 + // used on Windows to attach the standard IO streams + bool consoleAttached = false; +#endif +}; diff --git a/launcher/updater/windows/windows_main.cpp b/launcher/updater/windows/windows_main.cpp new file mode 100644 index 000000000..31d7646b5 --- /dev/null +++ b/launcher/updater/windows/windows_main.cpp @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> +// +// SPDX-License-Identifier: GPL-3.0-only + +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + + +#include "updater/windows/WindowsUpdater.h" +int main(int argc, char* argv[]) +{ + WindowsUpdaterApp wUpApp(argc, argv); + + switch(wUpApp.status()) { + case WindowsUpdaterApp::Starting: + case WindowsUpdaterApp::Initialized: + { + return wUpApp.exec(); + } + case WindowsUpdaterApp::Failed: + return 1; + case WindowsUpdaterApp::Succeeded: + return 0; + default: + return -1; + } + +} From 0b2044e9a434ba3d2724630399bc41ebb321b039 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 31 May 2023 13:52:23 -0700 Subject: [PATCH 026/268] fix: typo - no space before comment Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/minecraft/ComponentUpdateTask_p.h | 1 + 1 file changed, 1 insertion(+) diff --git a/launcher/minecraft/ComponentUpdateTask_p.h b/launcher/minecraft/ComponentUpdateTask_p.h index 5b02431b2..84f6f9a63 100644 --- a/launcher/minecraft/ComponentUpdateTask_p.h +++ b/launcher/minecraft/ComponentUpdateTask_p.h @@ -4,6 +4,7 @@ #include #include #include "net/Mode.h" +#include "ComponentUpdateTask.h" class PackProfile; From f5729d9a7c953d88ae60e106a787b1d7c35ede8b Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 31 May 2023 13:53:44 -0700 Subject: [PATCH 027/268] Revert "fix: typo - no space before comment" This reverts commit 0b2044e9a434ba3d2724630399bc41ebb321b039. --- launcher/minecraft/ComponentUpdateTask_p.h | 1 - 1 file changed, 1 deletion(-) diff --git a/launcher/minecraft/ComponentUpdateTask_p.h b/launcher/minecraft/ComponentUpdateTask_p.h index 84f6f9a63..5b02431b2 100644 --- a/launcher/minecraft/ComponentUpdateTask_p.h +++ b/launcher/minecraft/ComponentUpdateTask_p.h @@ -4,7 +4,6 @@ #include #include #include "net/Mode.h" -#include "ComponentUpdateTask.h" class PackProfile; From 2dce08caf16ac80452ccaebed5e4f24c0ec7418a Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 31 May 2023 13:54:13 -0700 Subject: [PATCH 028/268] fix: typo - space before comment Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- program_info/win_install.nsi.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program_info/win_install.nsi.in b/program_info/win_install.nsi.in index 27c400395..8389e2468 100644 --- a/program_info/win_install.nsi.in +++ b/program_info/win_install.nsi.in @@ -293,7 +293,7 @@ Function RunUninstall stringloop: ; get string length StrCpy $2 $1 1 $3 ; get next char IntOp $3 $3 + 1 ; index += 1 - StrCmp $2 "" +2 stringloop; if empty exit loop + StrCmp $2 "" +2 stringloop ; if empty exit loop IntOp $3 $3 - 1 ; index -= 1 Goto run quoteloop: ; get string length with quotes removed From 7c85462ff371f2400bd9cae8a80904aa6175ac73 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 31 May 2023 14:31:38 -0700 Subject: [PATCH 029/268] packaging: include file manifest in portable install Signed-off-by: Rachel Powers <508861+Ryex@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 691e257bf..838eddef0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -411,12 +411,14 @@ jobs: run: | cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable + cat ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt - name: Package (Windows MSVC, portable) if: runner.os == 'Windows' && matrix.msystem == '' run: | cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable + cat ${{ env.BUILD_DIR }}/install_manifest.txt >> ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt - name: Package (Windows, installer) if: runner.os == 'Windows' From dbe14c6be5ff3ae015c783a406a0d13759475aa0 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 31 May 2023 14:54:06 -0700 Subject: [PATCH 030/268] packaging: ensure all files are in manifest Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/build.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 838eddef0..f309a3d06 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -375,11 +375,13 @@ jobs: shell: msys2 {0} run: | cmake --install ${{ env.BUILD_DIR }} + Copy-Item ${{ env.BUILD_DIR }}/install_manifest.txt ${{ env.INSTALL_DIR }}/manifest.txt - name: Package (Windows MSVC) if: runner.os == 'Windows' && matrix.msystem == '' run: | cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build_type }} + Copy-Item ${{ env.BUILD_DIR }}/install_manifest.txt ${{ env.INSTALL_DIR }}/manifest.txt cd ${{ env.INSTALL_DIR }} if ("${{ matrix.qt_ver }}" -eq "5") @@ -418,7 +420,7 @@ jobs: run: | cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable - cat ${{ env.BUILD_DIR }}/install_manifest.txt >> ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt + cat ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt - name: Package (Windows, installer) if: runner.os == 'Windows' From 68c28335520ca4a4a17d47f863dd92a0b9a00904 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 31 May 2023 14:55:13 -0700 Subject: [PATCH 031/268] packaging: use powershell name Signed-off-by: Rachel Powers <508861+Ryex@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 f309a3d06..dcc891c62 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -413,14 +413,14 @@ jobs: run: | cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable - cat ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt + Get-Content ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt - name: Package (Windows MSVC, portable) if: runner.os == 'Windows' && matrix.msystem == '' run: | cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable - cat ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt + Get-Content ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt - name: Package (Windows, installer) if: runner.os == 'Windows' From 4cbae0a751f54aa77a5e49cce23a3e4b5bea9a37 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 31 May 2023 15:36:25 -0700 Subject: [PATCH 032/268] packaging: trim manfest paths to be relative Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dcc891c62..338f71a05 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -375,13 +375,13 @@ jobs: shell: msys2 {0} run: | cmake --install ${{ env.BUILD_DIR }} - Copy-Item ${{ env.BUILD_DIR }}/install_manifest.txt ${{ env.INSTALL_DIR }}/manifest.txt + Get-Content ${{ env.BUILD_DIR }}/install_manifest.txt | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('./') } | Out-File -Append -FilePath ${{ env.INSTALL_DIR }}/manifest.txt - name: Package (Windows MSVC) if: runner.os == 'Windows' && matrix.msystem == '' run: | cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build_type }} - Copy-Item ${{ env.BUILD_DIR }}/install_manifest.txt ${{ env.INSTALL_DIR }}/manifest.txt + Get-Content ${{ env.BUILD_DIR }}/install_manifest.txt | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('./') } | Out-File -Append -FilePath ${{ env.INSTALL_DIR }}/manifest.txt cd ${{ env.INSTALL_DIR }} if ("${{ matrix.qt_ver }}" -eq "5") @@ -413,14 +413,14 @@ jobs: run: | cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable - Get-Content ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt + Get-Content ${{ env.BUILD_DIR }}/install_manifest_portable.txt | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('./') } | Out-File -Append -FilePath ${{ env.INSTALL_DIR }}/manifest.txt - name: Package (Windows MSVC, portable) if: runner.os == 'Windows' && matrix.msystem == '' run: | cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable - Get-Content ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt + Get-Content ${{ env.BUILD_DIR }}/install_manifest_portable.txt | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('./') } | Out-File -Append -FilePath ${{ env.INSTALL_DIR }}/manifest.txt - name: Package (Windows, installer) if: runner.os == 'Windows' From 9b1e0eb57d4e59421bb019b866688c4138b15d7e Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 31 May 2023 16:01:31 -0700 Subject: [PATCH 033/268] packaging: msys2 shell is bash Signed-off-by: Rachel Powers <508861+Ryex@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 338f71a05..fa1081c5c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -375,7 +375,7 @@ jobs: shell: msys2 {0} run: | cmake --install ${{ env.BUILD_DIR }} - Get-Content ${{ env.BUILD_DIR }}/install_manifest.txt | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('./') } | Out-File -Append -FilePath ${{ env.INSTALL_DIR }}/manifest.txt + while read l; do l=${l#${{ env.INSTALL_DIR }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest.txt >> ${{ env.INSTALL_DIR }}/manifest.txt - name: Package (Windows MSVC) if: runner.os == 'Windows' && matrix.msystem == '' @@ -413,7 +413,7 @@ jobs: run: | cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable - Get-Content ${{ env.BUILD_DIR }}/install_manifest_portable.txt | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('./') } | Out-File -Append -FilePath ${{ env.INSTALL_DIR }}/manifest.txt + while read l; do l=${l#${{ env.INSTALL_DIR }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_DIR }}/manifest.txt - name: Package (Windows MSVC, portable) if: runner.os == 'Windows' && matrix.msystem == '' From 4e0a08345d7049fe9785a88d66e8216d3bc573b7 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 31 May 2023 16:07:29 -0700 Subject: [PATCH 034/268] packaging: add manifest.txt to linux tarballs Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fa1081c5c..ef4becc08 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -441,6 +441,7 @@ jobs: if: runner.os == 'Linux' run: | cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_DIR }} + while read l; do l=${l#${{ env.INSTALL_DIR }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest.txt >> ${{ env.INSTALL_DIR }}/manifest.txt cd ${{ env.INSTALL_DIR }} tar --owner root --group root -czf ../PrismLauncher.tar.gz * @@ -450,6 +451,8 @@ jobs: run: | cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable + while read l; do l=${l#${{ env.INSTALL_DIR_PORTABLE }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest.txt >> ${{ env.INSTALL_DIR_PORTABLE }}/manifest.txt + while read l; do l=${l#${{ env.INSTALL_DIR_PORTABLE }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_DIR_PORTABLE }}/manifest.txt cd ${{ env.INSTALL_PORTABLE_DIR }} tar -czf ../PrismLauncher-portable.tar.gz * From b48540dc322ab9eb5f0f270598f2720b13599fae Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 31 May 2023 16:27:53 -0700 Subject: [PATCH 035/268] packaging: trim leading working dir from manifest paths Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/build.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ef4becc08..2fde49226 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -375,13 +375,13 @@ jobs: shell: msys2 {0} run: | cmake --install ${{ env.BUILD_DIR }} - while read l; do l=${l#${{ env.INSTALL_DIR }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest.txt >> ${{ env.INSTALL_DIR }}/manifest.txt + while read l; do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest.txt >> ${{ env.INSTALL_DIR }}/manifest.txt - name: Package (Windows MSVC) if: runner.os == 'Windows' && matrix.msystem == '' run: | cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build_type }} - Get-Content ${{ env.BUILD_DIR }}/install_manifest.txt | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('./') } | Out-File -Append -FilePath ${{ env.INSTALL_DIR }}/manifest.txt + Get-Content ${{ env.BUILD_DIR }}/install_manifest.txt | %{ $_.TrimStart("$pwd/") } | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('./') } | Out-File -Append -FilePath ${{ env.INSTALL_DIR }}/manifest.txt cd ${{ env.INSTALL_DIR }} if ("${{ matrix.qt_ver }}" -eq "5") @@ -413,14 +413,14 @@ jobs: run: | cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable - while read l; do l=${l#${{ env.INSTALL_DIR }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_DIR }}/manifest.txt + while read l; do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_DIR }}/manifest.txt - name: Package (Windows MSVC, portable) if: runner.os == 'Windows' && matrix.msystem == '' run: | cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable - Get-Content ${{ env.BUILD_DIR }}/install_manifest_portable.txt | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('./') } | Out-File -Append -FilePath ${{ env.INSTALL_DIR }}/manifest.txt + Get-Content ${{ env.BUILD_DIR }}/install_manifest_portable.txt | %{ $_.TrimStart("$pwd/") } | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('./') } | Out-File -Append -FilePath ${{ env.INSTALL_DIR }}/manifest.txt - name: Package (Windows, installer) if: runner.os == 'Windows' @@ -441,7 +441,7 @@ jobs: if: runner.os == 'Linux' run: | cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_DIR }} - while read l; do l=${l#${{ env.INSTALL_DIR }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest.txt >> ${{ env.INSTALL_DIR }}/manifest.txt + while read l; do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest.txt >> ${{ env.INSTALL_DIR }}/manifest.txt cd ${{ env.INSTALL_DIR }} tar --owner root --group root -czf ../PrismLauncher.tar.gz * @@ -451,8 +451,8 @@ jobs: run: | cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable - while read l; do l=${l#${{ env.INSTALL_DIR_PORTABLE }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest.txt >> ${{ env.INSTALL_DIR_PORTABLE }}/manifest.txt - while read l; do l=${l#${{ env.INSTALL_DIR_PORTABLE }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_DIR_PORTABLE }}/manifest.txt + while read l; do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR_PORTABLE }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest.txt >> ${{ env.INSTALL_DIR_PORTABLE }}/manifest.txt + while read l; do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR_PORTABLE }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_DIR_PORTABLE }}/manifest.txt cd ${{ env.INSTALL_PORTABLE_DIR }} tar -czf ../PrismLauncher-portable.tar.gz * From 3bbe33132af3d979200d03a16d625ecf7e9cfc51 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 31 May 2023 19:39:14 -0700 Subject: [PATCH 036/268] packaging: convert msys paths Signed-off-by: Rachel Powers <508861+Ryex@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 2fde49226..4f59354b5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -375,7 +375,7 @@ jobs: shell: msys2 {0} run: | cmake --install ${{ env.BUILD_DIR }} - while read l; do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest.txt >> ${{ env.INSTALL_DIR }}/manifest.txt + while read l; do l;=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest.txt >> ${{ env.INSTALL_DIR }}/manifest.txt - name: Package (Windows MSVC) if: runner.os == 'Windows' && matrix.msystem == '' @@ -413,7 +413,7 @@ jobs: run: | cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable - while read l; do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_DIR }}/manifest.txt + while read l; do l=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_DIR }}/manifest.txt - name: Package (Windows MSVC, portable) if: runner.os == 'Windows' && matrix.msystem == '' From 46a13c876771bf521dbaf1902623f080b4843bd1 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 31 May 2023 20:16:58 -0700 Subject: [PATCH 037/268] packaging: fix typo ; in middle of line Signed-off-by: Rachel Powers <508861+Ryex@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 4f59354b5..f103ecc1e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -375,7 +375,7 @@ jobs: shell: msys2 {0} run: | cmake --install ${{ env.BUILD_DIR }} - while read l; do l;=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest.txt >> ${{ env.INSTALL_DIR }}/manifest.txt + while read l; do l=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest.txt >> ${{ env.INSTALL_DIR }}/manifest.txt - name: Package (Windows MSVC) if: runner.os == 'Windows' && matrix.msystem == '' From 5627b4a9c555a6e2b3fe781500a4c103e9deee0b Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Thu, 1 Jun 2023 13:45:36 -0700 Subject: [PATCH 038/268] refactor: rename updater files Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/updater/{windows => prismupdater}/GitHubRelease.h | 0 .../{windows/WindowsUpdater.cpp => prismupdater/PrismUpdater.cpp} | 0 .../{windows/WindowsUpdater.h => prismupdater/PrismUpdater.h} | 0 launcher/updater/{windows => prismupdater}/SelectReleaseDialog.ui | 0 launcher/updater/{windows => prismupdater}/UpdaterDialogs.cpp | 0 launcher/updater/{windows => prismupdater}/UpdaterDialogs.h | 0 launcher/updater/{windows => prismupdater}/windows_main.cpp | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename launcher/updater/{windows => prismupdater}/GitHubRelease.h (100%) rename launcher/updater/{windows/WindowsUpdater.cpp => prismupdater/PrismUpdater.cpp} (100%) rename launcher/updater/{windows/WindowsUpdater.h => prismupdater/PrismUpdater.h} (100%) rename launcher/updater/{windows => prismupdater}/SelectReleaseDialog.ui (100%) rename launcher/updater/{windows => prismupdater}/UpdaterDialogs.cpp (100%) rename launcher/updater/{windows => prismupdater}/UpdaterDialogs.h (100%) rename launcher/updater/{windows => prismupdater}/windows_main.cpp (100%) diff --git a/launcher/updater/windows/GitHubRelease.h b/launcher/updater/prismupdater/GitHubRelease.h similarity index 100% rename from launcher/updater/windows/GitHubRelease.h rename to launcher/updater/prismupdater/GitHubRelease.h diff --git a/launcher/updater/windows/WindowsUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp similarity index 100% rename from launcher/updater/windows/WindowsUpdater.cpp rename to launcher/updater/prismupdater/PrismUpdater.cpp diff --git a/launcher/updater/windows/WindowsUpdater.h b/launcher/updater/prismupdater/PrismUpdater.h similarity index 100% rename from launcher/updater/windows/WindowsUpdater.h rename to launcher/updater/prismupdater/PrismUpdater.h diff --git a/launcher/updater/windows/SelectReleaseDialog.ui b/launcher/updater/prismupdater/SelectReleaseDialog.ui similarity index 100% rename from launcher/updater/windows/SelectReleaseDialog.ui rename to launcher/updater/prismupdater/SelectReleaseDialog.ui diff --git a/launcher/updater/windows/UpdaterDialogs.cpp b/launcher/updater/prismupdater/UpdaterDialogs.cpp similarity index 100% rename from launcher/updater/windows/UpdaterDialogs.cpp rename to launcher/updater/prismupdater/UpdaterDialogs.cpp diff --git a/launcher/updater/windows/UpdaterDialogs.h b/launcher/updater/prismupdater/UpdaterDialogs.h similarity index 100% rename from launcher/updater/windows/UpdaterDialogs.h rename to launcher/updater/prismupdater/UpdaterDialogs.h diff --git a/launcher/updater/windows/windows_main.cpp b/launcher/updater/prismupdater/windows_main.cpp similarity index 100% rename from launcher/updater/windows/windows_main.cpp rename to launcher/updater/prismupdater/windows_main.cpp From f619a04fe7ce1e7649152e383f0415f59de55d5d Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Thu, 1 Jun 2023 14:55:20 -0700 Subject: [PATCH 039/268] refactor: rename app class Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/CMakeLists.txt | 28 +++++------ .../filelink/{main.cpp => filelink_main.cpp} | 0 .../updater/prismupdater/PrismUpdater.cpp | 48 +++++++++---------- launcher/updater/prismupdater/PrismUpdater.h | 8 ++-- .../updater/prismupdater/UpdaterDialogs.h | 2 +- .../{windows_main.cpp => updater_main.cpp} | 12 ++--- 6 files changed, 49 insertions(+), 49 deletions(-) rename launcher/filelink/{main.cpp => filelink_main.cpp} (100%) rename launcher/updater/prismupdater/{windows_main.cpp => updater_main.cpp} (80%) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 5a979b8d9..89dea0d11 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -567,12 +567,12 @@ set(LINKEXE_SOURCES DesktopServices.cpp ) -set(WINDOWSUPDATEREXE_SOURCES - updater/windows/WindowsUpdater.h - updater/windows/WindowsUpdater.cpp - updater/windows/UpdaterDialogs.h - updater/windows/UpdaterDialogs.cpp - updater/windows/GitHubRelease.h +set(PRISMUPDATER_SOURCES + updater/prismupdater/PrismUpdater.h + updater/prismupdater/PrismUpdater.cpp + updater/prismupdater/UpdaterDialogs.h + updater/prismupdater/UpdaterDialogs.cpp + updater/prismupdater/GitHubRelease.h Json.h Json.cpp FileSystem.h @@ -1094,8 +1094,8 @@ qt_add_resources(LAUNCHER_RESOURCES ../${Launcher_Branding_LogoQRC} ) -qt_wrap_ui(WINDOWSUPDATER_UI - updater/windows/SelectReleaseDialog.ui +qt_wrap_ui(PRISMUPDATER_UI + updater/prismupdater/SelectReleaseDialog.ui ) ######## Windows resource files ######## @@ -1183,9 +1183,9 @@ install(TARGETS ${Launcher_Name} if(WIN32 OR (DEFINED Launcher_BUILD_UPDATER AND Launcher_BUILD_UPDATER) ) # Updater - add_library(windows_updater_logic STATIC ${WINDOWSUPDATEREXE_SOURCES} ${WINDOWSUPDATER_UI}) - target_include_directories(windows_updater_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) - target_link_libraries(windows_updater_logic + add_library(prism_updater_logic STATIC ${PRISMUPDATER_SOURCES} ${PRISMUPDATER_UI}) + target_include_directories(prism_updater_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + target_link_libraries(prism_updater_logic systeminfo BuildConfig ghcFilesystem::ghc_filesystem @@ -1196,8 +1196,8 @@ if(WIN32 OR (DEFINED Launcher_BUILD_UPDATER AND Launcher_BUILD_UPDATER) ) cmark::cmark ) - add_executable("${Launcher_Name}_updater" WIN32 updater/windows/windows_main.cpp) - target_link_libraries("${Launcher_Name}_updater" windows_updater_logic) + add_executable("${Launcher_Name}_updater" WIN32 updater/prismupdater/updater_main.cpp) + target_link_libraries("${Launcher_Name}_updater" prism_updater_logic) if(DEFINED Launcher_APP_BINARY_NAME) set_target_properties("${Launcher_Name}_updater" PROPERTIES OUTPUT_NAME "${Launcher_APP_BINARY_NAME}_updater") @@ -1229,7 +1229,7 @@ if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER)) ${Launcher_QT_LIBS} ) - add_executable("${Launcher_Name}_filelink" WIN32 filelink/main.cpp) + add_executable("${Launcher_Name}_filelink" WIN32 filelink/filelink_main.cpp) target_sources("${Launcher_Name}_filelink" PRIVATE filelink/filelink.exe.manifest) diff --git a/launcher/filelink/main.cpp b/launcher/filelink/filelink_main.cpp similarity index 100% rename from launcher/filelink/main.cpp rename to launcher/filelink/filelink_main.cpp diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index f0ea9998c..923b891f9 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -20,7 +20,7 @@ * */ -#include "WindowsUpdater.h" +#include "PrismUpdater.h" #include "BuildConfig.h" #include @@ -68,7 +68,7 @@ namespace fs = ghc::filesystem; #include -#include "UpdaterDialogs.h" +#include "updater/prismupdater/UpdaterDialogs.h" #include "FileSystem.h" #include "Json.h" @@ -83,7 +83,7 @@ void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QSt QString out = qFormatLogMessage(type, context, msg); out += QChar::LineFeed; - WindowsUpdaterApp* app = static_cast(QCoreApplication::instance()); + PrismUpdaterApp* app = static_cast(QCoreApplication::instance()); app->logFile->write(out.toUtf8()); app->logFile->flush(); if (app->logToConsole) { @@ -92,7 +92,7 @@ void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QSt } } -WindowsUpdaterApp::WindowsUpdaterApp(int& argc, char** argv) : QApplication(argc, argv) +PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, argv) { #if defined Q_OS_WIN32 // attach the parent console @@ -313,7 +313,7 @@ WindowsUpdaterApp::WindowsUpdaterApp(int& argc, char** argv) : QApplication(argc loadReleaseList(); } -WindowsUpdaterApp::~WindowsUpdaterApp() +PrismUpdaterApp::~PrismUpdaterApp() { qDebug() << "updater shutting down"; // Shut down logger by setting the logger function to nothing @@ -334,21 +334,21 @@ WindowsUpdaterApp::~WindowsUpdaterApp() m_reply->deleteLater(); } -void WindowsUpdaterApp::fail(const QString& reason) +void PrismUpdaterApp::fail(const QString& reason) { qCritical() << qPrintable(reason); m_status = Failed; exit(1); } -void WindowsUpdaterApp::abort(const QString& reason) +void PrismUpdaterApp::abort(const QString& reason) { qCritical() << qPrintable(reason); m_status = Aborted; exit(2); } -void WindowsUpdaterApp::showFatalErrorMessage(const QString& title, const QString& content) +void PrismUpdaterApp::showFatalErrorMessage(const QString& title, const QString& content) { m_status = Failed; auto msgBox = new QMessageBox(); @@ -362,7 +362,7 @@ void WindowsUpdaterApp::showFatalErrorMessage(const QString& title, const QStrin exit(1); } -void WindowsUpdaterApp::run() +void PrismUpdaterApp::run() { qDebug() << "found" << m_releases.length() << "releases on github"; qDebug() << "loading exe at " << m_prismExecutable; @@ -424,14 +424,14 @@ void WindowsUpdaterApp::run() exit(0); } -void WindowsUpdaterApp::printReleases() +void PrismUpdaterApp::printReleases() { for (auto release : m_releases) { std::cout << release.name.toStdString() << " Version: " << release.tag_name.toStdString() << std::endl; } } -QList WindowsUpdaterApp::nonDraftReleases() +QList PrismUpdaterApp::nonDraftReleases() { QList nonDraft; for (auto rls : m_releases) { @@ -441,7 +441,7 @@ QList WindowsUpdaterApp::nonDraftReleases() return nonDraft; } -QList WindowsUpdaterApp::newerReleases() +QList PrismUpdaterApp::newerReleases() { QList newer; for (auto rls : nonDraftReleases()) { @@ -451,7 +451,7 @@ QList WindowsUpdaterApp::newerReleases() return newer; } -GitHubRelease WindowsUpdaterApp::selectRelease() +GitHubRelease PrismUpdaterApp::selectRelease() { QList releases; @@ -475,12 +475,12 @@ GitHubRelease WindowsUpdaterApp::selectRelease() return release; } -void WindowsUpdaterApp::performUpdate(const GitHubRelease& release) +void PrismUpdaterApp::performUpdate(const GitHubRelease& release) { qDebug() << "Updating to" << release.tag_name; } -void WindowsUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) +void PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) { QProcess proc = QProcess(); proc.start(exe_path, { "-v" }); @@ -509,7 +509,7 @@ void WindowsUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) m_prismGitCommit = lines.takeFirst().simplified(); } -void WindowsUpdaterApp::loadReleaseList() +void PrismUpdaterApp::loadReleaseList() { auto github_repo = m_prismRepoUrl; if (github_repo.host() != "github.com") @@ -526,7 +526,7 @@ void WindowsUpdaterApp::loadReleaseList() downloadReleasePage(api_url, 1); } -void WindowsUpdaterApp::downloadReleasePage(const QString& api_url, int page) +void PrismUpdaterApp::downloadReleasePage(const QString& api_url, int page) { int per_page = 30; auto page_url = QString("%1?per_page=%2&page=%3").arg(api_url).arg(QString::number(per_page)).arg(QString::number(page)); @@ -549,18 +549,18 @@ void WindowsUpdaterApp::downloadReleasePage(const QString& api_url, int page) } }); #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 - connect(rep, &QNetworkReply::errorOccurred, this, &WindowsUpdaterApp::downloadError); + connect(rep, &QNetworkReply::errorOccurred, this, &PrismUpdaterApp::downloadError); #else connect(rep, QOverload::of(&QNetworkReply::error), this, &WindowsUpdaterApp::downloadError); #endif - connect(rep, &QNetworkReply::sslErrors, this, &WindowsUpdaterApp::sslErrors); + connect(rep, &QNetworkReply::sslErrors, this, &PrismUpdaterApp::sslErrors); connect(rep, &QNetworkReply::readyRead, this, [this, responce]() { auto data = m_reply->readAll(); responce->append(data); }); } -int WindowsUpdaterApp::parseReleasePage(const QByteArray* responce) +int PrismUpdaterApp::parseReleasePage(const QByteArray* responce) { if (responce->isEmpty()) // empty page return 0; @@ -607,7 +607,7 @@ int WindowsUpdaterApp::parseReleasePage(const QByteArray* responce) return num_releases; } -GitHubRelease WindowsUpdaterApp::getLatestRelease() +GitHubRelease PrismUpdaterApp::getLatestRelease() { GitHubRelease latest; for (auto release : m_releases) { @@ -618,13 +618,13 @@ GitHubRelease WindowsUpdaterApp::getLatestRelease() return latest; } -bool WindowsUpdaterApp::needUpdate(const GitHubRelease& release) +bool PrismUpdaterApp::needUpdate(const GitHubRelease& release) { auto current_ver = Version(QString("%1.%2").arg(QString::number(m_prismVersionMajor)).arg(QString::number(m_prismVersionMinor))); return current_ver < release.version; } -void WindowsUpdaterApp::downloadError(QNetworkReply::NetworkError error) +void PrismUpdaterApp::downloadError(QNetworkReply::NetworkError error) { if (error == QNetworkReply::OperationCanceledError) { abort(QString("Aborted %1").arg(m_reply->url().toString())); @@ -633,7 +633,7 @@ void WindowsUpdaterApp::downloadError(QNetworkReply::NetworkError error) } } -void WindowsUpdaterApp::sslErrors(const QList& errors) +void PrismUpdaterApp::sslErrors(const QList& errors) { int i = 1; QString err_msg; diff --git a/launcher/updater/prismupdater/PrismUpdater.h b/launcher/updater/prismupdater/PrismUpdater.h index b79a86271..da6b4bf71 100644 --- a/launcher/updater/prismupdater/PrismUpdater.h +++ b/launcher/updater/prismupdater/PrismUpdater.h @@ -39,15 +39,15 @@ #define PRISM_EXTERNAL_EXE #include "FileSystem.h" -#include "updater/windows/GitHubRelease.h" +#include "updater/prismupdater/GitHubRelease.h" -class WindowsUpdaterApp : public QApplication { +class PrismUpdaterApp : public QApplication { // friends for the purpose of limiting access to deprecated stuff Q_OBJECT public: enum Status { Starting, Failed, Succeeded, Initialized, Aborted }; - WindowsUpdaterApp(int& argc, char** argv); - virtual ~WindowsUpdaterApp(); + PrismUpdaterApp(int& argc, char** argv); + virtual ~PrismUpdaterApp(); void loadReleaseList(); void run(); Status status() const { return m_status; } diff --git a/launcher/updater/prismupdater/UpdaterDialogs.h b/launcher/updater/prismupdater/UpdaterDialogs.h index 7481e499a..1449befbd 100644 --- a/launcher/updater/prismupdater/UpdaterDialogs.h +++ b/launcher/updater/prismupdater/UpdaterDialogs.h @@ -4,7 +4,7 @@ #include #include "Version.h" -#include "updater/windows/GitHubRelease.h" +#include "updater/prismupdater/GitHubRelease.h" namespace Ui { class SelectReleaseDialog; diff --git a/launcher/updater/prismupdater/windows_main.cpp b/launcher/updater/prismupdater/updater_main.cpp similarity index 80% rename from launcher/updater/prismupdater/windows_main.cpp rename to launcher/updater/prismupdater/updater_main.cpp index 31d7646b5..6bc7dd208 100644 --- a/launcher/updater/prismupdater/windows_main.cpp +++ b/launcher/updater/prismupdater/updater_main.cpp @@ -22,20 +22,20 @@ -#include "updater/windows/WindowsUpdater.h" +#include "updater/prismupdater/PrismUpdater.h" int main(int argc, char* argv[]) { - WindowsUpdaterApp wUpApp(argc, argv); + PrismUpdaterApp wUpApp(argc, argv); switch(wUpApp.status()) { - case WindowsUpdaterApp::Starting: - case WindowsUpdaterApp::Initialized: + case PrismUpdaterApp::Starting: + case PrismUpdaterApp::Initialized: { return wUpApp.exec(); } - case WindowsUpdaterApp::Failed: + case PrismUpdaterApp::Failed: return 1; - case WindowsUpdaterApp::Succeeded: + case PrismUpdaterApp::Succeeded: return 0; default: return -1; From 74d4a98864ba2deb2449e7e3ec7632054ece4823 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Thu, 1 Jun 2023 16:39:04 -0700 Subject: [PATCH 040/268] refactor: split out setting api headers for downloads Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/CMakeLists.txt | 4 ++ launcher/InstanceImportTask.cpp | 4 +- launcher/ResourceDownloadTask.cpp | 4 +- launcher/meta/BaseEntity.cpp | 4 +- launcher/minecraft/AssetsUtils.cpp | 4 +- launcher/minecraft/Library.cpp | 6 +-- launcher/minecraft/update/AssetUpdateTask.cpp | 4 +- .../minecraft/update/FMLLibrariesTask.cpp | 4 +- .../atlauncher/ATLPackInstallTask.cpp | 12 +++-- .../modplatform/flame/FileResolvingTask.cpp | 5 +- launcher/modplatform/flame/FlameAPI.cpp | 7 +-- .../modplatform/flame/FlameCheckUpdate.cpp | 6 ++- .../flame/FlameInstanceCreationTask.cpp | 4 +- .../helpers/NetworkResourceAPI.cpp | 8 +-- .../modplatform/legacy_ftb/PackFetchTask.cpp | 8 +-- .../legacy_ftb/PackInstallTask.cpp | 4 +- launcher/modplatform/modrinth/ModrinthAPI.cpp | 5 +- .../modrinth/ModrinthInstanceCreationTask.cpp | 5 +- .../technic/SingleZipPackInstallTask.cpp | 4 +- .../technic/SolderPackInstallTask.cpp | 5 +- launcher/net/ApiDownload.h | 39 ++++++++++++++ launcher/net/ApiHeaderProxy.h | 51 +++++++++++++++++++ launcher/net/Download.cpp | 12 ++--- launcher/net/HeaderProxy.h | 49 ++++++++++++++++++ launcher/net/NetAction.h | 9 +++- launcher/net/RawHeaderProxy.h | 44 ++++++++++++++++ .../ui/pages/instance/ManagedPackPage.cpp | 6 ++- .../ui/pages/modplatform/ResourceModel.cpp | 4 +- .../modplatform/atlauncher/AtlListModel.cpp | 6 ++- .../atlauncher/AtlOptionalModDialog.cpp | 4 +- .../ui/pages/modplatform/flame/FlameModel.cpp | 6 ++- .../ui/pages/modplatform/flame/FlamePage.cpp | 4 +- .../modplatform/legacy_ftb/ListModel.cpp | 3 +- .../modplatform/modrinth/ModrinthModel.cpp | 6 ++- .../modplatform/modrinth/ModrinthPage.cpp | 6 ++- .../modplatform/technic/TechnicModel.cpp | 6 ++- .../pages/modplatform/technic/TechnicPage.cpp | 6 ++- .../ui/widgets/VariableSizedImageObject.cpp | 3 +- 38 files changed, 303 insertions(+), 68 deletions(-) create mode 100644 launcher/net/ApiDownload.h create mode 100644 launcher/net/ApiHeaderProxy.h create mode 100644 launcher/net/HeaderProxy.h create mode 100644 launcher/net/RawHeaderProxy.h diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 273b5449a..bce885ca8 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -136,6 +136,10 @@ set(NET_SOURCES net/Validator.h net/Upload.cpp net/Upload.h + net/HeaderProxy.h + net/RawHeaderProxy.h + net/ApiHeaderProxy.h + net/ApiDownload.h ) # Game launch logic diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp index 352848f02..7332e917f 100644 --- a/launcher/InstanceImportTask.cpp +++ b/launcher/InstanceImportTask.cpp @@ -51,6 +51,8 @@ #include "settings/INISettingsObject.h" +#include "net/ApiDownload.h" + #include #include @@ -95,7 +97,7 @@ void InstanceImportTask::executeTask() m_archivePath = entry->getFullPath(); m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network())); - m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry)); + m_filesNetJob->addNetAction(Net::ApiDownload::makeCached(m_sourceUrl, entry)); connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded); connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged); diff --git a/launcher/ResourceDownloadTask.cpp b/launcher/ResourceDownloadTask.cpp index 61b918aaf..03e103f13 100644 --- a/launcher/ResourceDownloadTask.cpp +++ b/launcher/ResourceDownloadTask.cpp @@ -24,6 +24,8 @@ #include "minecraft/mod/ModFolderModel.h" #include "minecraft/mod/ResourceFolderModel.h" +#include "net/ApiDownload.h" + ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack pack, ModPlatform::IndexedVersion version, const std::shared_ptr packs, @@ -50,7 +52,7 @@ ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack pack, } } - m_filesNetJob->addNetAction(Net::Download::makeFile(m_pack_version.downloadUrl, dir.absoluteFilePath(getFilename()))); + m_filesNetJob->addNetAction(Net::ApiDownload::makeFile(m_pack_version.downloadUrl, dir.absoluteFilePath(getFilename()))); connect(m_filesNetJob.get(), &NetJob::succeeded, this, &ResourceDownloadTask::downloadSucceeded); connect(m_filesNetJob.get(), &NetJob::progress, this, &ResourceDownloadTask::downloadProgressChanged); connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &ResourceDownloadTask::propogateStepProgress); diff --git a/launcher/meta/BaseEntity.cpp b/launcher/meta/BaseEntity.cpp index 97815eba8..fd810a91b 100644 --- a/launcher/meta/BaseEntity.cpp +++ b/launcher/meta/BaseEntity.cpp @@ -15,7 +15,7 @@ #include "BaseEntity.h" -#include "net/Download.h" +#include "net/ApiDownload.h" #include "net/HttpMetaCache.h" #include "net/NetJob.h" #include "Json.h" @@ -130,7 +130,7 @@ void Meta::BaseEntity::load(Net::Mode loadType) auto url = this->url(); auto entry = APPLICATION->metacache()->resolveEntry("meta", localFilename()); entry->setStale(true); - auto dl = Net::Download::makeCached(url, entry); + auto dl = Net::ApiDownload::makeCached(url, entry); /* * The validator parses the file and loads it into the object. * If that fails, the file is not written to storage. diff --git a/launcher/minecraft/AssetsUtils.cpp b/launcher/minecraft/AssetsUtils.cpp index 16fdfdb1c..65ad6da69 100644 --- a/launcher/minecraft/AssetsUtils.cpp +++ b/launcher/minecraft/AssetsUtils.cpp @@ -45,7 +45,7 @@ #include "AssetsUtils.h" #include "FileSystem.h" -#include "net/Download.h" +#include "net/ApiDownload.h" #include "net/ChecksumValidator.h" #include "BuildConfig.h" @@ -311,7 +311,7 @@ NetAction::Ptr AssetObject::getDownloadAction() QFileInfo objectFile(getLocalPath()); if ((!objectFile.isFile()) || (objectFile.size() != size)) { - auto objectDL = Net::Download::makeFile(getUrl(), objectFile.filePath()); + auto objectDL = Net::ApiDownload::makeFile(getUrl(), objectFile.filePath()); if(hash.size()) { auto rawHash = QByteArray::fromHex(hash.toLatin1()); diff --git a/launcher/minecraft/Library.cpp b/launcher/minecraft/Library.cpp index cb2b5254d..e0318ef22 100644 --- a/launcher/minecraft/Library.cpp +++ b/launcher/minecraft/Library.cpp @@ -36,7 +36,7 @@ #include "Library.h" #include "MinecraftInstance.h" -#include +#include #include #include #include @@ -129,14 +129,14 @@ QList Library::getDownloads( if(sha1.size()) { auto rawSha1 = QByteArray::fromHex(sha1.toLatin1()); - auto dl = Net::Download::makeCached(url, entry, options); + auto dl = Net::ApiDownload::makeCached(url, entry, options); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1)); qDebug() << "Checksummed Download for:" << rawName().serialize() << "storage:" << storage << "url:" << url; out.append(dl); } else { - out.append(Net::Download::makeCached(url, entry, options)); + out.append(Net::ApiDownload::makeCached(url, entry, options)); qDebug() << "Download for:" << rawName().serialize() << "storage:" << storage << "url:" << url; } return true; diff --git a/launcher/minecraft/update/AssetUpdateTask.cpp b/launcher/minecraft/update/AssetUpdateTask.cpp index 31fd5eb11..bafbe75f1 100644 --- a/launcher/minecraft/update/AssetUpdateTask.cpp +++ b/launcher/minecraft/update/AssetUpdateTask.cpp @@ -7,6 +7,8 @@ #include "Application.h" +#include "net/ApiDownload.h" + AssetUpdateTask::AssetUpdateTask(MinecraftInstance * inst) { m_inst = inst; @@ -34,7 +36,7 @@ void AssetUpdateTask::executeTask() entry->setStale(true); auto hexSha1 = assets->sha1.toLatin1(); qDebug() << "Asset index SHA1:" << hexSha1; - auto dl = Net::Download::makeCached(indexUrl, entry); + auto dl = Net::ApiDownload::makeCached(indexUrl, entry); auto rawSha1 = QByteArray::fromHex(assets->sha1.toLatin1()); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1)); job->addNetAction(dl); diff --git a/launcher/minecraft/update/FMLLibrariesTask.cpp b/launcher/minecraft/update/FMLLibrariesTask.cpp index 75e5c5720..3651f4f40 100644 --- a/launcher/minecraft/update/FMLLibrariesTask.cpp +++ b/launcher/minecraft/update/FMLLibrariesTask.cpp @@ -8,6 +8,8 @@ #include "BuildConfig.h" #include "Application.h" +#include "net/ApiDownload.h" + FMLLibrariesTask::FMLLibrariesTask(MinecraftInstance * inst) { m_inst = inst; @@ -68,7 +70,7 @@ void FMLLibrariesTask::executeTask() { auto entry = metacache->resolveEntry("fmllibs", lib.filename); QString urlString = BuildConfig.FMLLIBS_BASE_URL + lib.filename; - dljob->addNetAction(Net::Download::makeCached(QUrl(urlString), entry, options)); + dljob->addNetAction(Net::ApiDownload::makeCached(QUrl(urlString), entry, options)); } connect(dljob.get(), &NetJob::succeeded, this, &FMLLibrariesTask::fmllibsFinished); diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp index 96cea7b7d..966e2665f 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp @@ -53,6 +53,8 @@ #include "meta/Version.h" #include "meta/VersionList.h" +#include "net/ApiDownload.h" + #include "BuildConfig.h" #include "Application.h" @@ -84,7 +86,7 @@ void PackInstallTask::executeTask() NetJob::Ptr netJob{ new NetJob("ATLauncher::VersionFetch", APPLICATION->network()) }; auto searchUrl = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "packs/%1/versions/%2/Configs.json") .arg(m_pack_safe_name).arg(m_version_name); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchUrl), &response)); QObject::connect(netJob.get(), &NetJob::succeeded, this, &PackInstallTask::onDownloadSucceeded); QObject::connect(netJob.get(), &NetJob::failed, this, &PackInstallTask::onDownloadFailed); @@ -658,7 +660,7 @@ void PackInstallTask::installConfigs() auto entry = APPLICATION->metacache()->resolveEntry("ATLauncherPacks", path); entry->setStale(true); - auto dl = Net::Download::makeCached(url, entry); + auto dl = Net::ApiDownload::makeCached(url, entry); if (!m_version.configs.sha1.isEmpty()) { auto rawSha1 = QByteArray::fromHex(m_version.configs.sha1.toLatin1()); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1)); @@ -781,7 +783,7 @@ void PackInstallTask::downloadMods() entry->setStale(true); modsToExtract.insert(entry->getFullPath(), mod); - auto dl = Net::Download::makeCached(url, entry); + auto dl = Net::ApiDownload::makeCached(url, entry); if (!mod.md5.isEmpty()) { auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1()); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5)); @@ -793,7 +795,7 @@ void PackInstallTask::downloadMods() entry->setStale(true); modsToDecomp.insert(entry->getFullPath(), mod); - auto dl = Net::Download::makeCached(url, entry); + auto dl = Net::ApiDownload::makeCached(url, entry); if (!mod.md5.isEmpty()) { auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1()); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5)); @@ -807,7 +809,7 @@ void PackInstallTask::downloadMods() auto entry = APPLICATION->metacache()->resolveEntry("ATLauncherPacks", cacheName); entry->setStale(true); - auto dl = Net::Download::makeCached(url, entry); + auto dl = Net::ApiDownload::makeCached(url, entry); if (!mod.md5.isEmpty()) { auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1()); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5)); diff --git a/launcher/modplatform/flame/FileResolvingTask.cpp b/launcher/modplatform/flame/FileResolvingTask.cpp index 83db642e7..011c4cdf2 100644 --- a/launcher/modplatform/flame/FileResolvingTask.cpp +++ b/launcher/modplatform/flame/FileResolvingTask.cpp @@ -2,6 +2,7 @@ #include "Json.h" #include "net/Upload.h" +#include "net/ApiDownload.h" #include "modplatform/modrinth/ModrinthPackIndex.h" @@ -94,7 +95,7 @@ void Flame::FileResolvingTask::netJobFinished() if(!hash.isEmpty()) { auto url = QString("https://api.modrinth.com/v2/version_file/%1?algorithm=sha1").arg(hash); auto output = std::make_shared(); - auto dl = Net::Download::makeByteArray(QUrl(url), output.get()); + auto dl = Net::ApiDownload::makeByteArray(QUrl(url), output.get()); QObject::connect(dl.get(), &Net::Download::succeeded, [&out]() { out.resolved = true; }); @@ -169,7 +170,7 @@ void Flame::FileResolvingTask::modrinthCheckFinished() { auto projectId = mod->projectId; auto output = std::make_shared(); auto url = QString("https://api.curseforge.com/v1/mods/%1").arg(projectId); - auto dl = Net::Download::makeByteArray(url, output.get()); + auto dl = Net::ApiDownload::makeByteArray(url, output.get()); qDebug() << "Fetching url slug for file:" << mod->fileName; QObject::connect(dl.get(), &Net::Download::succeeded, [block, index, output]() { auto mod = block->at(index); // use the shared_ptr so it is captured and only freed when we are done diff --git a/launcher/modplatform/flame/FlameAPI.cpp b/launcher/modplatform/flame/FlameAPI.cpp index 92590a084..49ab06db2 100644 --- a/launcher/modplatform/flame/FlameAPI.cpp +++ b/launcher/modplatform/flame/FlameAPI.cpp @@ -9,6 +9,7 @@ #include "BuildConfig.h" #include "Json.h" #include "net/NetJob.h" +#include "net/ApiDownload.h" #include "net/Upload.h" Task::Ptr FlameAPI::matchFingerprints(const QList& fingerprints, QByteArray* response) @@ -40,7 +41,7 @@ auto FlameAPI::getModFileChangelog(int modId, int fileId) -> QString auto netJob = makeShared(QString("Flame::FileChangelog"), APPLICATION->network()); auto response = std::make_shared(); - netJob->addNetAction(Net::Download::makeByteArray( + netJob->addNetAction(Net::ApiDownload::makeByteArray( QString("https://api.curseforge.com/v1/mods/%1/files/%2/changelog") .arg(QString::fromStdString(std::to_string(modId)), QString::fromStdString(std::to_string(fileId))), response.get())); @@ -75,7 +76,7 @@ auto FlameAPI::getModDescription(int modId) -> QString auto netJob = makeShared(QString("Flame::ModDescription"), APPLICATION->network()); auto response = std::make_shared(); - netJob->addNetAction(Net::Download::makeByteArray( + netJob->addNetAction(Net::ApiDownload::makeByteArray( QString("https://api.curseforge.com/v1/mods/%1/description").arg(QString::number(modId)), response.get())); QObject::connect(netJob.get(), &NetJob::succeeded, [&netJob, response, &description] { @@ -115,7 +116,7 @@ auto FlameAPI::getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::Indexe auto response = std::make_shared(); ModPlatform::IndexedVersion ver; - netJob->addNetAction(Net::Download::makeByteArray(versions_url, response.get())); + netJob->addNetAction(Net::ApiDownload::makeByteArray(versions_url, response.get())); QObject::connect(netJob.get(), &NetJob::succeeded, [response, args, &ver] { QJsonParseError parse_error{}; diff --git a/launcher/modplatform/flame/FlameCheckUpdate.cpp b/launcher/modplatform/flame/FlameCheckUpdate.cpp index 06a895027..5736f16e0 100644 --- a/launcher/modplatform/flame/FlameCheckUpdate.cpp +++ b/launcher/modplatform/flame/FlameCheckUpdate.cpp @@ -12,6 +12,8 @@ #include "minecraft/mod/ModFolderModel.h" #include "minecraft/mod/ResourceFolderModel.h" +#include "net/ApiDownload.h" + static FlameAPI api; bool FlameCheckUpdate::abort() @@ -32,7 +34,7 @@ ModPlatform::IndexedPack getProjectInfo(ModPlatform::IndexedVersion& ver_info) auto response = new QByteArray(); auto url = QString("https://api.curseforge.com/v1/mods/%1").arg(ver_info.addonId.toString()); - auto dl = Net::Download::makeByteArray(url, response); + auto dl = Net::ApiDownload::makeByteArray(url, response); get_project_job->addNetAction(dl); QObject::connect(get_project_job, &NetJob::succeeded, [response, &pack]() { @@ -76,7 +78,7 @@ ModPlatform::IndexedVersion getFileInfo(int addonId, int fileId) auto response = new QByteArray(); auto url = QString("https://api.curseforge.com/v1/mods/%1/files/%2").arg(QString::number(addonId), QString::number(fileId)); - auto dl = Net::Download::makeByteArray(url, response); + auto dl = Net::ApiDownload::makeByteArray(url, response); get_file_info_job->addNetAction(dl); QObject::connect(get_file_info_job, &NetJob::succeeded, [response, &ver]() { diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp index dae93d1c6..099e3ae4f 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp @@ -60,6 +60,8 @@ #include "minecraft/World.h" #include "minecraft/mod/tasks/LocalResourceParse.h" +#include "net/ApiDownload.h" + const static QMap forgemap = { { "1.2.5", "3.4.9.171" }, { "1.4.2", "6.0.1.355" }, @@ -473,7 +475,7 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop) case Flame::File::Type::Mod: { if (!result.url.isEmpty()) { qDebug() << "Will download" << result.url << "to" << path; - auto dl = Net::Download::makeFile(result.url, path); + auto dl = Net::ApiDownload::makeFile(result.url, path); m_files_job->addNetAction(dl); } break; diff --git a/launcher/modplatform/helpers/NetworkResourceAPI.cpp b/launcher/modplatform/helpers/NetworkResourceAPI.cpp index a3c592fdc..0ff8ac8a4 100644 --- a/launcher/modplatform/helpers/NetworkResourceAPI.cpp +++ b/launcher/modplatform/helpers/NetworkResourceAPI.cpp @@ -9,6 +9,8 @@ #include "modplatform/ModIndex.h" +#include "net/ApiDownload.h" + Task::Ptr NetworkResourceAPI::searchProjects(SearchArgs&& args, SearchCallbacks&& callbacks) const { auto search_url_optional = getSearchURL(args); @@ -22,7 +24,7 @@ Task::Ptr NetworkResourceAPI::searchProjects(SearchArgs&& args, SearchCallbacks& auto response = new QByteArray(); auto netJob = makeShared(QString("%1::Search").arg(debugName()), APPLICATION->network()); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(search_url), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(search_url), response)); QObject::connect(netJob.get(), &NetJob::succeeded, [this, response, callbacks]{ QJsonParseError parse_error{}; @@ -90,7 +92,7 @@ Task::Ptr NetworkResourceAPI::getProjectVersions(VersionSearchArgs&& args, Versi auto netJob = makeShared(QString("%1::Versions").arg(args.pack.name), APPLICATION->network()); auto response = new QByteArray(); - netJob->addNetAction(Net::Download::makeByteArray(versions_url, response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(versions_url, response)); QObject::connect(netJob.get(), &NetJob::succeeded, [response, callbacks, args] { QJsonParseError parse_error{}; @@ -122,7 +124,7 @@ Task::Ptr NetworkResourceAPI::getProject(QString addonId, QByteArray* response) auto netJob = makeShared(QString("%1::GetProject").arg(addonId), APPLICATION->network()); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(project_url), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(project_url), response)); QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; diff --git a/launcher/modplatform/legacy_ftb/PackFetchTask.cpp b/launcher/modplatform/legacy_ftb/PackFetchTask.cpp index e8768c5cd..320ddd3e8 100644 --- a/launcher/modplatform/legacy_ftb/PackFetchTask.cpp +++ b/launcher/modplatform/legacy_ftb/PackFetchTask.cpp @@ -40,6 +40,8 @@ #include "BuildConfig.h" #include "Application.h" +#include "net/ApiDownload.h" + namespace LegacyFTB { void PackFetchTask::fetch() @@ -51,11 +53,11 @@ void PackFetchTask::fetch() QUrl publicPacksUrl = QUrl(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/modpacks.xml"); qDebug() << "Downloading public version info from" << publicPacksUrl.toString(); - jobPtr->addNetAction(Net::Download::makeByteArray(publicPacksUrl, &publicModpacksXmlFileData)); + jobPtr->addNetAction(Net::ApiDownload::makeByteArray(publicPacksUrl, &publicModpacksXmlFileData)); QUrl thirdPartyUrl = QUrl(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/thirdparty.xml"); qDebug() << "Downloading thirdparty version info from" << thirdPartyUrl.toString(); - jobPtr->addNetAction(Net::Download::makeByteArray(thirdPartyUrl, &thirdPartyModpacksXmlFileData)); + jobPtr->addNetAction(Net::ApiDownload::makeByteArray(thirdPartyUrl, &thirdPartyModpacksXmlFileData)); QObject::connect(jobPtr.get(), &NetJob::succeeded, this, &PackFetchTask::fileDownloadFinished); QObject::connect(jobPtr.get(), &NetJob::failed, this, &PackFetchTask::fileDownloadFailed); @@ -72,7 +74,7 @@ void PackFetchTask::fetchPrivate(const QStringList & toFetch) { QByteArray *data = new QByteArray(); NetJob *job = new NetJob("Fetching private pack", m_network); - job->addNetAction(Net::Download::makeByteArray(privatePackBaseUrl.arg(packCode), data)); + job->addNetAction(Net::ApiDownload::makeByteArray(privatePackBaseUrl.arg(packCode), data)); QObject::connect(job, &NetJob::succeeded, this, [this, job, data, packCode] { diff --git a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp index 36c142acb..6da8fc882 100644 --- a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp +++ b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp @@ -48,6 +48,8 @@ #include "BuildConfig.h" #include "Application.h" +#include "net/ApiDownload.h" + namespace LegacyFTB { PackInstallTask::PackInstallTask(shared_qobject_ptr network, Modpack pack, QString version) @@ -76,7 +78,7 @@ void PackInstallTask::downloadPack() } else { url = QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "modpacks/%1").arg(archivePath); } - netJobContainer->addNetAction(Net::Download::makeFile(url, archivePath)); + netJobContainer->addNetAction(Net::ApiDownload::makeFile(url, archivePath)); connect(netJobContainer.get(), &NetJob::succeeded, this, &PackInstallTask::onDownloadSucceeded); connect(netJobContainer.get(), &NetJob::failed, this, &PackInstallTask::onDownloadFailed); diff --git a/launcher/modplatform/modrinth/ModrinthAPI.cpp b/launcher/modplatform/modrinth/ModrinthAPI.cpp index 29e3d129d..ab77ef119 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.cpp +++ b/launcher/modplatform/modrinth/ModrinthAPI.cpp @@ -8,12 +8,13 @@ #include "Json.h" #include "net/NetJob.h" #include "net/Upload.h" +#include "net/ApiDownload.h" Task::Ptr ModrinthAPI::currentVersion(QString hash, QString hash_format, QByteArray* response) { auto netJob = makeShared(QString("Modrinth::GetCurrentVersion"), APPLICATION->network()); - netJob->addNetAction(Net::Download::makeByteArray( + netJob->addNetAction(Net::ApiDownload::makeByteArray( QString(BuildConfig.MODRINTH_PROD_URL + "/version_file/%1?algorithm=%2").arg(hash, hash_format), response)); QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; }); @@ -111,7 +112,7 @@ Task::Ptr ModrinthAPI::getProjects(QStringList addonIds, QByteArray* response) c auto netJob = makeShared(QString("Modrinth::GetProjects"), APPLICATION->network()); auto searchUrl = getMultipleModInfoURL(addonIds); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchUrl), response)); QObject::connect(netJob.get(), &NetJob::finished, [response, netJob] { delete response; diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp index bb8227aa2..0edd696f7 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp @@ -12,6 +12,7 @@ #include "net/ChecksumValidator.h" #include "net/NetJob.h" +#include "net/ApiDownload.h" #include "settings/INISettingsObject.h" #include "ui/dialogs/CustomMessageBox.h" @@ -238,7 +239,7 @@ bool ModrinthCreationTask::createInstance() } qDebug() << "Will try to download" << file.downloads.front() << "to" << file_path; - auto dl = Net::Download::makeFile(file.downloads.dequeue(), file_path); + auto dl = Net::ApiDownload::makeFile(file.downloads.dequeue(), file_path); dl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash)); m_files_job->addNetAction(dl); @@ -247,7 +248,7 @@ bool ModrinthCreationTask::createInstance() // MultipleOptionsTask's , once those exist :) auto param = dl.toWeakRef(); connect(dl.get(), &NetAction::failed, [this, &file, file_path, param] { - auto ndl = Net::Download::makeFile(file.downloads.dequeue(), file_path); + auto ndl = Net::ApiDownload::makeFile(file.downloads.dequeue(), file_path); ndl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash)); m_files_job->addNetAction(ndl); if (auto shared = param.lock()) shared->succeeded(); diff --git a/launcher/modplatform/technic/SingleZipPackInstallTask.cpp b/launcher/modplatform/technic/SingleZipPackInstallTask.cpp index f07ca24af..21b0defc0 100644 --- a/launcher/modplatform/technic/SingleZipPackInstallTask.cpp +++ b/launcher/modplatform/technic/SingleZipPackInstallTask.cpp @@ -23,6 +23,8 @@ #include "Application.h" +#include "net/ApiDownload.h" + Technic::SingleZipPackInstallTask::SingleZipPackInstallTask(const QUrl &sourceUrl, const QString &minecraftVersion) { m_sourceUrl = sourceUrl; @@ -45,7 +47,7 @@ void Technic::SingleZipPackInstallTask::executeTask() auto entry = APPLICATION->metacache()->resolveEntry("general", path); entry->setStale(true); m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network())); - m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry)); + m_filesNetJob->addNetAction(Net::ApiDownload::makeCached(m_sourceUrl, entry)); m_archivePath = entry->getFullPath(); auto job = m_filesNetJob.get(); connect(job, &NetJob::succeeded, this, &Technic::SingleZipPackInstallTask::downloadSucceeded); diff --git a/launcher/modplatform/technic/SolderPackInstallTask.cpp b/launcher/modplatform/technic/SolderPackInstallTask.cpp index c26d6a5a6..0035c8808 100644 --- a/launcher/modplatform/technic/SolderPackInstallTask.cpp +++ b/launcher/modplatform/technic/SolderPackInstallTask.cpp @@ -43,6 +43,7 @@ #include "TechnicPackProcessor.h" #include "SolderPackManifest.h" #include "net/ChecksumValidator.h" +#include "net/ApiDownload.h" Technic::SolderPackInstallTask::SolderPackInstallTask( shared_qobject_ptr network, @@ -72,7 +73,7 @@ void Technic::SolderPackInstallTask::executeTask() m_filesNetJob.reset(new NetJob(tr("Resolving modpack files"), m_network)); auto sourceUrl = QString("%1/modpack/%2/%3").arg(m_solderUrl.toString(), m_pack, m_version); - m_filesNetJob->addNetAction(Net::Download::makeByteArray(sourceUrl, &m_response)); + m_filesNetJob->addNetAction(Net::ApiDownload::makeByteArray(sourceUrl, &m_response)); auto job = m_filesNetJob.get(); connect(job, &NetJob::succeeded, this, &Technic::SolderPackInstallTask::fileListSucceeded); @@ -113,7 +114,7 @@ void Technic::SolderPackInstallTask::fileListSucceeded() for (const auto &mod : build.mods) { auto path = FS::PathCombine(m_outputDir.path(), QString("%1").arg(i)); - auto dl = Net::Download::makeFile(mod.url, path); + auto dl = Net::ApiDownload::makeFile(mod.url, path); if (!mod.md5.isEmpty()) { auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1()); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5)); diff --git a/launcher/net/ApiDownload.h b/launcher/net/ApiDownload.h new file mode 100644 index 000000000..3ffda445a --- /dev/null +++ b/launcher/net/ApiDownload.h @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2022 flowln + * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#pragma once + +#include "ApiHeaderProxy.h" +#include "Download.h" + +namespace Net { + +class ApiDownload : public Download { + public: + ApiDownload() : Download() + { + auto api_headers = new ApiHeaderProxy(); + addHeaderProxy(api_headers); + } + virtual ~ApiDownload() = default; +}; + +} // namespace Net diff --git a/launcher/net/ApiHeaderProxy.h b/launcher/net/ApiHeaderProxy.h new file mode 100644 index 000000000..0da92a745 --- /dev/null +++ b/launcher/net/ApiHeaderProxy.h @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2022 flowln + * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#pragma once + +#include "Application.h" +#include "BuildConfig.h" +#include "net/HeaderProxy.h" + +namespace Net { + +class ApiHeaderProxy : public HeaderProxy { + public: + ApiHeaderProxy() : HeaderProxy(){}; + virtual ~ApiHeaderProxy() = default; + + public: + virtual QList headers(const QNetworkRequest& request) const override + { + QList hdrs; + if (APPLICATION->capabilities() & Application::SupportsFlame && request.url().host() == QUrl(BuildConfig.FLAME_BASE_URL).host()) { + hdrs.append({ "x-api-key", APPLICATION->getFlameAPIKey().toUtf8() }); + } else if (request.url().host() == QUrl(BuildConfig.MODRINTH_PROD_URL).host() || + request.url().host() == QUrl(BuildConfig.MODRINTH_STAGING_URL).host()) { + QString token = APPLICATION->getModrinthAPIToken(); + if (!token.isNull()) + hdrs.append({ "Authorization", token.toUtf8() }); + } + return hdrs; + }; +}; + +} // namespace Net diff --git a/launcher/net/Download.cpp b/launcher/net/Download.cpp index 7f8d3a067..6c2173793 100644 --- a/launcher/net/Download.cpp +++ b/launcher/net/Download.cpp @@ -124,15 +124,11 @@ void Download::executeTask() } request.setHeader(QNetworkRequest::UserAgentHeader, APPLICATION->getUserAgent().toUtf8()); - // TODO remove duplication - if (APPLICATION->capabilities() & Application::SupportsFlame && request.url().host() == QUrl(BuildConfig.FLAME_BASE_URL).host()) { - request.setRawHeader("x-api-key", APPLICATION->getFlameAPIKey().toUtf8()); - } else if (request.url().host() == QUrl(BuildConfig.MODRINTH_PROD_URL).host() || - request.url().host() == QUrl(BuildConfig.MODRINTH_STAGING_URL).host()) { - QString token = APPLICATION->getModrinthAPIToken(); - if (!token.isNull()) - request.setRawHeader("Authorization", token.toUtf8()); + for ( auto header_proxy : m_headerProxies ) { + header_proxy->writeHeaders(request); } + // TODO remove duplication + #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) request.setTransferTimeout(); diff --git a/launcher/net/HeaderProxy.h b/launcher/net/HeaderProxy.h new file mode 100644 index 000000000..e94579e74 --- /dev/null +++ b/launcher/net/HeaderProxy.h @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2022 flowln + * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#pragma once + +#include + +namespace Net { + +struct HeaderPair { + QByteArray headerName; + QByteArray headerValue; +}; + +class HeaderProxy { + public: + HeaderProxy(){}; + virtual ~HeaderProxy(){}; + + public: + virtual QList headers(const QNetworkRequest& request) const = 0; + + public: + void writeHeaders(QNetworkRequest& request) { + for (auto header : headers(request)) { + request.setRawHeader(header.headerName, header.headerValue); + } + }; +}; + +} // namespace Net diff --git a/launcher/net/NetAction.h b/launcher/net/NetAction.h index ab9322c26..acb0672fc 100644 --- a/launcher/net/NetAction.h +++ b/launcher/net/NetAction.h @@ -42,6 +42,8 @@ #include "QObjectPtr.h" #include "tasks/Task.h" +#include "HeaderProxy.h" + class NetAction : public Task { Q_OBJECT protected: @@ -56,13 +58,16 @@ class NetAction : public Task { void setNetwork(shared_qobject_ptr network) { m_network = network; } + void addHeaderProxy(Net::HeaderProxy* proxy) { m_headerProxies.push_back(std::shared_ptr(proxy)); } + protected slots: virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) = 0; virtual void downloadError(QNetworkReply::NetworkError error) = 0; virtual void downloadFinished() = 0; virtual void downloadReadyRead() = 0; - virtual void sslErrors(const QList& errors) { + virtual void sslErrors(const QList& errors) + { int i = 1; for (auto error : errors) { qCritical() << "Network SSL Error #" << i << " : " << error.errorString(); @@ -70,7 +75,6 @@ class NetAction : public Task { qCritical() << "Certificate in question:\n" << cert.toText(); i++; } - }; public slots: @@ -91,4 +95,5 @@ class NetAction : public Task { /// source URL QUrl m_url; + std::vector> m_headerProxies; }; diff --git a/launcher/net/RawHeaderProxy.h b/launcher/net/RawHeaderProxy.h new file mode 100644 index 000000000..ffe6a81a9 --- /dev/null +++ b/launcher/net/RawHeaderProxy.h @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2022 flowln + * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#pragma once + +#include "net/HeaderProxy.h" + +namespace Net { + +class ApiHeaderProxy : public HeaderProxy { + public: + ApiHeaderProxy() : HeaderProxy(){}; + virtual ~ApiHeaderProxy() = default; + + public: + virtual QList headers(const QNetworkRequest&) const override { return m_headers; }; + + void addHeader(const HeaderPair& header) { m_headers.append(header); } + void addHeader(const QByteArray& headerName, const QByteArray& headerValue) { m_headers.append({ headerName, headerValue }); } + void addHeaders(const QList& headers) { m_headers.append(headers); } + + private: + QList m_headers; +}; + +} // namespace Net diff --git a/launcher/ui/pages/instance/ManagedPackPage.cpp b/launcher/ui/pages/instance/ManagedPackPage.cpp index d0701a7ad..68119f06b 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.cpp +++ b/launcher/ui/pages/instance/ManagedPackPage.cpp @@ -23,6 +23,8 @@ #include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/ProgressDialog.h" +#include "net/ApiDownload.h" + /** This is just to override the combo box popup behavior so that the combo box doesn't take the whole screen. * ... thanks Qt. */ @@ -226,7 +228,7 @@ void ModrinthManagedPackPage::parseManagedPack() QString id = m_inst->getManagedPackID(); - m_fetch_job->addNetAction(Net::Download::makeByteArray(QString("%1/project/%2/version").arg(BuildConfig.MODRINTH_PROD_URL, id), response.get())); + m_fetch_job->addNetAction(Net::ApiDownload::makeByteArray(QString("%1/project/%2/version").arg(BuildConfig.MODRINTH_PROD_URL, id), response.get())); QObject::connect(m_fetch_job.get(), &NetJob::succeeded, this, [this, response, id] { QJsonParseError parse_error{}; @@ -369,7 +371,7 @@ void FlameManagedPackPage::parseManagedPack() QString id = m_inst->getManagedPackID(); - m_fetch_job->addNetAction(Net::Download::makeByteArray(QString("%1/mods/%2/files").arg(BuildConfig.FLAME_BASE_URL, id), response.get())); + m_fetch_job->addNetAction(Net::ApiDownload::makeByteArray(QString("%1/mods/%2/files").arg(BuildConfig.FLAME_BASE_URL, id), response.get())); QObject::connect(m_fetch_job.get(), &NetJob::succeeded, this, [this, response, id] { QJsonParseError parse_error{}; diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp index 472aa8515..ed741c881 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.cpp +++ b/launcher/ui/pages/modplatform/ResourceModel.cpp @@ -15,7 +15,7 @@ #include "BuildConfig.h" #include "Json.h" -#include "net/Download.h" +#include "net/ApiDownload.h" #include "net/NetJob.h" #include "modplatform/ModIndex.h" @@ -276,7 +276,7 @@ std::optional ResourceModel::getIcon(QModelIndex& index, const QUrl& url) auto cache_entry = APPLICATION->metacache()->resolveEntry( metaEntryBase(), QString("logos/%1").arg(QString(QCryptographicHash::hash(url.toEncoded(), QCryptographicHash::Algorithm::Sha1).toHex()))); - auto icon_fetch_action = Net::Download::makeCached(url, cache_entry); + auto icon_fetch_action = Net::ApiDownload::makeCached(url, cache_entry); auto full_file_path = cache_entry->getFullPath(); connect(icon_fetch_action.get(), &NetAction::succeeded, this, [=] { diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp index 9ad26f472..e064919eb 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp @@ -20,6 +20,8 @@ #include #include +#include "net/ApiDownload.h" + namespace Atl { ListModel::ListModel(QObject *parent) : QAbstractListModel(parent) @@ -88,7 +90,7 @@ void ListModel::request() auto netJob = makeShared("Atl::Request", APPLICATION->network()); auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/json/packsnew.json"); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(url), &response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(url), &response)); jobPtr = netJob; jobPtr->start(); @@ -184,7 +186,7 @@ void ListModel::requestLogo(QString file, QString url) MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("ATLauncherPacks", QString("logos/%1").arg(file.section(".", 0, 0))); NetJob *job = new NetJob(QString("ATLauncher Icon Download %1").arg(file), APPLICATION->network()); - job->addNetAction(Net::Download::makeCached(QUrl(url), entry)); + job->addNetAction(Net::ApiDownload::makeCached(QUrl(url), entry)); auto fullPath = entry->getFullPath(); QObject::connect(job, &NetJob::succeeded, this, [this, file, fullPath] diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp index cdb4532c8..edc73345f 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp @@ -43,6 +43,8 @@ #include "modplatform/atlauncher/ATLShareCode.h" #include "Application.h" +#include "net/ApiDownload.h" + AtlOptionalModListModel::AtlOptionalModListModel(QWidget* parent, ATLauncher::PackVersion version, QVector mods) : QAbstractListModel(parent) , m_version(version) @@ -152,7 +154,7 @@ Qt::ItemFlags AtlOptionalModListModel::flags(const QModelIndex &index) const { void AtlOptionalModListModel::useShareCode(const QString& code) { m_jobPtr.reset(new NetJob("Atl::Request", APPLICATION->network())); auto url = QString(BuildConfig.ATL_API_BASE_URL + "share-codes/" + code); - m_jobPtr->addNetAction(Net::Download::makeByteArray(QUrl(url), &m_response)); + m_jobPtr->addNetAction(Net::ApiDownload::makeByteArray(QUrl(url), &m_response)); connect(m_jobPtr.get(), &NetJob::succeeded, this, &AtlOptionalModListModel::shareCodeSuccess); diff --git a/launcher/ui/pages/modplatform/flame/FlameModel.cpp b/launcher/ui/pages/modplatform/flame/FlameModel.cpp index 5961ea026..0205552fb 100644 --- a/launcher/ui/pages/modplatform/flame/FlameModel.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameModel.cpp @@ -3,6 +3,8 @@ #include "Application.h" #include "ui/widgets/ProjectItem.h" +#include "net/ApiDownload.h" + #include #include @@ -102,7 +104,7 @@ void ListModel::requestLogo(QString logo, QString url) MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("FlamePacks", QString("logos/%1").arg(logo.section(".", 0, 0))); auto job = new NetJob(QString("Flame Icon Download %1").arg(logo), APPLICATION->network()); - job->addNetAction(Net::Download::makeCached(QUrl(url), entry)); + job->addNetAction(Net::ApiDownload::makeCached(QUrl(url), entry)); auto fullPath = entry->getFullPath(); QObject::connect(job, &NetJob::succeeded, this, [this, logo, fullPath, job] { @@ -169,7 +171,7 @@ void ListModel::performPaginatedSearch() .arg(currentSearchTerm) .arg(currentSort + 1); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchUrl), &response)); jobPtr = netJob; jobPtr->start(); QObject::connect(netJob.get(), &NetJob::succeeded, this, &ListModel::searchRequestFinished); diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.cpp b/launcher/ui/pages/modplatform/flame/FlamePage.cpp index f9ac4a789..c98e40600 100644 --- a/launcher/ui/pages/modplatform/flame/FlamePage.cpp +++ b/launcher/ui/pages/modplatform/flame/FlamePage.cpp @@ -46,6 +46,8 @@ #include "ui/widgets/ProjectItem.h" #include "modplatform/flame/FlameAPI.h" +#include "net/ApiDownload.h" + static FlameAPI api; FlamePage::FlamePage(NewInstanceDialog* dialog, QWidget* parent) : QWidget(parent), ui(new Ui::FlamePage), dialog(dialog) @@ -132,7 +134,7 @@ void FlamePage::onSelectionChanged(QModelIndex curr, QModelIndex prev) auto netJob = new NetJob(QString("Flame::PackVersions(%1)").arg(current.name), APPLICATION->network()); auto response = new QByteArray(); int addonId = current.addonId; - netJob->addNetAction(Net::Download::makeByteArray(QString("https://api.curseforge.com/v1/mods/%1/files").arg(addonId), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QString("https://api.curseforge.com/v1/mods/%1/files").arg(addonId), response)); QObject::connect(netJob, &NetJob::succeeded, this, [this, response, addonId, curr] { if (addonId != current.addonId) { diff --git a/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp b/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp index 2343b79f2..c391c1288 100644 --- a/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp +++ b/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp @@ -37,6 +37,7 @@ #include "Application.h" #include "net/HttpMetaCache.h" #include "net/NetJob.h" +#include "net/ApiDownload.h" #include "StringUtils.h" #include @@ -254,7 +255,7 @@ void ListModel::requestLogo(QString file) MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("FTBPacks", QString("logos/%1").arg(file.section(".", 0, 0))); NetJob *job = new NetJob(QString("FTB Icon Download for %1").arg(file), APPLICATION->network()); - job->addNetAction(Net::Download::makeCached(QUrl(QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/%1").arg(file)), entry)); + job->addNetAction(Net::ApiDownload::makeCached(QUrl(QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/%1").arg(file)), entry)); auto fullPath = entry->getFullPath(); QObject::connect(job, &NetJob::finished, this, [this, file, fullPath] diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp index 346a00b0e..02c43acee 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -42,6 +42,8 @@ #include "minecraft/PackProfile.h" #include "ui/widgets/ProjectItem.h" +#include "net/ApiDownload.h" + #include namespace Modrinth { @@ -140,7 +142,7 @@ void ModpackListModel::performPaginatedSearch() .arg(currentSearchTerm) .arg(currentSort); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchAllUrl), &m_all_response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchAllUrl), &m_all_response)); QObject::connect(netJob.get(), &NetJob::succeeded, this, [this] { QJsonParseError parse_error_all{}; @@ -233,7 +235,7 @@ void ModpackListModel::requestLogo(QString logo, QString url) MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry(m_parent->metaEntryBase(), QString("logos/%1").arg(logo.section(".", 0, 0))); auto job = new NetJob(QString("%1 Icon Download %2").arg(m_parent->debugName()).arg(logo), APPLICATION->network()); - job->addNetAction(Net::Download::makeCached(QUrl(url), entry)); + job->addNetAction(Net::ApiDownload::makeCached(QUrl(url), entry)); auto fullPath = entry->getFullPath(); QObject::connect(job, &NetJob::succeeded, this, [this, logo, fullPath, job] { diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index 0bb11d834..c450395c3 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -46,6 +46,8 @@ #include "ui/widgets/ProjectItem.h" +#include "net/ApiDownload.h" + #include #include #include @@ -127,7 +129,7 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, QModelIndex prev) QString id = current.id; - netJob->addNetAction(Net::Download::makeByteArray(QString("%1/project/%2").arg(BuildConfig.MODRINTH_PROD_URL, id), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QString("%1/project/%2").arg(BuildConfig.MODRINTH_PROD_URL, id), response)); QObject::connect(netJob, &NetJob::succeeded, this, [this, response, id, curr] { if (id != current.id) { @@ -179,7 +181,7 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, QModelIndex prev) QString id = current.id; netJob->addNetAction( - Net::Download::makeByteArray(QString("%1/project/%2/version").arg(BuildConfig.MODRINTH_PROD_URL, id), response)); + Net::ApiDownload::makeByteArray(QString("%1/project/%2/version").arg(BuildConfig.MODRINTH_PROD_URL, id), response)); QObject::connect(netJob, &NetJob::succeeded, this, [this, response, id, curr] { if (id != current.id) { diff --git a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp index 50f0c72d1..0790fb0bc 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp +++ b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp @@ -38,6 +38,8 @@ #include "BuildConfig.h" #include "Json.h" +#include "net/ApiDownload.h" + #include Technic::ListModel::ListModel(QObject *parent) : QAbstractListModel(parent) @@ -134,7 +136,7 @@ void Technic::ListModel::performSearch() ).arg(BuildConfig.TECHNIC_API_BASE_URL, BuildConfig.TECHNIC_API_BUILD, currentSearchTerm); searchMode = List; } - netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchUrl), &response)); jobPtr = netJob; jobPtr->start(); QObject::connect(netJob.get(), &NetJob::succeeded, this, &ListModel::searchRequestFinished); @@ -286,7 +288,7 @@ void Technic::ListModel::requestLogo(QString logo, QString url) MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("TechnicPacks", QString("logos/%1").arg(logo)); NetJob *job = new NetJob(QString("Technic Icon Download %1").arg(logo), APPLICATION->network()); - job->addNetAction(Net::Download::makeCached(QUrl(url), entry)); + job->addNetAction(Net::ApiDownload::makeCached(QUrl(url), entry)); auto fullPath = entry->getFullPath(); diff --git a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp index 859da97e9..5aabcf34f 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp +++ b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp @@ -49,6 +49,8 @@ #include "Application.h" #include "modplatform/technic/SolderPackManifest.h" +#include "net/ApiDownload.h" + TechnicPage::TechnicPage(NewInstanceDialog* dialog, QWidget *parent) : QWidget(parent), ui(new Ui::TechnicPage), dialog(dialog) { @@ -143,7 +145,7 @@ void TechnicPage::suggestCurrent() auto netJob = makeShared(QString("Technic::PackMeta(%1)").arg(current.name), APPLICATION->network()); QString slug = current.slug; - netJob->addNetAction(Net::Download::makeByteArray(QString("%1modpack/%2?build=%3").arg(BuildConfig.TECHNIC_API_BASE_URL, slug, BuildConfig.TECHNIC_API_BUILD), &response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QString("%1modpack/%2?build=%3").arg(BuildConfig.TECHNIC_API_BASE_URL, slug, BuildConfig.TECHNIC_API_BUILD), &response)); QObject::connect(netJob.get(), &NetJob::succeeded, this, [this, slug] { jobPtr.reset(); @@ -249,7 +251,7 @@ void TechnicPage::metadataLoaded() auto netJob = makeShared(QString("Technic::SolderMeta(%1)").arg(current.name), APPLICATION->network()); auto url = QString("%1/modpack/%2").arg(current.url, current.slug); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(url), &response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(url), &response)); QObject::connect(netJob.get(), &NetJob::succeeded, this, &TechnicPage::onSolderLoaded); diff --git a/launcher/ui/widgets/VariableSizedImageObject.cpp b/launcher/ui/widgets/VariableSizedImageObject.cpp index 991b4a047..3aa3343d0 100644 --- a/launcher/ui/widgets/VariableSizedImageObject.cpp +++ b/launcher/ui/widgets/VariableSizedImageObject.cpp @@ -26,6 +26,7 @@ #include "Application.h" #include "net/NetJob.h" +#include "net/ApiDownload.h" enum FormatProperties { ImageData = QTextFormat::UserProperty + 1 }; @@ -97,7 +98,7 @@ void VariableSizedImageObject::loadImage(QTextDocument* doc, const QUrl& source, QString("images/%1").arg(QString(QCryptographicHash::hash(source.toEncoded(), QCryptographicHash::Algorithm::Sha1).toHex()))); auto job = new NetJob(QString("Load Image: %1").arg(source.fileName()), APPLICATION->network()); - job->addNetAction(Net::Download::makeCached(source, entry)); + job->addNetAction(Net::ApiDownload::makeCached(source, entry)); auto full_entry_path = entry->getFullPath(); auto source_url = source; From 0bda885bbff4944b017862a75d91e6b128ece221 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sat, 3 Jun 2023 21:53:29 -0700 Subject: [PATCH 041/268] packaging: add appimage update capability Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/build.yml | 15 +++++++++++++++ .github/workflows/trigger_release.yml | 2 ++ 2 files changed, 17 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 691e257bf..cb4f5ebe1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -249,6 +249,8 @@ jobs: wget "https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage" wget "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage" + wget "https://github.com/AppImageCommunity/AppImageUpdate/releases/download/continuous/AppImageUpdate-x86_64.AppImage" + ${{ github.workspace }}/.github/scripts/prepare_JREs.sh - name: Add QT_HOST_PATH var (Windows MSVC arm64) @@ -479,6 +481,12 @@ jobs: LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk/lib" export LD_LIBRARY_PATH + chmod +x AppImageUpdate-x86_64.AppImage + ./AppImageUpdate-x86_64.AppImage --appimage-extract + rsync --ignore-existing -ar squashfs_root/usr/ ${{ env.INSTALL_APPIMAGE_DIR }}/usr/ + + export UPDATE_INFORMATION="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|PrismLauncher-${{ runner.os }}-*-x86_64.AppImage.zsync" + ./linuxdeploy-x86_64.AppImage --appdir ${{ env.INSTALL_APPIMAGE_DIR }} --output appimage --plugin qt -i ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/icons/hicolor/scalable/apps/org.prismlauncher.PrismLauncher.svg ## @@ -547,6 +555,13 @@ jobs: with: name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage path: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage + + - name: Upload AppImage Zsync (Linux) + if: runner.os == 'Linux' && matrix.qt_ver != 5 + uses: actions/upload-artifact@v3 + with: + name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage.zsync + path: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage.zsync - name: ccache stats (Windows MinGW-w64) if: runner.os == 'Windows' && matrix.msystem != '' diff --git a/.github/workflows/trigger_release.yml b/.github/workflows/trigger_release.yml index 3c56a38ea..31e8bde40 100644 --- a/.github/workflows/trigger_release.yml +++ b/.github/workflows/trigger_release.yml @@ -44,6 +44,7 @@ jobs: mv PrismLauncher-Linux-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz mv PrismLauncher-Linux*/PrismLauncher.tar.gz PrismLauncher-Linux-${{ env.VERSION }}.tar.gz mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage + mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage.zsync PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage.zsync mv PrismLauncher-macOS-Legacy*/PrismLauncher.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz mv PrismLauncher-macOS*/PrismLauncher.tar.gz PrismLauncher-macOS-${{ env.VERSION }}.tar.gz @@ -89,6 +90,7 @@ jobs: PrismLauncher-Linux-${{ env.VERSION }}.tar.gz PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage + PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage.zsync PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz PrismLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip From 89d8f9b82977dab87aecfe7282ca2b88dbf0991e Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sat, 3 Jun 2023 22:43:41 -0700 Subject: [PATCH 042/268] packaging(linux): use vars when refrencing qt install dir Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cb4f5ebe1..25abab9eb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -469,7 +469,7 @@ jobs: cp -r ${{ github.workspace }}/JREs/jre17/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk - cp -r /home/runner/work/PrismLauncher/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines + cp -r ${{ runner.workspace }}/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines cp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/ cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}//usr/lib/ @@ -483,6 +483,7 @@ jobs: chmod +x AppImageUpdate-x86_64.AppImage ./AppImageUpdate-x86_64.AppImage --appimage-extract + rm -rf squashfs_root/usr/share rsync --ignore-existing -ar squashfs_root/usr/ ${{ env.INSTALL_APPIMAGE_DIR }}/usr/ export UPDATE_INFORMATION="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|PrismLauncher-${{ runner.os }}-*-x86_64.AppImage.zsync" From 2c959734463d3efd3cd62566e155453de879c7ac Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 4 Jun 2023 10:36:34 -0700 Subject: [PATCH 043/268] packaging(appimage): dont use rsync Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/build.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 25abab9eb..ba80885d4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -483,8 +483,14 @@ jobs: chmod +x AppImageUpdate-x86_64.AppImage ./AppImageUpdate-x86_64.AppImage --appimage-extract - rm -rf squashfs_root/usr/share - rsync --ignore-existing -ar squashfs_root/usr/ ${{ env.INSTALL_APPIMAGE_DIR }}/usr/ + + mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/optional + mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins + + cp -r squashfs-root/usr/bin/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/bin + cp -r squashfs-root/usr/lib/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib + cp -r squashfs-root/usr/optional/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/optional + cp -r squashfs-root/usr/optional/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins export UPDATE_INFORMATION="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|PrismLauncher-${{ runner.os }}-*-x86_64.AppImage.zsync" From ab418162657e95e510a15a4d64e04b2af9856a8a Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 4 Jun 2023 12:27:01 -0700 Subject: [PATCH 044/268] packaging: use runner.workspace when copying files Signed-off-by: Rachel Powers <508861+Ryex@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 ba80885d4..6dc94dbd4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -386,8 +386,8 @@ jobs: cd ${{ env.INSTALL_DIR }} if ("${{ matrix.qt_ver }}" -eq "5") { - Copy-Item D:/a/PrismLauncher/Qt/Tools/OpenSSL/Win_x86/bin/libcrypto-1_1.dll -Destination libcrypto-1_1.dll - Copy-Item D:/a/PrismLauncher/Qt/Tools/OpenSSL/Win_x86/bin/libssl-1_1.dll -Destination libssl-1_1.dll + Copy-Item ${{ runner.workspace }}/Qt/Tools/OpenSSL/Win_x86/bin/libcrypto-1_1.dll -Destination libcrypto-1_1.dll + Copy-Item ${{ runner.workspace }}/Qt/Tools/OpenSSL/Win_x86/bin/libssl-1_1.dll -Destination libssl-1_1.dll } - name: Fetch codesign certificate (Windows) From 3531c5bb8c29ede39371a4430da0f0f17c026bf5 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 4 Jun 2023 16:58:46 -0700 Subject: [PATCH 045/268] packaging(appimage): put zsync in relase:x Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/trigger_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/trigger_release.yml b/.github/workflows/trigger_release.yml index 31e8bde40..2041747f9 100644 --- a/.github/workflows/trigger_release.yml +++ b/.github/workflows/trigger_release.yml @@ -44,7 +44,7 @@ jobs: mv PrismLauncher-Linux-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz mv PrismLauncher-Linux*/PrismLauncher.tar.gz PrismLauncher-Linux-${{ env.VERSION }}.tar.gz mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage - mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage.zsync PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage.zsync + mv PrismLauncher-*.AppImage.zsync/PrismLauncher-*.AppImage.zsync PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage.zsync mv PrismLauncher-macOS-Legacy*/PrismLauncher.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz mv PrismLauncher-macOS*/PrismLauncher.tar.gz PrismLauncher-macOS-${{ env.VERSION }}.tar.gz From 564e394ec80fed5174a7c42edcd6801ba8c29058 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 24 Jun 2023 21:48:12 +0300 Subject: [PATCH 046/268] feat:Update mods now fills missing dependencies Signed-off-by: Trial97 --- launcher/modplatform/CheckUpdateTask.h | 20 +++++-- .../modplatform/flame/FlameCheckUpdate.cpp | 23 +++---- .../modrinth/ModrinthCheckUpdate.cpp | 23 +++---- launcher/ui/dialogs/ModUpdateDialog.cpp | 60 +++++++++++++++++-- 4 files changed, 94 insertions(+), 32 deletions(-) diff --git a/launcher/modplatform/CheckUpdateTask.h b/launcher/modplatform/CheckUpdateTask.h index f7582b8f1..94b16dcc9 100644 --- a/launcher/modplatform/CheckUpdateTask.h +++ b/launcher/modplatform/CheckUpdateTask.h @@ -1,8 +1,9 @@ #pragma once #include "minecraft/mod/Mod.h" -#include "modplatform/ResourceAPI.h" +#include "minecraft/mod/tasks/GetModDependenciesTask.h" #include "modplatform/ModIndex.h" +#include "modplatform/ResourceAPI.h" #include "tasks/Task.h" class ResourceDownloadTask; @@ -12,8 +13,11 @@ class CheckUpdateTask : public Task { Q_OBJECT public: - CheckUpdateTask(QList& mods, std::list& mcVersions, std::optional loaders, std::shared_ptr mods_folder) - : Task(nullptr), m_mods(mods), m_game_versions(mcVersions), m_loaders(loaders), m_mods_folder(mods_folder) {}; + CheckUpdateTask(QList& mods, + std::list& mcVersions, + std::optional loaders, + std::shared_ptr mods_folder) + : Task(nullptr), m_mods(mods), m_game_versions(mcVersions), m_loaders(loaders), m_mods_folder(mods_folder){}; struct UpdatableMod { QString name; @@ -25,12 +29,19 @@ class CheckUpdateTask : public Task { shared_qobject_ptr download; public: - UpdatableMod(QString name, QString old_h, QString old_v, QString new_v, QString changelog, ModPlatform::ResourceProvider p, shared_qobject_ptr t) + UpdatableMod(QString name, + QString old_h, + QString old_v, + QString new_v, + QString changelog, + ModPlatform::ResourceProvider p, + shared_qobject_ptr t) : name(name), old_hash(old_h), old_version(old_v), new_version(new_v), changelog(changelog), provider(p), download(t) {} }; auto getUpdatable() -> std::vector&& { return std::move(m_updatable); } + auto getDependencies() -> QList>&& { return std::move(m_deps); } public slots: bool abort() override = 0; @@ -48,4 +59,5 @@ class CheckUpdateTask : public Task { std::shared_ptr m_mods_folder; std::vector m_updatable; + QList> m_deps; }; diff --git a/launcher/modplatform/flame/FlameCheckUpdate.cpp b/launcher/modplatform/flame/FlameCheckUpdate.cpp index a2628e34c..dd6dd1eac 100644 --- a/launcher/modplatform/flame/FlameCheckUpdate.cpp +++ b/launcher/modplatform/flame/FlameCheckUpdate.cpp @@ -12,6 +12,7 @@ #include "minecraft/mod/ModFolderModel.h" #include "minecraft/mod/ResourceFolderModel.h" +#include "minecraft/mod/tasks/GetModDependenciesTask.h" static FlameAPI api; @@ -154,18 +155,17 @@ void FlameCheckUpdate::executeTask() continue; } + // Fake pack with the necessary info to pass to the download task :) + auto pack = std::make_shared(); + pack->name = mod->name(); + pack->slug = mod->metadata()->slug; + pack->addonId = mod->metadata()->project_id; + pack->websiteUrl = mod->homeurl(); + for (auto& author : mod->authors()) + pack->authors.append({ author }); + pack->description = mod->description(); + pack->provider = ModPlatform::ResourceProvider::FLAME; if (!latest_ver.hash.isEmpty() && (mod->metadata()->hash != latest_ver.hash || mod->status() == ModStatus::NotInstalled)) { - // Fake pack with the necessary info to pass to the download task :) - auto pack = std::make_shared(); - pack->name = mod->name(); - pack->slug = mod->metadata()->slug; - pack->addonId = mod->metadata()->project_id; - pack->websiteUrl = mod->homeurl(); - for (auto& author : mod->authors()) - pack->authors.append({ author }); - pack->description = mod->description(); - pack->provider = ModPlatform::ResourceProvider::FLAME; - auto old_version = mod->version(); if (old_version.isEmpty() && mod->status() != ModStatus::NotInstalled) { auto current_ver = getFileInfo(latest_ver.addonId.toInt(), mod->metadata()->file_id.toInt()); @@ -177,6 +177,7 @@ void FlameCheckUpdate::executeTask() api.getModFileChangelog(latest_ver.addonId.toInt(), latest_ver.fileId.toInt()), ModPlatform::ResourceProvider::FLAME, download_task); } + m_deps.append(std::make_shared(pack, latest_ver)); } emitSucceeded(); diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp index a7c22832a..dac110226 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp @@ -145,26 +145,27 @@ void ModrinthCheckUpdate::executeTask() auto mod = *mod_iter; auto key = project_ver.hash; + + // Fake pack with the necessary info to pass to the download task :) + auto pack = std::make_shared(); + pack->name = mod->name(); + pack->slug = mod->metadata()->slug; + pack->addonId = mod->metadata()->project_id; + pack->websiteUrl = mod->homeurl(); + for (auto& author : mod->authors()) + pack->authors.append({ author }); + pack->description = mod->description(); + pack->provider = ModPlatform::ResourceProvider::MODRINTH; if ((key != hash && project_ver.is_preferred) || (mod->status() == ModStatus::NotInstalled)) { if (mod->version() == project_ver.version_number) continue; - // Fake pack with the necessary info to pass to the download task :) - auto pack = std::make_shared(); - pack->name = mod->name(); - pack->slug = mod->metadata()->slug; - pack->addonId = mod->metadata()->project_id; - pack->websiteUrl = mod->homeurl(); - for (auto& author : mod->authors()) - pack->authors.append({ author }); - pack->description = mod->description(); - pack->provider = ModPlatform::ResourceProvider::MODRINTH; - auto download_task = makeShared(pack, project_ver, m_mods_folder); m_updatable.emplace_back(pack->name, hash, mod->version(), project_ver.version_number, project_ver.changelog, ModPlatform::ResourceProvider::MODRINTH, download_task); } + m_deps.append(std::make_shared(pack, project_ver)); } } catch (Json::JsonException& e) { failed(e.cause() + " : " + e.what()); diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index 8618b9240..6629b1f23 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -3,6 +3,9 @@ #include "CustomMessageBox.h" #include "ProgressDialog.h" #include "ScrollMessageBox.h" +#include "minecraft/mod/tasks/GetModDependenciesTask.h" +#include "modplatform/ModIndex.h" +#include "modplatform/flame/FlameAPI.h" #include "ui_ReviewMessageBox.h" #include "FileSystem.h" @@ -89,15 +92,17 @@ void ModUpdateDialog::checkCandidates() if (!m_modrinth_to_update.empty()) { m_modrinth_check_task.reset(new ModrinthCheckUpdate(m_modrinth_to_update, versions, loaders, m_mod_model)); - connect(m_modrinth_check_task.get(), &CheckUpdateTask::checkFailed, this, - [this](Mod* mod, QString reason, QUrl recover_url) { m_failed_check_update.append({mod, reason, recover_url}); }); + connect(m_modrinth_check_task.get(), &CheckUpdateTask::checkFailed, this, [this](Mod* mod, QString reason, QUrl recover_url) { + m_failed_check_update.append({ mod, reason, recover_url }); + }); check_task.addTask(m_modrinth_check_task); } if (!m_flame_to_update.empty()) { m_flame_check_task.reset(new FlameCheckUpdate(m_flame_to_update, versions, loaders, m_mod_model)); - connect(m_flame_check_task.get(), &CheckUpdateTask::checkFailed, this, - [this](Mod* mod, QString reason, QUrl recover_url) { m_failed_check_update.append({mod, reason, recover_url}); }); + connect(m_flame_check_task.get(), &CheckUpdateTask::checkFailed, this, [this](Mod* mod, QString reason, QUrl recover_url) { + m_failed_check_update.append({ mod, reason, recover_url }); + }); check_task.addTask(m_flame_check_task); } @@ -124,6 +129,8 @@ void ModUpdateDialog::checkCandidates() return; } + QList> selectedVers; + // Add found updates for Modrinth if (m_modrinth_check_task) { auto modrinth_updates = m_modrinth_check_task->getUpdatable(); @@ -133,6 +140,7 @@ void ModUpdateDialog::checkCandidates() appendMod(updatable); m_tasks.insert(updatable.name, updatable.download); } + selectedVers.append(m_modrinth_check_task->getDependencies()); } // Add found updated for Flame @@ -144,6 +152,7 @@ void ModUpdateDialog::checkCandidates() appendMod(updatable); m_tasks.insert(updatable.name, updatable.download); } + selectedVers.append(m_flame_check_task->getDependencies()); } // Report failed update checking @@ -162,7 +171,7 @@ void ModUpdateDialog::checkCandidates() if (!recover_url.isEmpty()) //: %1 is the link to download it manually text += tr("Possible solution: Getting the latest version manually:
%1
") - .arg(QString("%1").arg(recover_url.toString())); + .arg(QString("%1").arg(recover_url.toString())); text += "
"; } @@ -178,6 +187,45 @@ void ModUpdateDialog::checkCandidates() } } + { // dependencies + auto depTask = makeShared(this, m_instance, m_mod_model.get(), selectedVers); + + connect(depTask.get(), &Task::failed, this, + [&](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); }); + + connect(depTask.get(), &Task::succeeded, this, [&]() { + QStringList warnings = depTask->warnings(); + if (warnings.count()) { + CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->exec(); + } + }); + + ProgressDialog progress_dialog_deps(m_parent); + progress_dialog_deps.setSkipButton(true, tr("Abort")); + progress_dialog_deps.setWindowTitle(tr("Checking for dependencies...")); + auto dret = progress_dialog_deps.execWithTask(depTask.get()); + + // If the dialog was skipped / some download error happened + if (dret == QDialog::DialogCode::Rejected) { + m_aborted = true; + QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection); + return; + } + static FlameAPI api; + + for (auto dep : depTask->getDependecies()) { + auto changelog = dep->version.changelog; + if (dep->pack->provider == ModPlatform::ResourceProvider::FLAME) + changelog = api.getModFileChangelog(dep->version.addonId.toInt(), dep->version.fileId.toInt()); + auto download_task = makeShared(dep->pack, dep->version, m_mod_model); + CheckUpdateTask::UpdatableMod updatable = { dep->pack->name, dep->version.hash, "", dep->version.version, + changelog, dep->pack->provider, download_task }; + + appendMod(updatable); + m_tasks.insert(updatable.name, updatable.download); + } + } + // If there's no mod to be updated if (ui->modTreeWidget->topLevelItemCount() == 0) { m_no_updates = true; @@ -342,7 +390,7 @@ void ModUpdateDialog::onMetadataFailed(Mod* mod, bool try_others, ModPlatform::R } else { QString reason{ tr("Couldn't find a valid version on the selected mod provider(s)") }; - m_failed_metadata.append({mod, reason}); + m_failed_metadata.append({ mod, reason }); } } From 158b07a39e98467c10682a64fa76eb31642abc9e Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 25 Jun 2023 00:24:08 +0300 Subject: [PATCH 047/268] moved getRequiredBy Signed-off-by: Trial97 --- .../mod/tasks/GetModDependenciesTask.cpp | 29 +++++++++++++++++++ .../mod/tasks/GetModDependenciesTask.h | 1 + launcher/ui/dialogs/ModUpdateDialog.cpp | 24 +++++++++++++-- launcher/ui/dialogs/ModUpdateDialog.h | 6 ++-- .../ui/dialogs/ResourceDownloadDialog.cpp | 29 +++---------------- 5 files changed, 60 insertions(+), 29 deletions(-) diff --git a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp index f8ecdb33e..43d38cfdc 100644 --- a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp +++ b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp @@ -250,3 +250,32 @@ void GetModDependenciesTask::removePack(const QVariant addonId) ++it; #endif } + +QHash GetModDependenciesTask::getRequiredBy() +{ + QHash rby; + auto fullList = m_selected + m_pack_dependencies; + for (auto mod : fullList) { + auto addonId = mod->pack->addonId; + auto provider = mod->pack->provider; + auto version = mod->version.fileId; + auto req = QStringList(); + for (auto& smod : fullList) { + if (provider != smod->pack->provider) + continue; + auto deps = smod->version.dependencies; + if (auto dep = std::find_if(deps.begin(), deps.end(), + [addonId, provider, version](const ModPlatform::Dependency& d) { + return d.type == ModPlatform::DependencyType::REQUIRED && + (provider == ModPlatform::ResourceProvider::MODRINTH && d.addonId.toString().isEmpty() + ? version == d.version + : d.addonId == addonId); + }); + dep != deps.end()) { + req.append(smod->pack->name); + } + } + rby[addonId.toString()] = req; + } + return rby; +} \ No newline at end of file diff --git a/launcher/minecraft/mod/tasks/GetModDependenciesTask.h b/launcher/minecraft/mod/tasks/GetModDependenciesTask.h index 50eba6afc..5a29b6eae 100644 --- a/launcher/minecraft/mod/tasks/GetModDependenciesTask.h +++ b/launcher/minecraft/mod/tasks/GetModDependenciesTask.h @@ -62,6 +62,7 @@ class GetModDependenciesTask : public SequentialTask { QList> selected); auto getDependecies() const -> QList> { return m_pack_dependencies; } + QHash getRequiredBy(); protected slots: Task::Ptr prepareDependencyTask(const ModPlatform::Dependency&, const ModPlatform::ResourceProvider, int); diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index 6629b1f23..33ea5a1d5 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -213,6 +213,8 @@ void ModUpdateDialog::checkCandidates() } static FlameAPI api; + auto getRequiredBy = depTask->getRequiredBy(); + for (auto dep : depTask->getDependecies()) { auto changelog = dep->version.changelog; if (dep->pack->provider == ModPlatform::ResourceProvider::FLAME) @@ -221,7 +223,7 @@ void ModUpdateDialog::checkCandidates() CheckUpdateTask::UpdatableMod updatable = { dep->pack->name, dep->version.hash, "", dep->version.version, changelog, dep->pack->provider, download_task }; - appendMod(updatable); + appendMod(updatable, getRequiredBy.value(dep->version.addonId.toString())); m_tasks.insert(updatable.name, updatable.download); } } @@ -394,7 +396,7 @@ void ModUpdateDialog::onMetadataFailed(Mod* mod, bool try_others, ModPlatform::R } } -void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info) +void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info, QStringList requiredBy) { auto item_top = new QTreeWidgetItem(ui->modTreeWidget); item_top->setCheckState(0, Qt::CheckState::Checked); @@ -410,6 +412,24 @@ void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info) auto new_version_item = new QTreeWidgetItem(item_top); new_version_item->setText(0, tr("New version: %1").arg(info.new_version)); + if (!requiredBy.isEmpty()) { + auto new_version_item = new QTreeWidgetItem(item_top); + new_version_item->setText(0, tr("New version: %1").arg(info.new_version)); + + auto requiredByItem = new QTreeWidgetItem(item_top); + if (requiredBy.length() == 1) { + requiredByItem->setText(0, tr("Required by: %1").arg(requiredBy.back())); + } else { + requiredByItem->setText(0, tr("Required by:")); + auto i = 0; + for (auto req : requiredBy) { + auto reqItem = new QTreeWidgetItem(requiredByItem); + reqItem->setText(0, req); + reqItem->insertChildren(i++, { reqItem }); + } + } + } + auto changelog_item = new QTreeWidgetItem(item_top); changelog_item->setText(0, tr("Changelog of the latest version")); diff --git a/launcher/ui/dialogs/ModUpdateDialog.h b/launcher/ui/dialogs/ModUpdateDialog.h index 1a92f6134..b79aa4943 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.h +++ b/launcher/ui/dialogs/ModUpdateDialog.h @@ -23,7 +23,7 @@ class ModUpdateDialog final : public ReviewMessageBox { void checkCandidates(); - void appendMod(const CheckUpdateTask::UpdatableMod& info); + void appendMod(const CheckUpdateTask::UpdatableMod& info, QStringList requiredBy = {}); const QList getTasks(); auto indexDir() const -> QDir { return m_mod_model->indexDir(); } @@ -36,7 +36,9 @@ class ModUpdateDialog final : public ReviewMessageBox { private slots: void onMetadataEnsured(Mod*); - void onMetadataFailed(Mod*, bool try_others = false, ModPlatform::ResourceProvider first_choice = ModPlatform::ResourceProvider::MODRINTH); + void onMetadataFailed(Mod*, + bool try_others = false, + ModPlatform::ResourceProvider first_choice = ModPlatform::ResourceProvider::MODRINTH); private: QWidget* m_parent; diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp index 4f59f5605..0e579ce60 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp +++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp @@ -125,35 +125,12 @@ void ResourceDownloadDialog::connectButtons() static ModPlatform::ProviderCapabilities ProviderCaps; -QStringList getRequiredBy(QList tasks, ResourceDownloadDialog::DownloadTaskPtr pack) -{ - auto addonId = pack->getPack()->addonId; - auto provider = pack->getPack()->provider; - auto version = pack->getVersionID(); - auto req = QStringList(); - for (auto& task : tasks) { - if (provider != task->getPack()->provider) - continue; - auto deps = task->getVersion().dependencies; - if (auto dep = std::find_if(deps.begin(), deps.end(), - [addonId, provider, version](const ModPlatform::Dependency& d) { - return d.type == ModPlatform::DependencyType::REQUIRED && - (provider == ModPlatform::ResourceProvider::MODRINTH && d.addonId.toString().isEmpty() - ? version == d.version - : d.addonId == addonId); - }); - dep != deps.end()) { - req.append(task->getName()); - } - } - return req; -} - void ResourceDownloadDialog::confirm() { auto confirm_dialog = ReviewMessageBox::create(this, tr("Confirm %1 to download").arg(resourcesString())); confirm_dialog->retranslateUi(resourcesString()); + QHash getRequiredBy; if (auto task = getModDependenciesTask(); task) { connect(task.get(), &Task::failed, this, [&](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); }); @@ -178,6 +155,7 @@ void ResourceDownloadDialog::confirm() } else { for (auto dep : task->getDependecies()) addResource(dep->pack, dep->version); + getRequiredBy = task->getRequiredBy(); } } @@ -187,7 +165,8 @@ void ResourceDownloadDialog::confirm() }); for (auto& task : selected) { confirm_dialog->appendResource({ task->getName(), task->getFilename(), task->getCustomPath(), - ProviderCaps.name(task->getProvider()), getRequiredBy(selected, task) }); + ProviderCaps.name(task->getProvider()), + getRequiredBy.value(task->getPack()->addonId.toString()) }); } if (confirm_dialog->exec()) { From 6f7d901a1f5c02e0629e4bae9172c04bb81ce0d9 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 25 Jun 2023 09:17:28 +0300 Subject: [PATCH 048/268] removed extra variable Signed-off-by: Trial97 --- launcher/ui/dialogs/ModUpdateDialog.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index 33ea5a1d5..66d7e3afa 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -413,9 +413,6 @@ void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info, QStri new_version_item->setText(0, tr("New version: %1").arg(info.new_version)); if (!requiredBy.isEmpty()) { - auto new_version_item = new QTreeWidgetItem(item_top); - new_version_item->setText(0, tr("New version: %1").arg(info.new_version)); - auto requiredByItem = new QTreeWidgetItem(item_top); if (requiredBy.length() == 1) { requiredByItem->setText(0, tr("Required by: %1").arg(requiredBy.back())); From 98174b7a377aea8466c6a5fae97ee0db9b2419ae Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 2 Jun 2023 15:15:25 -0700 Subject: [PATCH 049/268] refactor: use Net tasks for github api download Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/CMakeLists.txt | 30 ++++++- launcher/net/Download.cpp | 9 +- launcher/net/Download.h | 1 + .../updater/prismupdater/PrismUpdater.cpp | 89 +++++++++---------- launcher/updater/prismupdater/PrismUpdater.h | 17 ++-- 5 files changed, 90 insertions(+), 56 deletions(-) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 68fd5f7b7..58bd57a87 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -584,6 +584,7 @@ set(PRISMUPDATER_SOURCES updater/prismupdater/UpdaterDialogs.h updater/prismupdater/UpdaterDialogs.cpp updater/prismupdater/GitHubRelease.h + Json.h Json.cpp FileSystem.h @@ -595,6 +596,31 @@ set(PRISMUPDATER_SOURCES Version.h Version.cpp Markdown.h + + # Time + MMCTime.h + MMCTime.cpp + + + net/ByteArraySink.h + net/ChecksumValidator.h + net/Download.cpp + net/Download.h + net/FileSink.cpp + net/FileSink.h + net/HttpMetaCache.cpp + net/HttpMetaCache.h + net/Logging.h + net/Logging.cpp + net/NetAction.h + net/NetJob.cpp + net/NetJob.h + net/NetUtils.h + net/Sink.h + net/Validator.h + net/HeaderProxy.h + net/RawHeaderProxy.h + ) ######## Logging categories ######## @@ -1125,6 +1151,7 @@ endif() add_library(Launcher_logic STATIC ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${LAUNCHER_UI} ${LAUNCHER_RESOURCES}) target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION) target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION) target_link_libraries(Launcher_logic systeminfo Launcher_murmur2 @@ -1202,7 +1229,7 @@ install(TARGETS ${Launcher_Name} if(WIN32 OR (DEFINED Launcher_BUILD_UPDATER AND Launcher_BUILD_UPDATER) ) # Updater - add_library(prism_updater_logic STATIC ${PRISMUPDATER_SOURCES} ${PRISMUPDATER_UI}) + add_library(prism_updater_logic STATIC ${PRISMUPDATER_SOURCES} ${TASKS_SOURCES} ${PRISMUPDATER_UI}) target_include_directories(prism_updater_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(prism_updater_logic systeminfo @@ -1213,6 +1240,7 @@ if(WIN32 OR (DEFINED Launcher_BUILD_UPDATER AND Launcher_BUILD_UPDATER) ) Qt${QT_VERSION_MAJOR}::Network ${Launcher_QT_LIBS} cmark::cmark + Katabasis ) add_executable("${Launcher_Name}_updater" WIN32 updater/prismupdater/updater_main.cpp) diff --git a/launcher/net/Download.cpp b/launcher/net/Download.cpp index c86452137..de6f43380 100644 --- a/launcher/net/Download.cpp +++ b/launcher/net/Download.cpp @@ -131,13 +131,12 @@ void Download::executeTask() return; } -#if defined (LAUNCHER_APPLICATION) - auto user_agent = APPLICATION->getUserAgent(); +#if defined(LAUNCHER_APPLICATION) + auto user_agent = APPLICATION->getUserAgent().toUtf8(); #else - auto user_agent = BuildConfig.USER_AGENT; + auto user_agent = BuildConfig.USER_AGENT.toUtf8(); #endif - - request.setHeader(QNetworkRequest::UserAgentHeader, user_agent.toUtf8()); + request.setHeader(QNetworkRequest::UserAgentHeader, user_agent); for ( auto& header_proxy : m_headerProxies ) { header_proxy->writeHeaders(request); diff --git a/launcher/net/Download.h b/launcher/net/Download.h index 19f675adc..65141a04c 100644 --- a/launcher/net/Download.h +++ b/launcher/net/Download.h @@ -59,6 +59,7 @@ class Download : public NetAction { public: ~Download() override = default; + #if defined(LAUNCHER_APPLICATION) static auto makeCached(QUrl url, MetaEntryPtr entry, Options options = Option::NoOptions) -> Download::Ptr; static auto makeByteArray(QUrl url, std::shared_ptr output, Options options = Option::NoOptions) -> Download::Ptr; static auto makeFile(QUrl url, QString path, Options options = Option::NoOptions) -> Download::Ptr; diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 923b891f9..0e6620085 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -34,7 +34,9 @@ #include #include +#include #include +#include #include #include @@ -74,6 +76,9 @@ namespace fs = ghc::filesystem; #include "Json.h" #include "StringUtils.h" +#include "net/Download.h" +#include "net/RawHeaderProxy.h" + /** output to the log file */ void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg) { @@ -205,8 +210,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar } #endif } - m_network = new QNetworkAccessManager(); - + { // setup logging static const QString logBase = BuildConfig.LAUNCHER_NAME + "Updater" + (m_checkOnly ? "-CheckOnly" : "") + "-%0.log"; auto moveFile = [](const QString& oldName, const QString& newName) { @@ -292,6 +296,8 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar qDebug() << "<> Log initialized."; } + + { // log debug program info qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME) << "Updater" << ", (c) 2022-2023 " << qPrintable(QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", ")); @@ -310,7 +316,14 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar qDebug() << "<> Paths set."; } - loadReleaseList(); + { // network + m_network = makeShared(new QNetworkAccessManager()); + qDebug() << "Detecting proxy settings..."; + QNetworkProxy proxy = QNetworkProxy::applicationProxy(); + m_network->setProxy(proxy); + } + + QMetaObject::invokeMethod(this, &PrismUpdaterApp::loadReleaseList, Qt::QueuedConnection); } PrismUpdaterApp::~PrismUpdaterApp() @@ -329,9 +342,6 @@ PrismUpdaterApp::~PrismUpdaterApp() } #endif - m_network->deleteLater(); - if (m_reply) - m_reply->deleteLater(); } void PrismUpdaterApp::fail(const QString& reason) @@ -460,7 +470,7 @@ GitHubRelease PrismUpdaterApp::selectRelease() } else { releases = newerReleases(); } - + if (releases.isEmpty()) return {}; @@ -530,34 +540,40 @@ void PrismUpdaterApp::downloadReleasePage(const QString& api_url, int page) { int per_page = 30; auto page_url = QString("%1?per_page=%2&page=%3").arg(api_url).arg(QString::number(per_page)).arg(QString::number(page)); - QNetworkRequest request(page_url); - request.setRawHeader("Accept", "application/vnd.github+json"); - request.setRawHeader("X-GitHub-Api-Version", "2022-11-28"); + auto responce = std::make_shared(); + auto download = Net::Download::makeByteArray(page_url, responce.get()); + download->setNetwork(m_network); + m_current_url = page_url; - QNetworkReply* rep = m_network->get(request); - m_reply = rep; - auto responce = new QByteArray(); - - connect(rep, &QNetworkReply::finished, this, [this, responce, per_page, api_url, page]() { - int num_found = parseReleasePage(responce); - delete responce; + auto githup_api_headers = new Net::RawHeaderProxy(); + githup_api_headers->addHeaders({ + { "Accept", "application/vnd.github+json" }, + { "X-GitHub-Api-Version", "2022-11-28" }, + }); + download->addHeaderProxy(githup_api_headers); + connect(download.get(), &Net::Download::succeeded, this, [this, responce, per_page, api_url, page]() { + int num_found = parseReleasePage(responce.get()); if (!(num_found < per_page)) { // there may be more, fetch next page downloadReleasePage(api_url, page + 1); } else { run(); } }); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 - connect(rep, &QNetworkReply::errorOccurred, this, &PrismUpdaterApp::downloadError); -#else - connect(rep, QOverload::of(&QNetworkReply::error), this, &WindowsUpdaterApp::downloadError); -#endif - connect(rep, &QNetworkReply::sslErrors, this, &PrismUpdaterApp::sslErrors); - connect(rep, &QNetworkReply::readyRead, this, [this, responce]() { - auto data = m_reply->readAll(); - responce->append(data); + connect(download.get(), &Net::Download::failed, this, &PrismUpdaterApp::downloadError); + + + m_current_task.reset(download); + connect(download.get(), &Net::Download::finished, this, [this](){ + qDebug() << "Download" << m_current_task->getUid().toString() << "finsihed"; + m_current_task.reset(); + m_current_url = ""; }); + + + QCoreApplication::processEvents(); + + QMetaObject::invokeMethod(download.get(), &Task::start, Qt::QueuedConnection); } int PrismUpdaterApp::parseReleasePage(const QByteArray* responce) @@ -624,24 +640,7 @@ bool PrismUpdaterApp::needUpdate(const GitHubRelease& release) return current_ver < release.version; } -void PrismUpdaterApp::downloadError(QNetworkReply::NetworkError error) +void PrismUpdaterApp::downloadError(QString reason) { - if (error == QNetworkReply::OperationCanceledError) { - abort(QString("Aborted %1").arg(m_reply->url().toString())); - } else { - fail(QString("Network request Failed: %1 with reason %2").arg(m_reply->url().toString()).arg(error)); - } -} - -void PrismUpdaterApp::sslErrors(const QList& errors) -{ - int i = 1; - QString err_msg; - for (auto error : errors) { - err_msg.append(QString("Network request %1 SSL Error %2: %3\n").arg(m_reply->url().toString()).arg(i).arg(error.errorString())); - auto cert = error.certificate(); - err_msg.append(QString("Certificate in question:\n%1").arg(cert.toText())); - i++; - } - fail(err_msg); + fail(QString("Network request Failed: %1 with reason %2").arg(m_current_url).arg(reason)); } diff --git a/launcher/updater/prismupdater/PrismUpdater.h b/launcher/updater/prismupdater/PrismUpdater.h index da6b4bf71..52bb7e71b 100644 --- a/launcher/updater/prismupdater/PrismUpdater.h +++ b/launcher/updater/prismupdater/PrismUpdater.h @@ -36,6 +36,10 @@ #include #include +#include "QObjectPtr.h" +#include "net/Download.h" + + #define PRISM_EXTERNAL_EXE #include "FileSystem.h" @@ -58,7 +62,7 @@ class PrismUpdaterApp : public QApplication { void showFatalErrorMessage(const QString& title, const QString& content); void loadPrismVersionFromExe(const QString& exe_path); - + void downloadReleasePage(const QString& api_url, int page); int parseReleasePage(const QByteArray* responce); GitHubRelease getLatestRelease(); @@ -69,8 +73,10 @@ class PrismUpdaterApp : public QApplication { QList newerReleases(); QList nonDraftReleases(); - void downloadError(QNetworkReply::NetworkError error); - void sslErrors(const QList& errors); + public slots: + void downloadError(QString reason); + + private: const QString& root() { return m_rootPath; } @@ -95,8 +101,9 @@ class PrismUpdaterApp : public QApplication { QString m_prismGitCommit; Status m_status = Status::Starting; - QNetworkAccessManager* m_network; - QNetworkReply* m_reply; + shared_qobject_ptr m_network; + QString m_current_url; + Task::Ptr m_current_task; QList m_releases; public: From bee88b1c7fbba6556e9a3f4a1644556274a0f1cb Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sat, 3 Jun 2023 18:41:54 -0700 Subject: [PATCH 050/268] feat(updater) select valid asset Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/CMakeLists.txt | 1 + .../updater/prismupdater/GitHubRelease.cpp | 36 +++++ launcher/updater/prismupdater/GitHubRelease.h | 6 + .../updater/prismupdater/PrismUpdater.cpp | 130 +++++++++++++----- launcher/updater/prismupdater/PrismUpdater.h | 18 ++- .../updater/prismupdater/UpdaterDialogs.cpp | 68 +++++++++ .../updater/prismupdater/UpdaterDialogs.h | 20 +++ 7 files changed, 243 insertions(+), 36 deletions(-) create mode 100644 launcher/updater/prismupdater/GitHubRelease.cpp diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 58bd57a87..d9bd577a0 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -584,6 +584,7 @@ set(PRISMUPDATER_SOURCES updater/prismupdater/UpdaterDialogs.h updater/prismupdater/UpdaterDialogs.cpp updater/prismupdater/GitHubRelease.h + updater/prismupdater/GitHubRelease.cpp Json.h Json.cpp diff --git a/launcher/updater/prismupdater/GitHubRelease.cpp b/launcher/updater/prismupdater/GitHubRelease.cpp new file mode 100644 index 000000000..25e60e885 --- /dev/null +++ b/launcher/updater/prismupdater/GitHubRelease.cpp @@ -0,0 +1,36 @@ +#include "GitHubRelease.h" + +QDebug operator<<(QDebug debug, const GitHubReleaseAsset& asset) +{ + QDebugStateSaver saver(debug); + debug.nospace() << "GitHubReleaseAsset( " + "id: " << asset.id << ", " + "name " << asset.name << ", " + "label: " << asset.label << ", " + "content_type: " << asset.content_type << ", " + "size: " << asset.size << ", " + "created_at: " << asset.created_at << ", " + "updated_at: " << asset.updated_at << ", " + "browser_download_url: " << asset.browser_download_url << " " + ")"; + return debug; +} + +QDebug operator<<(QDebug debug, const GitHubRelease& rls) +{ + QDebugStateSaver saver(debug); + debug.nospace() << "GitHubRelease( " + "id: " << rls.id << ", " + "name " << rls.name << ", " + "tag_name: " << rls.tag_name << ", " + "created_at: " << rls.created_at << ", " + "published_at: " << rls.published_at << ", " + "prerelease: " << rls.prerelease << ", " + "draft: " << rls.draft << ", " + "version" << rls.version << ", " + "body: " << rls.body << ", " + "assets: " << rls.assets << " " + ")"; + return debug; +} + diff --git a/launcher/updater/prismupdater/GitHubRelease.h b/launcher/updater/prismupdater/GitHubRelease.h index 1326a69f1..0c190333f 100644 --- a/launcher/updater/prismupdater/GitHubRelease.h +++ b/launcher/updater/prismupdater/GitHubRelease.h @@ -3,6 +3,8 @@ #include #include +#include + #include "Version.h" struct GitHubReleaseAsset { @@ -32,3 +34,7 @@ struct GitHubRelease { bool isValid() const { return id > 0; } }; + +QDebug operator<<(QDebug debug, const GitHubReleaseAsset& rls); +QDebug operator<<(QDebug debug, const GitHubRelease& rls); + diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 0e6620085..70ff1f81d 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -33,8 +33,8 @@ #include #include -#include #include +#include #include #include @@ -128,17 +128,16 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar QCommandLineParser parser; parser.setApplicationDescription(QObject::tr("An auto-updater for Prism Launcher")); - parser.addOptions({ { { "d", "dir" }, "Use a custom path as application root (use '.' for current directory).", "directory" }, - { { "I", "install-version" }, "Install a spesfic version.", "version name" }, - { { "U", "update-url" }, "Update from the spesified repo.", "github repo url" }, - { { "e", "executable" }, "Path to the prismluancher executable.", "path" }, + parser.addOptions({ { { "d", "dir" }, tr("Use a custom path as application root (use '.' for current directory)."), tr("directory") }, + { { "I", "install-version" }, "Install a spesfic version.", tr("version name") }, + { { "U", "update-url" }, tr("Update from the spesified repo."), tr("github repo url") }, { { "c", "check-only" }, - "Only check if an update is needed. Exit status 100 if true, 0 if false (or non 0 if there was an error)." }, - { { "F", "force" }, "Force an update, even if one is not needed." }, - { { "l", "list" }, "List avalible releases." }, - { "debug", "Log debug to console." }, - { { "L", "latest" }, "Update to the latest avalible version." }, - { { "D", "allow-downgrade" }, "Allow the updater to downgrade to previous verisons." } }); + tr("Only check if an update is needed. Exit status 100 if true, 0 if false (or non 0 if there was an error).") }, + { { "F", "force" }, tr("Force an update, even if one is not needed.") }, + { { "l", "list" }, tr("List avalible releases.") }, + { "debug", tr("Log debug to console.") }, + { { "L", "latest" }, tr("Update to the latest avalible version.") }, + { { "D", "allow-downgrade" }, tr("Allow the updater to downgrade to previous verisons.") } }); parser.addHelpOption(); parser.addVersionOption(); @@ -146,13 +145,25 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar logToConsole = parser.isSet("debug"); - auto prism_executable = parser.value("executable"); - if (prism_executable.isEmpty()) { - prism_executable = QCoreApplication::applicationDirPath() + "/" + BuildConfig.LAUNCHER_APP_BINARY_NAME; + auto prism_executable = QCoreApplication::applicationDirPath() + "/" + BuildConfig.LAUNCHER_APP_BINARY_NAME; #if defined(Q_OS_WIN32) - prism_executable += ".exe"; + prism_executable += ".exe"; #endif + + if (BuildConfig.BUILD_PLATFORM.toLower() == "macos") + showFatalErrorMessage(tr("MacOS Not Supported"), tr("The updater does not support instaltions on MacOS")); + + if (!QFileInfo(prism_executable).isFile()) + showFatalErrorMessage(tr("Unsupprted Instaltion"), tr("The updater can not find the main exacutable.")); + + if (prism_executable.startsWith("/tmp/.mount_")) { + m_appimage = true; + m_appimagePath = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE")); + if (m_appimagePath.isEmpty()) + showFatalErrorMessage(tr("Unsupprted Instaltion"), + tr("Updater is running as misconfigured AppImage? ($APPIMAGE environment variable is missing)")); } + m_prismExecutable = prism_executable; auto prism_update_url = parser.value("update-url"); @@ -210,7 +221,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar } #endif } - + { // setup logging static const QString logBase = BuildConfig.LAUNCHER_NAME + "Updater" + (m_checkOnly ? "-CheckOnly" : "") + "-%0.log"; auto moveFile = [](const QString& oldName, const QString& newName) { @@ -226,13 +237,13 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar logFile = std::unique_ptr(new QFile(logBase.arg(0))); if (!logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { - showFatalErrorMessage("The launcher data folder is not writable!", - QString("The launcher couldn't create a log file - the data folder is not writable.\n" - "\n" - "Make sure you have write permissions to the data folder.\n" - "(%1)\n" - "\n" - "The launcher cannot continue until you fix this problem.") + showFatalErrorMessage(tr("The launcher data folder is not writable!"), + tr("The launcher couldn't create a log file - the data folder is not writable.\n" + "\n" + "Make sure you have write permissions to the data folder.\n" + "(%1)\n" + "\n" + "The launcher cannot continue until you fix this problem.") .arg(dataPath)); return; } @@ -296,8 +307,6 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar qDebug() << "<> Log initialized."; } - - { // log debug program info qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME) << "Updater" << ", (c) 2022-2023 " << qPrintable(QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", ")); @@ -316,7 +325,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar qDebug() << "<> Paths set."; } - { // network + { // network m_network = makeShared(new QNetworkAccessManager()); qDebug() << "Detecting proxy settings..."; QNetworkProxy proxy = QNetworkProxy::applicationProxy(); @@ -341,7 +350,6 @@ PrismUpdaterApp::~PrismUpdaterApp() FreeConsole(); } #endif - } void PrismUpdaterApp::fail(const QString& reason) @@ -477,17 +485,79 @@ GitHubRelease PrismUpdaterApp::selectRelease() SelectReleaseDialog dlg(Version(m_prismVerison), releases); auto result = dlg.exec(); + if (result == QDialog::Rejected) { + return {}; + } GitHubRelease release = dlg.selectedRelease(); + + return release; +} + +QList PrismUpdaterApp::validReleaseArtifacts(const GitHubRelease& release) +{ + QList valid; + + qDebug() << "Selecting best asset from" << release.tag_name << "for platfom" << BuildConfig.BUILD_PLATFORM << "portable:" << m_portable; + if (BuildConfig.BUILD_PLATFORM.isEmpty()) + qWarning() << "Build platform is not set!"; + for (auto asset : release.assets) { + if (!m_appimage && asset.name.toLower().endsWith("appimage")) + continue; + else if (m_appimage && !asset.name.toLower().endsWith("appimage")) + continue; + + bool for_platform = !BuildConfig.BUILD_PLATFORM.isEmpty() && asset.name.toLower().contains(BuildConfig.BUILD_PLATFORM.toLower()); + bool for_portable = asset.name.toLower().contains("portable"); + if (((m_portable && for_portable) || (!m_portable && !for_portable)) && for_platform) { + valid.append(asset); + } + } + return valid; +} + +GitHubReleaseAsset PrismUpdaterApp::selectAsset(const QList& assets) +{ + SelectReleaseAssetDialog dlg(assets); + auto result = dlg.exec(); + if (result == QDialog::Rejected) { return {}; } - return release; + GitHubReleaseAsset asset = dlg.selectedAsset(); + return asset; } void PrismUpdaterApp::performUpdate(const GitHubRelease& release) { qDebug() << "Updating to" << release.tag_name; + auto valid_assets = validReleaseArtifacts(release); + qDebug() << "vallid release assets:" << valid_assets; + + GitHubReleaseAsset selected_asset; + if (valid_assets.isEmpty()) { + showFatalErrorMessage(tr("No Valid Release Assets"), + tr("Github release %1 has no valid assets for this platform: %2") + .arg(release.tag_name) + .arg(tr("%1 portable: %2").arg(BuildConfig.BUILD_PLATFORM).arg(m_portable))); + return; + } else if (valid_assets.length() > 1) { + selected_asset = selectAsset(valid_assets); + } else { + selected_asset = valid_assets.takeFirst(); + } + + if (! selected_asset.isValid()) { + showFatalErrorMessage("No version selected.", "No version was selected."); + return; + } + + qDebug() << "will intall" << selected_asset; +} + +QFileInfo PrismUpdaterApp::downloadAsset(const GitHubReleaseAsset& asset) +{ + // TODO! (researching what to do with appimages) } void PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) @@ -562,14 +632,12 @@ void PrismUpdaterApp::downloadReleasePage(const QString& api_url, int page) }); connect(download.get(), &Net::Download::failed, this, &PrismUpdaterApp::downloadError); - m_current_task.reset(download); - connect(download.get(), &Net::Download::finished, this, [this](){ + connect(download.get(), &Net::Download::finished, this, [this]() { qDebug() << "Download" << m_current_task->getUid().toString() << "finsihed"; m_current_task.reset(); m_current_url = ""; }); - QCoreApplication::processEvents(); diff --git a/launcher/updater/prismupdater/PrismUpdater.h b/launcher/updater/prismupdater/PrismUpdater.h index 52bb7e71b..1a461fcc8 100644 --- a/launcher/updater/prismupdater/PrismUpdater.h +++ b/launcher/updater/prismupdater/PrismUpdater.h @@ -39,7 +39,6 @@ #include "QObjectPtr.h" #include "net/Download.h" - #define PRISM_EXTERNAL_EXE #include "FileSystem.h" @@ -65,25 +64,34 @@ class PrismUpdaterApp : public QApplication { void downloadReleasePage(const QString& api_url, int page); int parseReleasePage(const QByteArray* responce); - GitHubRelease getLatestRelease(); + bool needUpdate(const GitHubRelease& release); + + GitHubRelease getLatestRelease(); GitHubRelease selectRelease(); - void performUpdate(const GitHubRelease& release); - void printReleases(); QList newerReleases(); QList nonDraftReleases(); + void printReleases(); + + QList validReleaseArtifacts(const GitHubRelease& release); + GitHubReleaseAsset selectAsset(const QList& assets); + void performUpdate(const GitHubRelease& release); + + QFileInfo downloadAsset(const GitHubReleaseAsset& asset); + public slots: void downloadError(QString reason); private: - const QString& root() { return m_rootPath; } bool isPortable() { return m_portable; } QString m_rootPath; bool m_portable = false; + bool m_appimage = false; + QString m_appimagePath; QString m_prismExecutable; QUrl m_prismRepoUrl; Version m_userSelectedVersion; diff --git a/launcher/updater/prismupdater/UpdaterDialogs.cpp b/launcher/updater/prismupdater/UpdaterDialogs.cpp index 5949194b2..72fdf3404 100644 --- a/launcher/updater/prismupdater/UpdaterDialogs.cpp +++ b/launcher/updater/prismupdater/UpdaterDialogs.cpp @@ -76,3 +76,71 @@ void SelectReleaseDialog::selectionChanged(QTreeWidgetItem* current, QTreeWidget ui->changelogTextBrowser->setHtml(body); } + + +SelectReleaseAssetDialog::SelectReleaseAssetDialog( const QList& assets, QWidget* parent) + : QDialog(parent), m_assets(assets), ui(new Ui::SelectReleaseDialog) +{ + ui->setupUi(this); + + ui->changelogTextBrowser->setOpenExternalLinks(true); + ui->changelogTextBrowser->setLineWrapMode(QTextBrowser::LineWrapMode::WidgetWidth); + ui->changelogTextBrowser->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded); + + ui->versionsTree->setColumnCount(2); + + ui->versionsTree->header()->setSectionResizeMode(0, QHeaderView::Stretch); + ui->versionsTree->header()->setSectionResizeMode(1, QHeaderView::Stretch); + ui->versionsTree->setHeaderLabels({tr("Verison"), tr("Published Date")}); + ui->versionsTree->header()->setStretchLastSection(false); + + ui->eplainLabel->setText(tr("Select a version to install.")); + + ui->changelogTextBrowser->setHidden(true); + + loadAssets(); + + connect(ui->versionsTree, &QTreeWidget::currentItemChanged, this, &SelectReleaseAssetDialog::selectionChanged); + + connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &SelectReleaseAssetDialog::accept); + connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &SelectReleaseAssetDialog::reject); +} + +SelectReleaseAssetDialog::~SelectReleaseAssetDialog() +{ + delete ui; +} + +void SelectReleaseAssetDialog::loadAssets() +{ + for (auto rls : m_assets) { + appendAsset(rls); + } +} + +void SelectReleaseAssetDialog::appendAsset(GitHubReleaseAsset const& asset) +{ + auto rls_item = new QTreeWidgetItem(ui->versionsTree); + rls_item->setText(0, asset.name); + rls_item->setExpanded(true); + rls_item->setText(1, asset.updated_at.toString()); + rls_item->setData(0, Qt::UserRole, QVariant(asset.id)); + + ui->versionsTree->addTopLevelItem(rls_item); +} + +GitHubReleaseAsset SelectReleaseAssetDialog::getAsset(QTreeWidgetItem* item) { + int id = item->data(0, Qt::UserRole).toInt(); + GitHubReleaseAsset selected_asset; + for (auto asset: m_assets) { + if (asset.id == id) + selected_asset = asset; + } + return selected_asset; +} + +void SelectReleaseAssetDialog::selectionChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous) +{ + GitHubReleaseAsset asset = getAsset(current); + m_selectedAsset = asset; +} diff --git a/launcher/updater/prismupdater/UpdaterDialogs.h b/launcher/updater/prismupdater/UpdaterDialogs.h index 1449befbd..c5e31b5a4 100644 --- a/launcher/updater/prismupdater/UpdaterDialogs.h +++ b/launcher/updater/prismupdater/UpdaterDialogs.h @@ -31,3 +31,23 @@ class SelectReleaseDialog : public QDialog { Ui::SelectReleaseDialog* ui; }; + +class SelectReleaseAssetDialog : public QDialog { + Q_OBJECT + public: + explicit SelectReleaseAssetDialog(const QList& assets, QWidget* parent = 0); + ~SelectReleaseAssetDialog(); + + void loadAssets(); + void appendAsset(GitHubReleaseAsset const& asset); + GitHubReleaseAsset selectedAsset() { return m_selectedAsset; } + private slots: + GitHubReleaseAsset getAsset(QTreeWidgetItem* item); + void selectionChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous); + + protected: + QList m_assets; + GitHubReleaseAsset m_selectedAsset; + + Ui::SelectReleaseDialog* ui; +}; From 50d5eb0621b0523db175672a511fd711d4018bac Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 4 Jun 2023 20:58:28 -0700 Subject: [PATCH 051/268] feat(updater): download new & back up old Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/CMakeLists.txt | 2 +- .../updater/prismupdater/PrismUpdater.cpp | 180 +++++++++++++++--- launcher/updater/prismupdater/PrismUpdater.h | 13 +- 3 files changed, 166 insertions(+), 29 deletions(-) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index d9bd577a0..41a916d12 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -1228,7 +1228,7 @@ install(TARGETS ${Launcher_Name} FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime ) -if(WIN32 OR (DEFINED Launcher_BUILD_UPDATER AND Launcher_BUILD_UPDATER) ) +if(NOT APPLE OR (DEFINED Launcher_BUILD_UPDATER AND Launcher_BUILD_UPDATER) ) # Updater add_library(prism_updater_logic STATIC ${PRISMUPDATER_SOURCES} ${TASKS_SOURCES} ${PRISMUPDATER_UI}) target_include_directories(prism_updater_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 70ff1f81d..8df19bf80 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -22,6 +22,7 @@ #include "PrismUpdater.h" #include "BuildConfig.h" +#include "ui/dialogs/ProgressDialog.h" #include #include @@ -136,7 +137,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar { { "F", "force" }, tr("Force an update, even if one is not needed.") }, { { "l", "list" }, tr("List avalible releases.") }, { "debug", tr("Log debug to console.") }, - { { "L", "latest" }, tr("Update to the latest avalible version.") }, + { { "S", "select-ui" }, tr("Select the version to install with a GUI.") }, { { "D", "allow-downgrade" }, tr("Allow the updater to downgrade to previous verisons.") } }); parser.addHelpOption(); @@ -145,10 +146,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar logToConsole = parser.isSet("debug"); - auto prism_executable = QCoreApplication::applicationDirPath() + "/" + BuildConfig.LAUNCHER_APP_BINARY_NAME; -#if defined(Q_OS_WIN32) - prism_executable += ".exe"; -#endif + auto prism_executable = QCoreApplication::applicationFilePath(); if (BuildConfig.BUILD_PLATFORM.toLower() == "macos") showFatalErrorMessage(tr("MacOS Not Supported"), tr("The updater does not support instaltions on MacOS")); @@ -157,13 +155,15 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar showFatalErrorMessage(tr("Unsupprted Instaltion"), tr("The updater can not find the main exacutable.")); if (prism_executable.startsWith("/tmp/.mount_")) { - m_appimage = true; + m_isAppimage = true; m_appimagePath = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE")); if (m_appimagePath.isEmpty()) showFatalErrorMessage(tr("Unsupprted Instaltion"), tr("Updater is running as misconfigured AppImage? ($APPIMAGE environment variable is missing)")); } + m_isFlatpak = DesktopServices::isFlatpak(); + m_prismExecutable = prism_executable; auto prism_update_url = parser.value("update-url"); @@ -178,7 +178,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar if (!user_version.isEmpty()) { m_userSelectedVersion = Version(user_version); } - m_updateLatest = parser.isSet("latest"); + m_selectUI = parser.isSet("select-ui"); m_allowDowngrade = parser.isSet("allow-downgrade"); QString origcwdPath = QDir::currentPath(); @@ -217,7 +217,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) { dataPath = m_rootPath; adjustedBy = "Portable data path"; - m_portable = true; + m_isPortable = true; } #endif } @@ -411,6 +411,24 @@ void PrismUpdaterApp::run() return exit(0); } + if (m_isFlatpak) { + showFatalErrorMessage(tr("Updating flatpack not supported"), tr("Actions outside of checking if an update is avalible are not " + "supported when running the flatpak verison of Prism Launcher.")); + return; + } + if (m_isAppimage) { + bool result = true; + if (need_update) + result = callAppimageUpdate(); + return exit(result ? 0 : 1); + } + + if (BuildConfig.BUILD_PLATFORM.toLower() == "linux" && !m_isPortable) { + showFatalErrorMessage(tr("Updating Not Supported"), + tr("Updating non-portable linux instalations is not supported. Please use your system package manager")); + return; + } + if (need_update || m_forceUpdate || !m_userSelectedVersion.isEmpty()) { GitHubRelease update_release = latest; if (!m_userSelectedVersion.isEmpty()) { @@ -428,7 +446,7 @@ void PrismUpdaterApp::run() QString("Can not find a github relase for user spesified verison %1").arg(m_userSelectedVersion.toString())); return; } - } else if (!m_updateLatest) { + } else if (m_selectUI) { update_release = selectRelease(); if (!update_release.isValid()) { showFatalErrorMessage("No version selected.", "No version was selected."); @@ -497,18 +515,19 @@ QList PrismUpdaterApp::validReleaseArtifacts(const GitHubRel { QList valid; - qDebug() << "Selecting best asset from" << release.tag_name << "for platfom" << BuildConfig.BUILD_PLATFORM << "portable:" << m_portable; + qDebug() << "Selecting best asset from" << release.tag_name << "for platfom" << BuildConfig.BUILD_PLATFORM + << "portable:" << m_isPortable; if (BuildConfig.BUILD_PLATFORM.isEmpty()) qWarning() << "Build platform is not set!"; for (auto asset : release.assets) { - if (!m_appimage && asset.name.toLower().endsWith("appimage")) + if (!m_isAppimage && asset.name.toLower().endsWith("appimage")) continue; - else if (m_appimage && !asset.name.toLower().endsWith("appimage")) + else if (m_isAppimage && !asset.name.toLower().endsWith("appimage")) continue; bool for_platform = !BuildConfig.BUILD_PLATFORM.isEmpty() && asset.name.toLower().contains(BuildConfig.BUILD_PLATFORM.toLower()); bool for_portable = asset.name.toLower().contains("portable"); - if (((m_portable && for_portable) || (!m_portable && !for_portable)) && for_platform) { + if (((m_isPortable && for_portable) || (!m_isPortable && !for_portable)) && for_platform) { valid.append(asset); } } @@ -536,35 +555,148 @@ void PrismUpdaterApp::performUpdate(const GitHubRelease& release) GitHubReleaseAsset selected_asset; if (valid_assets.isEmpty()) { - showFatalErrorMessage(tr("No Valid Release Assets"), - tr("Github release %1 has no valid assets for this platform: %2") - .arg(release.tag_name) - .arg(tr("%1 portable: %2").arg(BuildConfig.BUILD_PLATFORM).arg(m_portable))); - return; + return showFatalErrorMessage(tr("No Valid Release Assets"), + tr("Github release %1 has no valid assets for this platform: %2") + .arg(release.tag_name) + .arg(tr("%1 portable: %2").arg(BuildConfig.BUILD_PLATFORM).arg(m_isPortable))); } else if (valid_assets.length() > 1) { selected_asset = selectAsset(valid_assets); } else { selected_asset = valid_assets.takeFirst(); } - if (! selected_asset.isValid()) { - showFatalErrorMessage("No version selected.", "No version was selected."); - return; + if (!selected_asset.isValid()) { + return showFatalErrorMessage(tr("No version selected."), tr("No version was selected.")); } - qDebug() << "will intall" << selected_asset; + qDebug() << "will install" << selected_asset; + auto file = downloadAsset(selected_asset); + + if (!file.exists()) { + return showFatalErrorMessage(tr("Failed to Download"), tr("Failed to download the selected asset.")); + } + + performInstall(file); } QFileInfo PrismUpdaterApp::downloadAsset(const GitHubReleaseAsset& asset) { - // TODO! (researching what to do with appimages) + auto temp_dir = QDir::tempPath(); + auto file_url = QUrl(asset.browser_download_url); + auto out_file_path = FS::PathCombine(temp_dir, file_url.fileName()); + + auto download = Net::Download::makeFile(file_url, out_file_path); + + auto progress_dialog = ProgressDialog(); + + if (progress_dialog.execWithTask(download.get()) == QDialog::Rejected) + showFatalErrorMessage(tr("Download Aborted"), tr("Download of %1 aborted by user").arg(file_url.toString())); + + QFileInfo out_file(out_file_path); + return out_file; +} + +bool PrismUpdaterApp::callAppimageUpdate() +{ + QProcess proc = QProcess(); + proc.setProgram("AppImageUpdate"); + proc.setArguments({ QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE")) }); + return proc.startDetached(); +} + +void PrismUpdaterApp::performInstall(QFileInfo file) +{ + // TODO setup marker file + if (m_isPortable) { + unpackAndInstall(file); + } else { + QProcess proc = QProcess(); + proc.setProgram(file.absoluteFilePath()); + bool result = proc.startDetached(); + exit(result ? 0 : 1); + } +} + +void PrismUpdaterApp::unpackAndInstall(QFileInfo to_install_file) +{ + backupAppDir(); + // TODO: uppack (rename access failures) + +} + +void PrismUpdaterApp::backupAppDir() +{ + auto manifest_path = FS::PathCombine(QCoreApplication::applicationDirPath(), "manifest.txt"); + QFileInfo manifest(manifest_path); + + QStringList file_list; + if (manifest.isFile()) { + // load manifest from file + try { + auto contents = QString::fromUtf8(FS::read(manifest.absoluteFilePath())); + file_list.append(contents.split('\n')); + } catch (FS::FileSystemException) { + } + } + + if (file_list.isEmpty()) { + // best guess + if (BuildConfig.BUILD_PLATFORM.toLower() == "linux") { + file_list.append({ "PrismLauncher", "bin/*", "share/*", "lib/*" }); + } else { // windows by process of elimination + file_list.append({ + "jars/*", + "prismlauncher.exe", + "prismlauncher_filelink.exe", + "qtlogging.ini", + "imageformats/*", + "iconengines/*", + "platforms/*", + "styles/*", + "styles/*", + "tls/*", + }); + } + file_list.append("portable.txt"); + qDebug() << "manifest.txt empty or missing. makking best guess at files to back up."; + } + qDebug() << "Backing up" << file_list; + + QDir app_dir = QCoreApplication::applicationDirPath(); + auto backup_dir = FS::PathCombine(app_dir.absolutePath(), QStringLiteral("backup_") + m_prismVerison + ":" + m_prismGitCommit); + FS::ensureFolderPathExists(backup_dir); + + for (auto glob : file_list) { + QDirIterator iter(app_dir.absolutePath(), QStringList({ glob }), QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot, + QDirIterator::Subdirectories); + while (iter.hasNext()) { + auto to_bak_file = iter.next(); + auto rel_path = app_dir.relativeFilePath(to_bak_file); + auto bak_path = FS::PathCombine(backup_dir, rel_path); + + if (QFileInfo(to_bak_file).isFile()) { + qDebug() << "Backing up and then removing" << to_bak_file; + FS::ensureFilePathExists(bak_path); + auto result = FS::copy(to_bak_file, bak_path)(); + if (!result) { + qWarning() << "Failed to backup" << to_bak_file << "to" << bak_path; + } else { + if (!FS::deletePath(to_bak_file)) + qWarning() << "Failed to remove" << to_bak_file; + } + } + } + } } void PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) { QProcess proc = QProcess(); proc.start(exe_path, { "-v" }); - proc.waitForFinished(); + if (!proc.waitForStarted(5000)) // wait 5 seconds to start + return showFatalErrorMessage(tr("Failed to Check Version"), tr("Failed to launcher child launcher process to read verison.")); + if (!proc.waitForFinished(5000)) + return showFatalErrorMessage(tr("Failed to Check Version"), tr("Child launcher process failed.")); auto out = proc.readAll(); auto lines = out.split('\n'); if (lines.length() < 2) diff --git a/launcher/updater/prismupdater/PrismUpdater.h b/launcher/updater/prismupdater/PrismUpdater.h index 1a461fcc8..8404969a5 100644 --- a/launcher/updater/prismupdater/PrismUpdater.h +++ b/launcher/updater/prismupdater/PrismUpdater.h @@ -77,8 +77,12 @@ class PrismUpdaterApp : public QApplication { QList validReleaseArtifacts(const GitHubRelease& release); GitHubReleaseAsset selectAsset(const QList& assets); void performUpdate(const GitHubRelease& release); + void performInstall(QFileInfo file); + void unpackAndInstall(QFileInfo file); + void backupAppDir(); QFileInfo downloadAsset(const GitHubReleaseAsset& asset); + bool callAppimageUpdate(); public slots: void downloadError(QString reason); @@ -86,11 +90,12 @@ class PrismUpdaterApp : public QApplication { private: const QString& root() { return m_rootPath; } - bool isPortable() { return m_portable; } + bool isPortable() { return m_isPortable; } QString m_rootPath; - bool m_portable = false; - bool m_appimage = false; + bool m_isPortable = false; + bool m_isAppimage = false; + bool m_isFlatpak = false; QString m_appimagePath; QString m_prismExecutable; QUrl m_prismRepoUrl; @@ -98,7 +103,7 @@ class PrismUpdaterApp : public QApplication { bool m_checkOnly; bool m_forceUpdate; bool m_printOnly; - bool m_updateLatest; + bool m_selectUI; bool m_allowDowngrade; QString m_prismBinaryName; From 44bc60021d0e84b9a62e406c4ea69f0257573e3b Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 18 Jun 2023 22:34:23 -0700 Subject: [PATCH 052/268] feat(updater): unpack portable archive Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/CMakeLists.txt | 5 +- launcher/FileSystem.cpp | 34 ++++ launcher/FileSystem.h | 12 ++ .../updater/prismupdater/PrismUpdater.cpp | 181 ++++++++++++------ launcher/updater/prismupdater/PrismUpdater.h | 12 +- 5 files changed, 186 insertions(+), 58 deletions(-) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 41a916d12..5cdb0383e 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -597,12 +597,15 @@ set(PRISMUPDATER_SOURCES Version.h Version.cpp Markdown.h + + # Zip + MMCZip.h + MMCZip.cpp # Time MMCTime.h MMCTime.cpp - net/ByteArraySink.h net/ChecksumValidator.h net/Download.cpp diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 835ad925d..2a0ca76b5 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -193,6 +193,40 @@ void write(const QString& filename, const QByteArray& data) } } +void appendSafe(const QString& filename, const QByteArray& data) +{ + ensureExists(QFileInfo(filename).dir()); + QByteArray buffer; + try { + buffer = read(filename); + } catch (FileSystemException) { + buffer = QByteArray(); + } + buffer.append(data); + QSaveFile file(filename); + if (!file.open(QSaveFile::WriteOnly)) { + throw FileSystemException("Couldn't open " + filename + " for writing: " + file.errorString()); + } + if (buffer.size() != file.write(buffer)) { + throw FileSystemException("Error writing data to " + filename + ": " + file.errorString()); + } + if (!file.commit()) { + throw FileSystemException("Error while committing data to " + filename + ": " + file.errorString()); + } +} + +void append(const QString& filename, const QByteArray& data) +{ + ensureExists(QFileInfo(filename).dir()); + QFile file(filename); + if (!file.open(QFile::Append)) { + throw FileSystemException("Couldn't open " + filename + " for writing: " + file.errorString()); + } + if (data.size() != file.write(data)) { + throw FileSystemException("Error writing data to " + filename + ": " + file.errorString()); + } +} + QByteArray read(const QString& filename) { QFile file(filename); diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h index cb581d0c5..ca26c1ee7 100644 --- a/launcher/FileSystem.h +++ b/launcher/FileSystem.h @@ -60,6 +60,18 @@ class FileSystemException : public ::Exception { */ void write(const QString& filename, const QByteArray& data); + +/** + * append data to a file safely + */ +void appendSafe(const QString& filename, const QByteArray& data); + +/** + * append data to a file + */ +void append(const QString& filename, const QByteArray& data); + + /** * read data from a file safely\ */ diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 8df19bf80..001ecb10c 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -80,6 +80,8 @@ namespace fs = ghc::filesystem; #include "net/Download.h" #include "net/RawHeaderProxy.h" +#include + /** output to the log file */ void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg) { @@ -103,14 +105,16 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar #if defined Q_OS_WIN32 // attach the parent console if (AttachConsole(ATTACH_PARENT_PROCESS)) { + FILE* _stream; + errno_t err; // if attach succeeds, reopen and sync all the i/o - if (freopen("CON", "w", stdout)) { + if (err = freopen_s(&_stream, "CON", "w", stdout); err == 0) { std::cout.sync_with_stdio(); } - if (freopen("CON", "w", stderr)) { + if (err = freopen_s(&_stream, "CON", "w", stderr); err == 0) { std::cerr.sync_with_stdio(); } - if (freopen("CON", "r", stdin)) { + if (err = freopen_s(&_stream, "CON", "r", stdin); err == 0) { std::cin.sync_with_stdio(); } auto out = GetStdHandle(STD_OUTPUT_HANDLE); @@ -125,20 +129,20 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar setApplicationName(BuildConfig.LAUNCHER_NAME + "Updater"); setApplicationVersion(BuildConfig.printableVersionString() + "\n" + BuildConfig.GIT_COMMIT); - // Commandline parsing + // Command line parsing QCommandLineParser parser; parser.setApplicationDescription(QObject::tr("An auto-updater for Prism Launcher")); parser.addOptions({ { { "d", "dir" }, tr("Use a custom path as application root (use '.' for current directory)."), tr("directory") }, - { { "I", "install-version" }, "Install a spesfic version.", tr("version name") }, - { { "U", "update-url" }, tr("Update from the spesified repo."), tr("github repo url") }, + { { "I", "install-version" }, "Install a specific version.", tr("version name") }, + { { "U", "update-url" }, tr("Update from the specified repo."), tr("github repo url") }, { { "c", "check-only" }, tr("Only check if an update is needed. Exit status 100 if true, 0 if false (or non 0 if there was an error).") }, { { "F", "force" }, tr("Force an update, even if one is not needed.") }, - { { "l", "list" }, tr("List avalible releases.") }, + { { "l", "list" }, tr("List available releases.") }, { "debug", tr("Log debug to console.") }, { { "S", "select-ui" }, tr("Select the version to install with a GUI.") }, - { { "D", "allow-downgrade" }, tr("Allow the updater to downgrade to previous verisons.") } }); + { { "D", "allow-downgrade" }, tr("Allow the updater to downgrade to previous versions.") } }); parser.addHelpOption(); parser.addVersionOption(); @@ -149,16 +153,16 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar auto prism_executable = QCoreApplication::applicationFilePath(); if (BuildConfig.BUILD_PLATFORM.toLower() == "macos") - showFatalErrorMessage(tr("MacOS Not Supported"), tr("The updater does not support instaltions on MacOS")); + showFatalErrorMessage(tr("MacOS Not Supported"), tr("The updater does not support installations on MacOS")); if (!QFileInfo(prism_executable).isFile()) - showFatalErrorMessage(tr("Unsupprted Instaltion"), tr("The updater can not find the main exacutable.")); + showFatalErrorMessage(tr("Unsupported Installation"), tr("The updater can not find the main executable.")); if (prism_executable.startsWith("/tmp/.mount_")) { m_isAppimage = true; m_appimagePath = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE")); if (m_appimagePath.isEmpty()) - showFatalErrorMessage(tr("Unsupprted Instaltion"), + showFatalErrorMessage(tr("Unsupported Installation"), tr("Updater is running as misconfigured AppImage? ($APPIMAGE environment variable is missing)")); } @@ -181,7 +185,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar m_selectUI = parser.isSet("select-ui"); m_allowDowngrade = parser.isSet("allow-downgrade"); - QString origcwdPath = QDir::currentPath(); + QString origCwdPath = QDir::currentPath(); QString binPath = applicationDirPath(); { // find data director @@ -200,22 +204,21 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar } QString adjustedBy; - QString dataPath; // change folder QString dirParam = parser.value("dir"); if (!dirParam.isEmpty()) { - // the dir param. it makes multimc data path point to whatever the user specified + // the dir param. it makes prism launcher data path point to whatever the user specified // on command line adjustedBy = "Command line"; - dataPath = dirParam; + m_dataPath = dirParam; } else { QDir foo(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "..")); - dataPath = foo.absolutePath(); + m_dataPath = foo.absolutePath(); adjustedBy = "Persistent data path"; #ifndef Q_OS_MACOS if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) { - dataPath = m_rootPath; + m_dataPath = m_rootPath; adjustedBy = "Portable data path"; m_isPortable = true; } @@ -230,8 +233,6 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar QFile::remove(oldName); }; - moveFile(logBase.arg(3), logBase.arg(4)); - moveFile(logBase.arg(2), logBase.arg(3)); moveFile(logBase.arg(1), logBase.arg(2)); moveFile(logBase.arg(0), logBase.arg(1)); @@ -244,7 +245,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar "(%1)\n" "\n" "The launcher cannot continue until you fix this problem.") - .arg(dataPath)); + .arg(m_dataPath)); return; } qInstallMessageHandler(appDebugOutput); @@ -266,7 +267,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar bool foundLoggingRules = false; auto logRulesFile = QStringLiteral("qtlogging.ini"); - auto logRulesPath = FS::PathCombine(dataPath, logRulesFile); + auto logRulesPath = FS::PathCombine(m_dataPath, logRulesFile); qDebug() << "Testing" << logRulesPath << "..."; foundLoggingRules = QFile::exists(logRulesPath); @@ -314,7 +315,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT; qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC; if (adjustedBy.size()) { - qDebug() << "Work dir before adjustment : " << origcwdPath; + qDebug() << "Work dir before adjustment : " << origCwdPath; qDebug() << "Work dir after adjustment : " << QDir::currentPath(); qDebug() << "Adjusted by : " << adjustedBy; } else { @@ -394,7 +395,7 @@ void PrismUpdaterApp::run() loadPrismVersionFromExe(m_prismExecutable); m_status = Succeeded; - qDebug() << "Executable reports as:" << m_prismBinaryName << "version:" << m_prismVerison; + qDebug() << "Executable reports as:" << m_prismBinaryName << "version:" << m_prismVersion; qDebug() << "Version major:" << m_prismVersionMajor; qDebug() << "Verison minor:" << m_prismVersionMinor; qDebug() << "Verison channel:" << m_prsimVersionChannel; @@ -412,8 +413,8 @@ void PrismUpdaterApp::run() } if (m_isFlatpak) { - showFatalErrorMessage(tr("Updating flatpack not supported"), tr("Actions outside of checking if an update is avalible are not " - "supported when running the flatpak verison of Prism Launcher.")); + showFatalErrorMessage(tr("Updating flatpack not supported"), tr("Actions outside of checking if an update is available are not " + "supported when running the flatpak version of Prism Launcher.")); return; } if (m_isAppimage) { @@ -425,7 +426,7 @@ void PrismUpdaterApp::run() if (BuildConfig.BUILD_PLATFORM.toLower() == "linux" && !m_isPortable) { showFatalErrorMessage(tr("Updating Not Supported"), - tr("Updating non-portable linux instalations is not supported. Please use your system package manager")); + tr("Updating non-portable linux installations is not supported. Please use your system package manager")); return; } @@ -443,7 +444,7 @@ void PrismUpdaterApp::run() if (!found) { showFatalErrorMessage( "No release for version!", - QString("Can not find a github relase for user spesified verison %1").arg(m_userSelectedVersion.toString())); + QString("Can not find a github release for specified version %1").arg(m_userSelectedVersion.toString())); return; } } else if (m_selectUI) { @@ -481,7 +482,7 @@ QList PrismUpdaterApp::newerReleases() { QList newer; for (auto rls : nonDraftReleases()) { - if (rls.version > m_prismVerison) + if (rls.version > m_prismVersion) newer.append(rls); } return newer; @@ -500,7 +501,7 @@ GitHubRelease PrismUpdaterApp::selectRelease() if (releases.isEmpty()) return {}; - SelectReleaseDialog dlg(Version(m_prismVerison), releases); + SelectReleaseDialog dlg(Version(m_prismVersion), releases); auto result = dlg.exec(); if (result == QDialog::Rejected) { @@ -515,7 +516,7 @@ QList PrismUpdaterApp::validReleaseArtifacts(const GitHubRel { QList valid; - qDebug() << "Selecting best asset from" << release.tag_name << "for platfom" << BuildConfig.BUILD_PLATFORM + qDebug() << "Selecting best asset from" << release.tag_name << "for platform" << BuildConfig.BUILD_PLATFORM << "portable:" << m_isPortable; if (BuildConfig.BUILD_PLATFORM.isEmpty()) qWarning() << "Build platform is not set!"; @@ -549,9 +550,10 @@ GitHubReleaseAsset PrismUpdaterApp::selectAsset(const QList& void PrismUpdaterApp::performUpdate(const GitHubRelease& release) { + m_install_release = release; qDebug() << "Updating to" << release.tag_name; auto valid_assets = validReleaseArtifacts(release); - qDebug() << "vallid release assets:" << valid_assets; + qDebug() << "valid release assets:" << valid_assets; GitHubReleaseAsset selected_asset; if (valid_assets.isEmpty()) { @@ -604,12 +606,32 @@ bool PrismUpdaterApp::callAppimageUpdate() return proc.startDetached(); } +void PrismUpdaterApp::clearUpdateLog() +{ + auto update_log_path = FS::PathCombine(m_dataPath, "prism_launcher_update.log"); + QFile::remove(update_log_path); +} + +void PrismUpdaterApp::logUpdate(const QString& msg) +{ + qDebug() << msg; + auto update_log_path = FS::PathCombine(m_dataPath, "prism_launcher_update.log"); + FS::append(update_log_path, QStringLiteral("%1\n").arg(msg).toUtf8()); +} + void PrismUpdaterApp::performInstall(QFileInfo file) { + auto update_lock_path = FS::PathCombine(m_dataPath, ".prism_launcher_update.lock"); + FS::write(update_lock_path, QStringLiteral("FROM=%1\nTO=%2\n").arg(m_prismVersion).arg(m_install_release.tag_name).toUtf8()); + clearUpdateLog(); + + logUpdate(tr("Updating from %1 to %2").arg(m_prismVersion).arg(m_install_release.tag_name)); // TODO setup marker file if (m_isPortable) { + logUpdate(tr("Updating portable install at %1").arg(applicationDirPath())); unpackAndInstall(file); } else { + logUpdate(tr("Running installer file at %1").arg(file.absoluteFilePath())); QProcess proc = QProcess(); proc.setProgram(file.absoluteFilePath()); bool result = proc.startDetached(); @@ -617,21 +639,24 @@ void PrismUpdaterApp::performInstall(QFileInfo file) } } -void PrismUpdaterApp::unpackAndInstall(QFileInfo to_install_file) +void PrismUpdaterApp::unpackAndInstall(QFileInfo archive) { + logUpdate(tr("Backing up install")); backupAppDir(); - // TODO: uppack (rename access failures) - + auto loc = unpackArchive(archive); + // TODO: unpack (rename access failures) } void PrismUpdaterApp::backupAppDir() { - auto manifest_path = FS::PathCombine(QCoreApplication::applicationDirPath(), "manifest.txt"); + auto manifest_path = FS::PathCombine(applicationDirPath(), "manifest.txt"); QFileInfo manifest(manifest_path); QStringList file_list; if (manifest.isFile()) { // load manifest from file + + logUpdate(tr("Reading manifest from %1").arg(manifest.absoluteFilePath())); try { auto contents = QString::fromUtf8(FS::read(manifest.absoluteFilePath())); file_list.append(contents.split('\n')); @@ -658,12 +683,12 @@ void PrismUpdaterApp::backupAppDir() }); } file_list.append("portable.txt"); - qDebug() << "manifest.txt empty or missing. makking best guess at files to back up."; + logUpdate("manifest.txt empty or missing. making best guess at files to back up."); } - qDebug() << "Backing up" << file_list; + logUpdate(tr("Backing up:\n %1").arg(file_list.join(",\n "))); QDir app_dir = QCoreApplication::applicationDirPath(); - auto backup_dir = FS::PathCombine(app_dir.absolutePath(), QStringLiteral("backup_") + m_prismVerison + ":" + m_prismGitCommit); + auto backup_dir = FS::PathCombine(app_dir.absolutePath(), QStringLiteral("backup_") + m_prismVersion + ":" + m_prismGitCommit); FS::ensureFolderPathExists(backup_dir); for (auto glob : file_list) { @@ -675,26 +700,72 @@ void PrismUpdaterApp::backupAppDir() auto bak_path = FS::PathCombine(backup_dir, rel_path); if (QFileInfo(to_bak_file).isFile()) { - qDebug() << "Backing up and then removing" << to_bak_file; + logUpdate(tr("Backing up and then removing %1").arg(to_bak_file)); FS::ensureFilePathExists(bak_path); auto result = FS::copy(to_bak_file, bak_path)(); if (!result) { - qWarning() << "Failed to backup" << to_bak_file << "to" << bak_path; + logUpdate(tr("Failed to backup %1 to %2").arg(to_bak_file).arg(bak_path)); } else { if (!FS::deletePath(to_bak_file)) - qWarning() << "Failed to remove" << to_bak_file; + logUpdate(tr("Failed to remove %1").arg(to_bak_file)); } } } } } +std::optional PrismUpdaterApp::unpackArchive(QFileInfo archive) +{ + auto temp_extract_path = FS::PathCombine(m_dataPath, "prism_launcher_update_release"); + FS::ensureFolderPathExists(temp_extract_path); + auto tmp_extract_dir = QDir(temp_extract_path); + + if (archive.fileName().endsWith(".zip")) { + auto result = MMCZip::extractDir(archive.absoluteFilePath(), tmp_extract_dir.absolutePath()); + if (result) { + logUpdate( + tr("Extracted the following to \"%1\":\n %2").arg(tmp_extract_dir.absolutePath()).arg(result->join("\n "))); + } else { + logUpdate(tr("Failed to extract %1 to %2").arg(archive.absoluteFilePath()).arg(tmp_extract_dir.absolutePath())); + showFatalErrorMessage("Failed to extract archive", + tr("Failed to extract %1 to %2").arg(archive.absoluteFilePath()).arg(tmp_extract_dir.absolutePath())); + return {}; + } + + } else if (archive.fileName().endsWith(".tar.gz")) { + QString cmd = "tar"; + QStringList args = { "-xvf", archive.absoluteFilePath(), "-C", tmp_extract_dir.absolutePath() }; + logUpdate(tr("Running: `%1 %2`").arg(cmd).arg(args.join(" "))); + QProcess proc = QProcess(); + proc.start(cmd, args); + if (!proc.waitForStarted(5000)) { // wait 5 seconds to start + showFatalErrorMessage(tr("Failed extract archive"), + tr("Failed to launcher child process \"%1 %2\".").arg(cmd).arg(args.join(" "))); + return {}; + } + auto result = proc.waitForFinished(5000); + auto out = proc.readAll(); + logUpdate(out); + if (!result) { + showFatalErrorMessage(tr("Failed to extract archive"), tr("Child process \"%1 %2\" failed.").arg(cmd).arg(args.join(" "))); + return {}; + } + + } else { + logUpdate(tr("Unknown archive format for %1").arg(archive.absoluteFilePath())); + showFatalErrorMessage("Can not extract", QStringLiteral("Unknown archive format %1").arg(archive.absoluteFilePath())); + return {}; + } + + return tmp_extract_dir; +} + void PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) { QProcess proc = QProcess(); proc.start(exe_path, { "-v" }); if (!proc.waitForStarted(5000)) // wait 5 seconds to start - return showFatalErrorMessage(tr("Failed to Check Version"), tr("Failed to launcher child launcher process to read verison.")); + return showFatalErrorMessage(tr("Failed to Check Version"), tr("Failed to launcher child launcher process to read version.")); if (!proc.waitForFinished(5000)) return showFatalErrorMessage(tr("Failed to Check Version"), tr("Child launcher process failed.")); auto out = proc.readAll(); @@ -707,7 +778,7 @@ void PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) return; m_prismBinaryName = first_parts.takeFirst(); auto version = first_parts.takeFirst(); - m_prismVerison = version; + m_prismVersion = version; if (version.contains('-')) { auto index = version.indexOf('-'); m_prsimVersionChannel = version.mid(index + 1); @@ -742,20 +813,20 @@ void PrismUpdaterApp::downloadReleasePage(const QString& api_url, int page) { int per_page = 30; auto page_url = QString("%1?per_page=%2&page=%3").arg(api_url).arg(QString::number(per_page)).arg(QString::number(page)); - auto responce = std::make_shared(); - auto download = Net::Download::makeByteArray(page_url, responce.get()); + auto response = std::make_shared(); + auto download = Net::Download::makeByteArray(page_url, response.get()); download->setNetwork(m_network); m_current_url = page_url; - auto githup_api_headers = new Net::RawHeaderProxy(); - githup_api_headers->addHeaders({ + auto github_api_headers = new Net::RawHeaderProxy(); + github_api_headers->addHeaders({ { "Accept", "application/vnd.github+json" }, { "X-GitHub-Api-Version", "2022-11-28" }, }); - download->addHeaderProxy(githup_api_headers); + download->addHeaderProxy(github_api_headers); - connect(download.get(), &Net::Download::succeeded, this, [this, responce, per_page, api_url, page]() { - int num_found = parseReleasePage(responce.get()); + connect(download.get(), &Net::Download::succeeded, this, [this, response, per_page, api_url, page]() { + int num_found = parseReleasePage(response.get()); if (!(num_found < per_page)) { // there may be more, fetch next page downloadReleasePage(api_url, page + 1); } else { @@ -766,7 +837,7 @@ void PrismUpdaterApp::downloadReleasePage(const QString& api_url, int page) m_current_task.reset(download); connect(download.get(), &Net::Download::finished, this, [this]() { - qDebug() << "Download" << m_current_task->getUid().toString() << "finsihed"; + qDebug() << "Download" << m_current_task->getUid().toString() << "finished"; m_current_task.reset(); m_current_url = ""; }); @@ -776,13 +847,13 @@ void PrismUpdaterApp::downloadReleasePage(const QString& api_url, int page) QMetaObject::invokeMethod(download.get(), &Task::start, Qt::QueuedConnection); } -int PrismUpdaterApp::parseReleasePage(const QByteArray* responce) +int PrismUpdaterApp::parseReleasePage(const QByteArray* response) { - if (responce->isEmpty()) // empty page + if (response->isEmpty()) // empty page return 0; int num_releases = 0; try { - auto doc = Json::requireDocument(*responce); + auto doc = Json::requireDocument(*response); auto release_list = Json::requireArray(doc); for (auto release_json : release_list) { auto release_obj = Json::requireObject(release_json); @@ -817,7 +888,7 @@ int PrismUpdaterApp::parseReleasePage(const QByteArray* responce) } } catch (Json::JsonException& e) { auto err_msg = - QString("Failed to parse releases from github: %1\n%2").arg(e.what()).arg(QString::fromStdString(responce->toStdString())); + QString("Failed to parse releases from github: %1\n%2").arg(e.what()).arg(QString::fromStdString(response->toStdString())); fail(err_msg); } return num_releases; diff --git a/launcher/updater/prismupdater/PrismUpdater.h b/launcher/updater/prismupdater/PrismUpdater.h index 8404969a5..af7af7dee 100644 --- a/launcher/updater/prismupdater/PrismUpdater.h +++ b/launcher/updater/prismupdater/PrismUpdater.h @@ -63,7 +63,7 @@ class PrismUpdaterApp : public QApplication { void loadPrismVersionFromExe(const QString& exe_path); void downloadReleasePage(const QString& api_url, int page); - int parseReleasePage(const QByteArray* responce); + int parseReleasePage(const QByteArray* response); bool needUpdate(const GitHubRelease& release); @@ -80,6 +80,7 @@ class PrismUpdaterApp : public QApplication { void performInstall(QFileInfo file); void unpackAndInstall(QFileInfo file); void backupAppDir(); + std::optional unpackArchive(QFileInfo file); QFileInfo downloadAsset(const GitHubReleaseAsset& asset); bool callAppimageUpdate(); @@ -92,7 +93,12 @@ class PrismUpdaterApp : public QApplication { bool isPortable() { return m_isPortable; } + void clearUpdateLog(); + void logUpdate(const QString& msg); + + QString m_rootPath; + QString m_dataPath; bool m_isPortable = false; bool m_isAppimage = false; bool m_isFlatpak = false; @@ -107,12 +113,14 @@ class PrismUpdaterApp : public QApplication { bool m_allowDowngrade; QString m_prismBinaryName; - QString m_prismVerison; + QString m_prismVersion; int m_prismVersionMajor; int m_prismVersionMinor; QString m_prsimVersionChannel; QString m_prismGitCommit; + GitHubRelease m_install_release; + Status m_status = Status::Starting; shared_qobject_ptr m_network; QString m_current_url; From 516dd6bd1ad8f9ff939b72f87e88c5803f82eaaf Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sat, 24 Jun 2023 05:02:48 -0700 Subject: [PATCH 053/268] support windows console Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/Application.cpp | 764 +++++++++--------- launcher/CMakeLists.txt | 11 +- .../updater/prismupdater/PrismUpdater.cpp | 287 +++++-- launcher/updater/prismupdater/PrismUpdater.h | 10 +- .../updater/prismupdater/UpdaterDialogs.cpp | 8 +- 5 files changed, 602 insertions(+), 478 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 20eabc8af..f094a8ec9 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -48,27 +48,27 @@ #include "pathmatcher/MultiMatcher.h" #include "pathmatcher/SimplePrefixMatcher.h" #include "settings/INIFile.h" -#include "ui/MainWindow.h" #include "ui/InstanceWindow.h" +#include "ui/MainWindow.h" #include "ui/dialogs/ProgressDialog.h" #include "ui/instanceview/AccessibleInstanceView.h" #include "ui/pages/BasePageProvider.h" -#include "ui/pages/global/LauncherPage.h" -#include "ui/pages/global/MinecraftPage.h" +#include "ui/pages/global/APIPage.h" +#include "ui/pages/global/AccountListPage.h" +#include "ui/pages/global/CustomCommandsPage.h" +#include "ui/pages/global/ExternalToolsPage.h" #include "ui/pages/global/JavaPage.h" #include "ui/pages/global/LanguagePage.h" +#include "ui/pages/global/LauncherPage.h" +#include "ui/pages/global/MinecraftPage.h" #include "ui/pages/global/ProxyPage.h" -#include "ui/pages/global/ExternalToolsPage.h" -#include "ui/pages/global/AccountListPage.h" -#include "ui/pages/global/APIPage.h" -#include "ui/pages/global/CustomCommandsPage.h" -#include "ui/setupwizard/SetupWizard.h" -#include "ui/setupwizard/LanguageWizardPage.h" #include "ui/setupwizard/JavaWizardPage.h" +#include "ui/setupwizard/LanguageWizardPage.h" #include "ui/setupwizard/PasteWizardPage.h" +#include "ui/setupwizard/SetupWizard.h" #include "ui/setupwizard/ThemeWizardPage.h" #include "ui/dialogs/CustomMessageBox.h" @@ -82,20 +82,20 @@ #include #include -#include #include #include +#include #include #include -#include -#include +#include +#include #include #include +#include #include -#include #include +#include #include -#include #include "InstanceList.h" #include "MTPixmapCache.h" @@ -115,32 +115,35 @@ #include "settings/INISettingsObject.h" #include "settings/Setting.h" -#include "translations/TranslationsModel.h" #include "meta/Index.h" +#include "translations/TranslationsModel.h" -#include #include +#include #include #include #ifdef Q_OS_LINUX #include -#include "gamemode_client.h" #include "MangoHud.h" +#include "gamemode_client.h" #endif #ifdef Q_OS_MAC #include "updater/MacSparkleUpdater.h" #endif - #if defined Q_OS_WIN32 #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif -#include +#include +#include #include +#include +#include + #endif #define STRINGIFY(x) #x @@ -153,10 +156,10 @@ PixmapCache* PixmapCache::s_instance = nullptr; namespace { /** This is used so that we can output to the log file in addition to the CLI. */ -void appDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) +void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg) { static std::mutex loggerMutex; - const std::lock_guard lock(loggerMutex); // synchronized, QFile logFile is not thread-safe + const std::lock_guard lock(loggerMutex); // synchronized, QFile logFile is not thread-safe QString out = qFormatLogMessage(type, context, msg); out += QChar::LineFeed; @@ -167,31 +170,110 @@ void appDebugOutput(QtMsgType type, const QMessageLogContext &context, const QSt fflush(stderr); } -} +} // namespace -Application::Application(int &argc, char **argv) : QApplication(argc, argv) +#if defined Q_OS_WIN32 + +// taken from https://stackoverflow.com/a/25927081 +// getting a proper output to console with redirection support on windows is apearently hell +void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr) +{ + // Re-initialize the C runtime "FILE" handles with clean handles bound to "nul". We do this because it has been + // observed that the file number of our standard handle file objects can be assigned internally to a value of -2 + // when not bound to a valid target, which represents some kind of unknown internal invalid state. In this state our + // call to "_dup2" fails, as it specifically tests to ensure that the target file number isn't equal to this value + // before allowing the operation to continue. We can resolve this issue by first "re-opening" the target files to + // use the "nul" device, which will place them into a valid state, after which we can redirect them to our target + // using the "_dup2" function. + if (bindStdIn) { + FILE* dummyFile; + freopen_s(&dummyFile, "nul", "r", stdin); + } + if (bindStdOut) { + FILE* dummyFile; + freopen_s(&dummyFile, "nul", "w", stdout); + } + if (bindStdErr) { + FILE* dummyFile; + freopen_s(&dummyFile, "nul", "w", stderr); + } + + // Redirect unbuffered stdin from the current standard input handle + if (bindStdIn) { + HANDLE stdHandle = GetStdHandle(STD_INPUT_HANDLE); + if (stdHandle != INVALID_HANDLE_VALUE) { + int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); + if (fileDescriptor != -1) { + FILE* file = _fdopen(fileDescriptor, "r"); + if (file != NULL) { + int dup2Result = _dup2(_fileno(file), _fileno(stdin)); + if (dup2Result == 0) { + setvbuf(stdin, NULL, _IONBF, 0); + } + } + } + } + } + + // Redirect unbuffered stdout to the current standard output handle + if (bindStdOut) { + HANDLE stdHandle = GetStdHandle(STD_OUTPUT_HANDLE); + if (stdHandle != INVALID_HANDLE_VALUE) { + int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); + if (fileDescriptor != -1) { + FILE* file = _fdopen(fileDescriptor, "w"); + if (file != NULL) { + int dup2Result = _dup2(_fileno(file), _fileno(stdout)); + if (dup2Result == 0) { + setvbuf(stdout, NULL, _IONBF, 0); + } + } + } + } + } + + // Redirect unbuffered stderr to the current standard error handle + if (bindStdErr) { + HANDLE stdHandle = GetStdHandle(STD_ERROR_HANDLE); + if (stdHandle != INVALID_HANDLE_VALUE) { + int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); + if (fileDescriptor != -1) { + FILE* file = _fdopen(fileDescriptor, "w"); + if (file != NULL) { + int dup2Result = _dup2(_fileno(file), _fileno(stderr)); + if (dup2Result == 0) { + setvbuf(stderr, NULL, _IONBF, 0); + } + } + } + } + } + + // Clear the error state for each of the C++ standard stream objects. We need to do this, as attempts to access the + // standard streams before they refer to a valid target will cause the iostream objects to enter an error state. In + // versions of Visual Studio after 2005, this seems to always occur during startup regardless of whether anything + // has been read from or written to the targets or not. + if (bindStdIn) { + std::wcin.clear(); + std::cin.clear(); + } + if (bindStdOut) { + std::wcout.clear(); + std::cout.clear(); + } + if (bindStdErr) { + std::wcerr.clear(); + std::cerr.clear(); + } +} +#endif + +Application::Application(int& argc, char** argv) : QApplication(argc, argv) { #if defined Q_OS_WIN32 // attach the parent console - if(AttachConsole(ATTACH_PARENT_PROCESS)) - { - // if attach succeeds, reopen and sync all the i/o - if(freopen("CON", "w", stdout)) - { - std::cout.sync_with_stdio(); - } - if(freopen("CON", "w", stderr)) - { - std::cerr.sync_with_stdio(); - } - if(freopen("CON", "r", stdin)) - { - std::cin.sync_with_stdio(); - } - auto out = GetStdHandle (STD_OUTPUT_HANDLE); - DWORD written; - const char * endline = "\n"; - WriteConsole(out, endline, strlen(endline), &written, NULL); + if (AttachConsole(ATTACH_PARENT_PROCESS)) { + BindCrtHandlesToStdHandles(true, true, true); consoleAttached = true; } #endif @@ -210,15 +292,14 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) QCommandLineParser parser; parser.setApplicationDescription(BuildConfig.LAUNCHER_DISPLAYNAME); - parser.addOptions({ - {{"d", "dir"}, "Use a custom path as application root (use '.' for current directory)", "directory"}, - {{"l", "launch"}, "Launch the specified instance (by instance ID)", "instance"}, - {{"s", "server"}, "Join the specified server on launch (only valid in combination with --launch)", "address"}, - {{"a", "profile"}, "Use the account specified by its profile name (only valid in combination with --launch)", "profile"}, - {"alive", "Write a small '" + liveCheckFile + "' file after the launcher starts"}, - {{"I", "import"}, "Import instance from specified zip (local path or URL)", "file"}, - {"show", "Opens the window for the specified instance (by instance ID)", "show"} - }); + parser.addOptions( + { { { "d", "dir" }, "Use a custom path as application root (use '.' for current directory)", "directory" }, + { { "l", "launch" }, "Launch the specified instance (by instance ID)", "instance" }, + { { "s", "server" }, "Join the specified server on launch (only valid in combination with --launch)", "address" }, + { { "a", "profile" }, "Use the account specified by its profile name (only valid in combination with --launch)", "profile" }, + { "alive", "Write a small '" + liveCheckFile + "' file after the launcher starts" }, + { { "I", "import" }, "Import instance from specified zip (local path or URL)", "file" }, + { "show", "Opens the window for the specified instance (by instance ID)", "show" } }); parser.addHelpOption(); parser.addVersionOption(); @@ -231,7 +312,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_instanceIdToShowWindowOf = parser.value("show"); - for (auto zip_path : parser.values("import")){ + for (auto zip_path : parser.values("import")) { m_zipsToImport.append(QUrl::fromLocalFile(QFileInfo(zip_path).absoluteFilePath())); } @@ -240,10 +321,8 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_zipsToImport.append(QUrl::fromLocalFile(QFileInfo(zip_path).absoluteFilePath())); } - // error if --launch is missing with --server or --profile - if((!m_serverToJoin.isEmpty() || !m_profileToUse.isEmpty()) && m_instanceIdToLaunch.isEmpty()) - { + if ((!m_serverToJoin.isEmpty() || !m_profileToUse.isEmpty()) && m_instanceIdToLaunch.isEmpty()) { std::cerr << "--server and --profile can only be used in combination with --launch!" << std::endl; m_status = Application::Failed; return; @@ -255,7 +334,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) { // Root path is used for updates and portable data #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) - QDir foo(FS::PathCombine(binPath, "..")); // typically portable-root or /usr + QDir foo(FS::PathCombine(binPath, "..")); // typically portable-root or /usr m_rootPath = foo.absolutePath(); #elif defined(Q_OS_WIN32) m_rootPath = binPath; @@ -271,15 +350,12 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) QString dataPath; // change folder QString dirParam = parser.value("dir"); - if (!dirParam.isEmpty()) - { + if (!dirParam.isEmpty()) { // the dir param. it makes multimc data path point to whatever the user specified // on command line adjustedBy = "Command line"; dataPath = dirParam; - } - else - { + } else { QDir foo(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "..")); dataPath = foo.absolutePath(); adjustedBy = "Persistent data path"; @@ -293,34 +369,27 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) #endif } - if (!FS::ensureFolderPathExists(dataPath)) - { + if (!FS::ensureFolderPathExists(dataPath)) { showFatalErrorMessage( "The launcher data folder could not be created.", - QString( - "The launcher data folder could not be created.\n" - "\n" - "Make sure you have the right permissions to the launcher data folder and any folder needed to access it.\n" - "(%1)\n" - "\n" - "The launcher cannot continue until you fix this problem." - ).arg(dataPath) - ); + QString("The launcher data folder could not be created.\n" + "\n" + "Make sure you have the right permissions to the launcher data folder and any folder needed to access it.\n" + "(%1)\n" + "\n" + "The launcher cannot continue until you fix this problem.") + .arg(dataPath)); return; } - if (!QDir::setCurrent(dataPath)) - { - showFatalErrorMessage( - "The launcher data folder could not be opened.", - QString( - "The launcher data folder could not be opened.\n" - "\n" - "Make sure you have the right permissions to the launcher data folder.\n" - "(%1)\n" - "\n" - "The launcher cannot continue until you fix this problem." - ).arg(dataPath) - ); + if (!QDir::setCurrent(dataPath)) { + showFatalErrorMessage("The launcher data folder could not be opened.", + QString("The launcher data folder could not be opened.\n" + "\n" + "Make sure you have the right permissions to the launcher data folder.\n" + "(%1)\n" + "\n" + "The launcher cannot continue until you fix this problem.") + .arg(dataPath)); return; } @@ -334,17 +403,15 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) // FIXME: you can run the same binaries with multiple data dirs and they won't clash. This could cause issues for updates. m_peerInstance = new LocalPeer(this, appID); connect(m_peerInstance, &LocalPeer::messageReceived, this, &Application::messageReceived); - if(m_peerInstance->isClient()) { + if (m_peerInstance->isClient()) { int timeout = 2000; - if(m_instanceIdToLaunch.isEmpty()) - { + if (m_instanceIdToLaunch.isEmpty()) { ApplicationMessage activate; activate.command = "activate"; m_peerInstance->sendMessage(activate.serialize(), timeout); - if(!m_zipsToImport.isEmpty()) - { + if (!m_zipsToImport.isEmpty()) { for (auto zip_url : m_zipsToImport) { ApplicationMessage import; import.command = "import"; @@ -352,19 +419,15 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_peerInstance->sendMessage(import.serialize(), timeout); } } - } - else - { + } else { ApplicationMessage launch; launch.command = "launch"; launch.args["id"] = m_instanceIdToLaunch; - if(!m_serverToJoin.isEmpty()) - { + if (!m_serverToJoin.isEmpty()) { launch.args["server"] = m_serverToJoin; } - if(!m_profileToUse.isEmpty()) - { + if (!m_profileToUse.isEmpty()) { launch.args["profile"] = m_profileToUse; } m_peerInstance->sendMessage(launch.serialize(), timeout); @@ -408,40 +471,47 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) qInstallMessageHandler(appDebugOutput); qSetMessagePattern( - "%{time process}" " " - "%{if-debug}D%{endif}" "%{if-info}I%{endif}" "%{if-warning}W%{endif}" "%{if-critical}C%{endif}" "%{if-fatal}F%{endif}" - " " "|" " " - "%{if-category}[%{category}]: %{endif}" - "%{message}"); - + "%{time process}" + " " + "%{if-debug}D%{endif}" + "%{if-info}I%{endif}" + "%{if-warning}W%{endif}" + "%{if-critical}C%{endif}" + "%{if-fatal}F%{endif}" + " " + "|" + " " + "%{if-category}[%{category}]: %{endif}" + "%{message}"); + bool foundLoggingRules = false; - + auto logRulesFile = QStringLiteral("qtlogging.ini"); auto logRulesPath = FS::PathCombine(dataPath, logRulesFile); - - qDebug() << "Testing" << logRulesPath << "..."; + + qDebug() << "Testing" << logRulesPath << "..."; foundLoggingRules = QFile::exists(logRulesPath); // search the dataPath() // seach app data standard path - if(!foundLoggingRules && !isPortable() && dirParam.isEmpty()) { + if (!foundLoggingRules && !isPortable() && dirParam.isEmpty()) { logRulesPath = QStandardPaths::locate(QStandardPaths::AppDataLocation, FS::PathCombine("..", logRulesFile)); - if(!logRulesPath.isEmpty()) { + if (!logRulesPath.isEmpty()) { qDebug() << "Found" << logRulesPath << "..."; foundLoggingRules = true; } } // seach root path - if(!foundLoggingRules) { - logRulesPath = FS::PathCombine(m_rootPath, logRulesFile); + if (!foundLoggingRules) { + logRulesPath = FS::PathCombine(m_rootPath, logRulesFile); qDebug() << "Testing" << logRulesPath << "..."; foundLoggingRules = QFile::exists(logRulesPath); } - - if(foundLoggingRules) { + + if (foundLoggingRules) { // load and set logging rules qDebug() << "Loading logging rules from:" << logRulesPath; - QSettings loggingRules(logRulesPath, QSettings::IniFormat); + QSettings loggingRules(logRulesPath, QSettings::IniFormat); loggingRules.beginGroup("Rules"); QStringList rule_names = loggingRules.childKeys(); QStringList rules; @@ -462,48 +532,44 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) bool migrated = false; if (!migrated) - migrated = handleDataMigration(dataPath, FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "../../PolyMC"), "PolyMC", "polymc.cfg"); + 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"); + migrated = handleDataMigration( + dataPath, FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "../../multimc"), "MultiMC", + "multimc.cfg"); } { - - qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME) << ", (c) 2022-2023 " << qPrintable(QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", ")); + qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME) << ", (c) 2022-2023 " + << qPrintable(QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", ")); qDebug() << "Version : " << BuildConfig.printableVersionString(); qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT; qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC; - if (adjustedBy.size()) - { + if (adjustedBy.size()) { qDebug() << "Work dir before adjustment : " << origcwdPath; qDebug() << "Work dir after adjustment : " << QDir::currentPath(); qDebug() << "Adjusted by : " << adjustedBy; - } - else - { + } else { qDebug() << "Work dir : " << QDir::currentPath(); } qDebug() << "Binary path : " << binPath; qDebug() << "Application root path : " << m_rootPath; - if(!m_instanceIdToLaunch.isEmpty()) - { + if (!m_instanceIdToLaunch.isEmpty()) { qDebug() << "ID of instance to launch : " << m_instanceIdToLaunch; } - if(!m_serverToJoin.isEmpty()) - { + if (!m_serverToJoin.isEmpty()) { qDebug() << "Address of server to join :" << m_serverToJoin; } qDebug() << "<> Paths set."; } - if(m_liveCheck) - { + if (m_liveCheck) { QFile check(liveCheckFile); - if(check.open(QIODevice::WriteOnly | QIODevice::Truncate)) - { + if (check.open(QIODevice::WriteOnly | QIODevice::Truncate)) { auto payload = appID.toString().toUtf8(); - if(check.write(payload) == payload.size()) - { + if (check.write(payload) == payload.size()) { check.close(); } else { qWarning() << "Could not write into" << liveCheckFile << "!"; @@ -549,7 +615,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) QString resolvedDefaultMonospace = consoleFontInfo.family(); QFont resolvedFont(resolvedDefaultMonospace); qDebug() << "Detected default console font:" << resolvedDefaultMonospace - << ", substitutions:" << resolvedFont.substitutions().join(','); + << ", substitutions:" << resolvedFont.substitutions().join(','); m_settings->registerSetting("ConsoleFont", resolvedDefaultMonospace); m_settings->registerSetting("ConsoleFontSize", defaultSize); @@ -558,7 +624,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) // Folders m_settings->registerSetting("InstanceDir", "instances"); - m_settings->registerSetting({"CentralModsDir", "ModsDir"}, "mods"); + m_settings->registerSetting({ "CentralModsDir", "ModsDir" }, "mods"); m_settings->registerSetting("IconsDir", "icons"); m_settings->registerSetting("DownloadsDir", QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)); m_settings->registerSetting("DownloadsDirWatchRecursive", false); @@ -576,20 +642,20 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_settings->registerSetting("LogPrePostOutput", true); // Window Size - m_settings->registerSetting({"LaunchMaximized", "MCWindowMaximize"}, false); - m_settings->registerSetting({"MinecraftWinWidth", "MCWindowWidth"}, 854); - m_settings->registerSetting({"MinecraftWinHeight", "MCWindowHeight"}, 480); + m_settings->registerSetting({ "LaunchMaximized", "MCWindowMaximize" }, false); + m_settings->registerSetting({ "MinecraftWinWidth", "MCWindowWidth" }, 854); + m_settings->registerSetting({ "MinecraftWinHeight", "MCWindowHeight" }, 480); // Proxy Settings m_settings->registerSetting("ProxyType", "None"); - m_settings->registerSetting({"ProxyAddr", "ProxyHostName"}, "127.0.0.1"); + m_settings->registerSetting({ "ProxyAddr", "ProxyHostName" }, "127.0.0.1"); m_settings->registerSetting("ProxyPort", 8080); - m_settings->registerSetting({"ProxyUser", "ProxyUsername"}, ""); - m_settings->registerSetting({"ProxyPass", "ProxyPassword"}, ""); + m_settings->registerSetting({ "ProxyUser", "ProxyUsername" }, ""); + m_settings->registerSetting({ "ProxyPass", "ProxyPassword" }, ""); // Memory - m_settings->registerSetting({"MinMemAlloc", "MinMemoryAlloc"}, 512); - m_settings->registerSetting({"MaxMemAlloc", "MaxMemoryAlloc"}, suitableMaxMem()); + m_settings->registerSetting({ "MinMemAlloc", "MinMemoryAlloc" }, 512); + m_settings->registerSetting({ "MaxMemAlloc", "MaxMemoryAlloc" }, suitableMaxMem()); m_settings->registerSetting("PermGen", 128); // Java Settings @@ -631,8 +697,8 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_settings->registerSetting("WrapperCommand", ""); // Custom Commands - m_settings->registerSetting({"PreLaunchCommand", "PreLaunchCmd"}, ""); - m_settings->registerSetting({"PostExitCommand", "PostExitCmd"}, ""); + m_settings->registerSetting({ "PreLaunchCommand", "PreLaunchCmd" }, ""); + m_settings->registerSetting({ "PostExitCommand", "PostExitCmd" }, ""); // The cat m_settings->registerSetting("TheCat", false); @@ -671,8 +737,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) QString pastebinURL = m_settings->get("PastebinURL").toString(); bool userHadDefaultPastebin = pastebinURL == "https://0x0.st"; - if (!pastebinURL.isEmpty() && !userHadDefaultPastebin) - { + if (!pastebinURL.isEmpty() && !userHadDefaultPastebin) { m_settings->set("PastebinType", PasteUpload::PasteType::NullPointer); m_settings->set("PastebinCustomAPIBase", pastebinURL); m_settings->reset("PastebinURL"); @@ -681,8 +746,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) bool ok; int pasteType = m_settings->get("PastebinType").toInt(&ok); // If PastebinType is invalid then reset the related settings. - if (!ok || !(PasteUpload::PasteType::First <= pasteType && pasteType <= PasteUpload::PasteType::Last)) - { + if (!ok || !(PasteUpload::PasteType::First <= pasteType && pasteType <= PasteUpload::PasteType::Last)) { m_settings->reset("PastebinType"); m_settings->reset("PastebinCustomAPIBase"); } @@ -755,8 +819,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) } // initialize the updater - if(BuildConfig.UPDATER_ENABLED) - { + if (BuildConfig.UPDATER_ENABLED) { qDebug() << "Initializing updater"; #ifdef Q_OS_MAC m_updater.reset(new MacSparkleUpdater()); @@ -767,18 +830,11 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) // Instance icons { auto setting = APPLICATION->settings()->getSetting("IconsDir"); - QStringList instFolders = - { - ":/icons/multimc/32x32/instances/", - ":/icons/multimc/50x50/instances/", - ":/icons/multimc/128x128/instances/", - ":/icons/multimc/scalable/instances/" - }; + QStringList instFolders = { ":/icons/multimc/32x32/instances/", ":/icons/multimc/50x50/instances/", + ":/icons/multimc/128x128/instances/", ":/icons/multimc/scalable/instances/" }; m_icons.reset(new IconList(instFolders, setting->get().toString())); - connect(setting.get(), &Setting::SettingChanged,[&](const Setting &, QVariant value) - { - m_icons->directoryChanged(value.toString()); - }); + connect(setting.get(), &Setting::SettingChanged, + [&](const Setting&, QVariant value) { m_icons->directoryChanged(value.toString()); }); qDebug() << "<> Instance icons intialized."; } @@ -792,8 +848,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) // and remember that we have to show him a dialog when the gui starts (if it does so) QString instDir = InstDirSetting->get().toString(); qDebug() << "Instance path : " << instDir; - if (FS::checkProblemticPathJava(QDir(instDir))) - { + if (FS::checkProblemticPathJava(QDir(instDir))) { qWarning() << "Your instance path contains \'!\' and this is known to cause java problems!"; } m_instances.reset(new InstanceList(m_settings, instDir, this)); @@ -843,11 +898,10 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) // now we have network, download translation updates m_translations->downloadIndex(); - //FIXME: what to do with these? + // FIXME: what to do with these? m_profilers.insert("jprofiler", std::shared_ptr(new JProfilerFactory())); m_profilers.insert("jvisualvm", std::shared_ptr(new JVisualVMFactory())); - for (auto profiler : m_profilers.values()) - { + for (auto profiler : m_profilers.values()) { profiler->registerSettings(m_settings); } @@ -857,19 +911,15 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) } #ifdef Q_OS_MACOS - connect(this, &Application::clickedOnDock, [this]() { - this->showMainWindow(); - }); + connect(this, &Application::clickedOnDock, [this]() { this->showMainWindow(); }); #endif - connect(this, &Application::aboutToQuit, [this](){ - if(m_instances) - { + connect(this, &Application::aboutToQuit, [this]() { + if (m_instances) { // save any remaining instance state m_instances->saveNow(); } - if(logFile) - { + if (logFile) { logFile->flush(); logFile->close(); } @@ -879,8 +929,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) updateCapabilities(); - if(createSetupWizard()) - { + if (createSetupWizard()) { return; } @@ -889,29 +938,25 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) bool Application::createSetupWizard() { - bool javaRequired = [&]() - { + bool javaRequired = [&]() { bool ignoreJavaWizard = m_settings->get("IgnoreJavaWizard").toBool(); - if(ignoreJavaWizard) { + if (ignoreJavaWizard) { return false; } QString currentHostName = QHostInfo::localHostName(); QString oldHostName = settings()->get("LastHostname").toString(); - if (currentHostName != oldHostName) - { + if (currentHostName != oldHostName) { settings()->set("LastHostname", currentHostName); return true; } QString currentJavaPath = settings()->get("JavaPath").toString(); QString actualPath = FS::ResolveExecutable(currentJavaPath); - if (actualPath.isNull()) - { + if (actualPath.isNull()) { return true; } return false; }(); - bool languageRequired = [&]() - { + bool languageRequired = [&]() { if (settings()->get("Language").toString().isEmpty()) return true; return false; @@ -920,27 +965,22 @@ bool Application::createSetupWizard() bool themeInterventionRequired = settings()->get("ApplicationTheme") == ""; bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired || themeInterventionRequired; - if(wizardRequired) - { + if (wizardRequired) { m_setupWizard = new SetupWizard(nullptr); - if (languageRequired) - { + if (languageRequired) { m_setupWizard->addPage(new LanguageWizardPage(m_setupWizard)); } - if (javaRequired) - { + if (javaRequired) { m_setupWizard->addPage(new JavaWizardPage(m_setupWizard)); } - if (pasteInterventionRequired) - { + if (pasteInterventionRequired) { m_setupWizard->addPage(new PasteWizardPage(m_setupWizard)); } - if (themeInterventionRequired) - { - settings()->set("ApplicationTheme", QString("system")); // set default theme after going into theme wizard + if (themeInterventionRequired) { + settings()->set("ApplicationTheme", QString("system")); // set default theme after going into theme wizard m_setupWizard->addPage(new ThemeWizardPage(m_setupWizard)); } connect(m_setupWizard, &QDialog::finished, this, &Application::setupWizardFinished); @@ -980,26 +1020,22 @@ void Application::setupWizardFinished(int status) void Application::performMainStartupAction() { m_status = Application::Initialized; - if(!m_instanceIdToLaunch.isEmpty()) - { + if (!m_instanceIdToLaunch.isEmpty()) { auto inst = instances()->getInstanceById(m_instanceIdToLaunch); - if(inst) - { + if (inst) { MinecraftServerTargetPtr serverToJoin = nullptr; MinecraftAccountPtr accountToUse = nullptr; qDebug() << "<> Instance" << m_instanceIdToLaunch << "launching"; - if(!m_serverToJoin.isEmpty()) - { + if (!m_serverToJoin.isEmpty()) { // FIXME: validate the server string serverToJoin.reset(new MinecraftServerTarget(MinecraftServerTarget::parse(m_serverToJoin))); qDebug() << " Launching with server" << m_serverToJoin; } - if(!m_profileToUse.isEmpty()) - { + if (!m_profileToUse.isEmpty()) { accountToUse = accounts()->getAccountByProfileName(m_profileToUse); - if(!accountToUse) { + if (!accountToUse) { return; } qDebug() << " Launching with account" << m_profileToUse; @@ -1009,26 +1045,22 @@ void Application::performMainStartupAction() return; } } - if(!m_instanceIdToShowWindowOf.isEmpty()) - { + if (!m_instanceIdToShowWindowOf.isEmpty()) { auto inst = instances()->getInstanceById(m_instanceIdToShowWindowOf); - if(inst) - { + if (inst) { qDebug() << "<> Showing window of instance " << m_instanceIdToShowWindowOf; showInstanceWindow(inst); return; } } - if(!m_mainWindow) - { + if (!m_mainWindow) { // normal main window showMainWindow(false); qDebug() << "<> Main window shown."; } - if(!m_zipsToImport.isEmpty()) - { + if (!m_zipsToImport.isEmpty()) { qDebug() << "<> Importing from zip:" << m_zipsToImport; - m_mainWindow->processURLs( m_zipsToImport ); + m_mainWindow->processURLs(m_zipsToImport); } } @@ -1046,8 +1078,7 @@ Application::~Application() #if defined Q_OS_WIN32 // Detach from Windows console - if(consoleAttached) - { + if (consoleAttached) { fclose(stdout); fclose(stdin); fclose(stderr); @@ -1058,8 +1089,7 @@ Application::~Application() void Application::messageReceived(const QByteArray& message) { - if(status() != Initialized) - { + if (status() != Initialized) { qDebug() << "Received message" << message << "while still initializing. It will be ignored."; return; } @@ -1067,66 +1097,51 @@ void Application::messageReceived(const QByteArray& message) ApplicationMessage received; received.parse(message); - auto & command = received.command; + auto& command = received.command; - if(command == "activate") - { + if (command == "activate") { showMainWindow(); - } - else if(command == "import") - { + } else if (command == "import") { QString path = received.args["path"]; - if(path.isEmpty()) - { + if (path.isEmpty()) { qWarning() << "Received" << command << "message without a zip path/URL."; return; } m_mainWindow->processURLs({ QUrl::fromLocalFile(QFileInfo(path).absoluteFilePath()) }); - } - else if(command == "launch") - { + } else if (command == "launch") { QString id = received.args["id"]; QString server = received.args["server"]; QString profile = received.args["profile"]; InstancePtr instance; - if(!id.isEmpty()) { + if (!id.isEmpty()) { instance = instances()->getInstanceById(id); - if(!instance) { + if (!instance) { qWarning() << "Launch command requires an valid instance ID. " << id << "resolves to nothing."; return; } - } - else { + } else { qWarning() << "Launch command called without an instance ID..."; return; } MinecraftServerTargetPtr serverObject = nullptr; - if(!server.isEmpty()) { + if (!server.isEmpty()) { serverObject = std::make_shared(MinecraftServerTarget::parse(server)); } MinecraftAccountPtr accountObject; - if(!profile.isEmpty()) { + if (!profile.isEmpty()) { accountObject = accounts()->getAccountByProfileName(profile); - if(!accountObject) { - qWarning() << "Launch command requires the specified profile to be valid. " << profile << "does not resolve to any account."; + if (!accountObject) { + qWarning() << "Launch command requires the specified profile to be valid. " << profile + << "does not resolve to any account."; return; } } - launch( - instance, - true, - false, - nullptr, - serverObject, - accountObject - ); - } - else - { + launch(instance, true, false, nullptr, serverObject, accountObject); + } else { qWarning() << "Received invalid message" << message; } } @@ -1138,8 +1153,7 @@ std::shared_ptr Application::translations() std::shared_ptr Application::javalist() { - if (!m_javalist) - { + if (!m_javalist) { m_javalist.reset(new JavaInstallList()); } return m_javalist; @@ -1167,50 +1181,41 @@ void Application::setIconTheme(const QString& name) QIcon Application::getThemedIcon(const QString& name) { - if(name == "logo") { + if (name == "logo") { return QIcon(":/" + BuildConfig.LAUNCHER_SVGFILENAME); } return QIcon::fromTheme(name); } -bool Application::openJsonEditor(const QString &filename) +bool Application::openJsonEditor(const QString& filename) { const QString file = QDir::current().absoluteFilePath(filename); - if (m_settings->get("JsonEditor").toString().isEmpty()) - { + if (m_settings->get("JsonEditor").toString().isEmpty()) { return DesktopServices::openUrl(QUrl::fromLocalFile(file)); - } - else - { - //return DesktopServices::openFile(m_settings->get("JsonEditor").toString(), file); - return DesktopServices::run(m_settings->get("JsonEditor").toString(), {file}); + } else { + // return DesktopServices::openFile(m_settings->get("JsonEditor").toString(), file); + return DesktopServices::run(m_settings->get("JsonEditor").toString(), { file }); } } -bool Application::launch( - InstancePtr instance, - bool online, - bool demo, - BaseProfilerFactory *profiler, - MinecraftServerTargetPtr serverToJoin, - MinecraftAccountPtr accountToUse -) { - if(m_updateRunning) - { +bool Application::launch(InstancePtr instance, + bool online, + bool demo, + BaseProfilerFactory* profiler, + MinecraftServerTargetPtr serverToJoin, + MinecraftAccountPtr accountToUse) +{ + if (m_updateRunning) { qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed."; - } - else if(instance->canLaunch()) - { - auto & extras = m_instanceExtras[instance->id()]; - auto & window = extras.window; - if(window) - { - if(!window->saveAll()) - { + } else if (instance->canLaunch()) { + auto& extras = m_instanceExtras[instance->id()]; + auto& window = extras.window; + if (window) { + if (!window->saveAll()) { return false; } } - auto & controller = extras.controller; + auto& controller = extras.controller; controller.reset(new LaunchController()); controller->setInstance(instance); controller->setOnline(online); @@ -1218,30 +1223,21 @@ bool Application::launch( controller->setProfiler(profiler); controller->setServerToJoin(serverToJoin); controller->setAccountToUse(accountToUse); - if(window) - { + if (window) { controller->setParentWidget(window); - } - else if(m_mainWindow) - { + } else if (m_mainWindow) { controller->setParentWidget(m_mainWindow); } connect(controller.get(), &LaunchController::succeeded, this, &Application::controllerSucceeded); connect(controller.get(), &LaunchController::failed, this, &Application::controllerFailed); - connect(controller.get(), &LaunchController::aborted, this, [this] { - controllerFailed(tr("Aborted")); - }); + connect(controller.get(), &LaunchController::aborted, this, [this] { controllerFailed(tr("Aborted")); }); addRunningInstance(); controller->start(); return true; - } - else if (instance->isRunning()) - { + } else if (instance->isRunning()) { showInstanceWindow(instance, "console"); return true; - } - else if (instance->canEdit()) - { + } else if (instance->canEdit()) { showInstanceWindow(instance); return true; } @@ -1250,16 +1246,14 @@ bool Application::launch( bool Application::kill(InstancePtr instance) { - if (!instance->isRunning()) - { + if (!instance->isRunning()) { qWarning() << "Attempted to kill instance" << instance->id() << ", which isn't running."; return false; } - auto & extras = m_instanceExtras[instance->id()]; + auto& extras = m_instanceExtras[instance->id()]; // NOTE: copy of the shared pointer keeps it alive auto controller = extras.controller; - if(controller) - { + if (controller) { return controller->abort(); } return true; @@ -1273,23 +1267,20 @@ void Application::closeCurrentWindow() void Application::addRunningInstance() { - m_runningInstances ++; - if(m_runningInstances == 1) - { + m_runningInstances++; + if (m_runningInstances == 1) { emit updateAllowedChanged(false); } } void Application::subRunningInstance() { - if(m_runningInstances == 0) - { + if (m_runningInstances == 0) { qCritical() << "Something went really wrong and we now have less than 0 running instances... WTF"; return; } - m_runningInstances --; - if(m_runningInstances == 0) - { + m_runningInstances--; + if (m_runningInstances == 0) { emit updateAllowedChanged(true); } } @@ -1309,20 +1300,17 @@ void Application::updateIsRunning(bool running) m_updateRunning = running; } - void Application::controllerSucceeded() { - auto controller = qobject_cast(QObject::sender()); - if(!controller) + auto controller = qobject_cast(QObject::sender()); + if (!controller) return; auto id = controller->id(); - auto & extras = m_instanceExtras[id]; + auto& extras = m_instanceExtras[id]; // on success, do... - if (controller->instance()->settings()->get("AutoCloseConsole").toBool()) - { - if(extras.window) - { + if (controller->instance()->settings()->get("AutoCloseConsole").toBool()) { + if (extras.window) { extras.window->close(); } } @@ -1330,8 +1318,7 @@ void Application::controllerSucceeded() subRunningInstance(); // quit when there are no more windows. - if(shouldExitNow()) - { + if (shouldExitNow()) { m_status = Status::Succeeded; exit(0); } @@ -1340,19 +1327,18 @@ void Application::controllerSucceeded() void Application::controllerFailed(const QString& error) { Q_UNUSED(error); - auto controller = qobject_cast(QObject::sender()); - if(!controller) + auto controller = qobject_cast(QObject::sender()); + if (!controller) return; auto id = controller->id(); - auto & extras = m_instanceExtras[id]; + auto& extras = m_instanceExtras[id]; // on failure, do... nothing extras.controller.reset(); subRunningInstance(); // quit when there are no more windows. - if(shouldExitNow()) - { + if (shouldExitNow()) { m_status = Status::Failed; exit(1); } @@ -1360,7 +1346,7 @@ void Application::controllerFailed(const QString& error) void Application::ShowGlobalSettings(class QWidget* parent, QString open_page) { - if(!m_globalSettingsProvider) { + if (!m_globalSettingsProvider) { return; } emit globalSettingsAboutToOpen(); @@ -1374,24 +1360,18 @@ void Application::ShowGlobalSettings(class QWidget* parent, QString open_page) MainWindow* Application::showMainWindow(bool minimized) { - if(m_mainWindow) - { + if (m_mainWindow) { m_mainWindow->setWindowState(m_mainWindow->windowState() & ~Qt::WindowMinimized); m_mainWindow->raise(); m_mainWindow->activateWindow(); - } - else - { + } else { m_mainWindow = new MainWindow(); m_mainWindow->restoreState(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowState").toByteArray())); m_mainWindow->restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowGeometry").toByteArray())); - if(minimized) - { + if (minimized) { m_mainWindow->showMinimized(); - } - else - { + } else { m_mainWindow->show(); } @@ -1403,31 +1383,26 @@ MainWindow* Application::showMainWindow(bool minimized) return m_mainWindow; } -InstanceWindow *Application::showInstanceWindow(InstancePtr instance, QString page) +InstanceWindow* Application::showInstanceWindow(InstancePtr instance, QString page) { - if(!instance) + if (!instance) return nullptr; auto id = instance->id(); - auto & extras = m_instanceExtras[id]; - auto & window = extras.window; + auto& extras = m_instanceExtras[id]; + auto& window = extras.window; - if(window) - { + if (window) { window->raise(); window->activateWindow(); - } - else - { + } else { window = new InstanceWindow(instance); - m_openWindows ++; + m_openWindows++; connect(window, &InstanceWindow::isClosing, this, &Application::on_windowClose); } - if(!page.isEmpty()) - { + if (!page.isEmpty()) { window->selectPage(page); } - if(extras.controller) - { + if (extras.controller) { extras.controller->setParentWidget(window); } return window; @@ -1436,24 +1411,20 @@ InstanceWindow *Application::showInstanceWindow(InstancePtr instance, QString pa void Application::on_windowClose() { m_openWindows--; - auto instWindow = qobject_cast(QObject::sender()); - if(instWindow) - { - auto & extras = m_instanceExtras[instWindow->instanceId()]; + auto instWindow = qobject_cast(QObject::sender()); + if (instWindow) { + auto& extras = m_instanceExtras[instWindow->instanceId()]; extras.window = nullptr; - if(extras.controller) - { + if (extras.controller) { extras.controller->setParentWidget(m_mainWindow); } } - auto mainWindow = qobject_cast(QObject::sender()); - if(mainWindow) - { + auto mainWindow = qobject_cast(QObject::sender()); + if (mainWindow) { m_mainWindow = nullptr; } // quit when there are no more windows. - if(shouldExitNow()) - { + if (shouldExitNow()) { exit(0); } } @@ -1461,23 +1432,14 @@ void Application::on_windowClose() void Application::updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password) { // Set the application proxy settings. - if (proxyTypeStr == "SOCKS5") - { - QNetworkProxy::setApplicationProxy( - QNetworkProxy(QNetworkProxy::Socks5Proxy, addr, port, user, password)); - } - else if (proxyTypeStr == "HTTP") - { - QNetworkProxy::setApplicationProxy( - QNetworkProxy(QNetworkProxy::HttpProxy, addr, port, user, password)); - } - else if (proxyTypeStr == "None") - { + if (proxyTypeStr == "SOCKS5") { + QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, addr, port, user, password)); + } else if (proxyTypeStr == "HTTP") { + QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::HttpProxy, addr, port, user, password)); + } else if (proxyTypeStr == "None") { // If we have no proxy set, set no proxy and return. QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::NoProxy)); - } - else - { + } else { // If we have "Default" selected, set Qt to use the system proxy settings. QNetworkProxyFactory::setUseSystemConfiguration(true); } @@ -1487,39 +1449,35 @@ void Application::updateProxySettings(QString proxyTypeStr, QString addr, int po m_network->setProxy(proxy); QString proxyDesc; - if (proxy.type() == QNetworkProxy::NoProxy) - { + if (proxy.type() == QNetworkProxy::NoProxy) { qDebug() << "Using no proxy is an option!"; return; } - switch (proxy.type()) - { - case QNetworkProxy::DefaultProxy: - proxyDesc = "Default proxy: "; - break; - case QNetworkProxy::Socks5Proxy: - proxyDesc = "Socks5 proxy: "; - break; - case QNetworkProxy::HttpProxy: - proxyDesc = "HTTP proxy: "; - break; - case QNetworkProxy::HttpCachingProxy: - proxyDesc = "HTTP caching: "; - break; - case QNetworkProxy::FtpCachingProxy: - proxyDesc = "FTP caching: "; - break; - default: - proxyDesc = "DERP proxy: "; - break; + switch (proxy.type()) { + case QNetworkProxy::DefaultProxy: + proxyDesc = "Default proxy: "; + break; + case QNetworkProxy::Socks5Proxy: + proxyDesc = "Socks5 proxy: "; + break; + case QNetworkProxy::HttpProxy: + proxyDesc = "HTTP proxy: "; + break; + case QNetworkProxy::HttpCachingProxy: + proxyDesc = "HTTP caching: "; + break; + case QNetworkProxy::FtpCachingProxy: + proxyDesc = "FTP caching: "; + break; + default: + proxyDesc = "DERP proxy: "; + break; } - proxyDesc += QString("%1:%2") - .arg(proxy.hostName()) - .arg(proxy.port()); + proxyDesc += QString("%1:%2").arg(proxy.hostName()).arg(proxy.port()); qDebug() << proxyDesc; } -shared_qobject_ptr< HttpMetaCache > Application::metacache() +shared_qobject_ptr Application::metacache() { return m_metacache; } @@ -1531,8 +1489,7 @@ shared_qobject_ptr Application::network() shared_qobject_ptr Application::metadataIndex() { - if (!m_metadataIndex) - { + if (!m_metadataIndex) { m_metadataIndex.reset(new Meta::Index()); } return m_metadataIndex; @@ -1563,10 +1520,9 @@ QString Application::getJarPath(QString jarFile) #endif FS::PathCombine(m_rootPath, "jars"), FS::PathCombine(applicationDirPath(), "jars"), - FS::PathCombine(applicationDirPath(), "..", "jars") // from inside build dir, for debuging + FS::PathCombine(applicationDirPath(), "..", "jars") // from inside build dir, for debuging }; - for(QString p : potentialPaths) - { + for (QString p : potentialPaths) { QString jarPath = FS::PathCombine(p, jarFile); if (QFileInfo(jarPath).isFile()) return jarPath; @@ -1631,7 +1587,7 @@ int Application::suitableMaxMem() // If totalRAM < 6GB, use (totalRAM / 1.5), else 4GB if (totalRAM < (4096 * 1.5)) - maxMemoryAlloc = (int) (totalRAM / 1.5); + maxMemoryAlloc = (int)(totalRAM / 1.5); else maxMemoryAlloc = 4096; diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 5cdb0383e..dc413bb62 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -625,6 +625,11 @@ set(PRISMUPDATER_SOURCES net/HeaderProxy.h net/RawHeaderProxy.h + ui/dialogs/ProgressDialog.cpp + ui/dialogs/ProgressDialog.h + ui/widgets/SubTaskProgressBar.h + ui/widgets/SubTaskProgressBar.cpp + ) ######## Logging categories ######## @@ -1144,6 +1149,8 @@ qt_add_resources(LAUNCHER_RESOURCES qt_wrap_ui(PRISMUPDATER_UI updater/prismupdater/SelectReleaseDialog.ui + ui/widgets/SubTaskProgressBar.ui + ui/dialogs/ProgressDialog.ui ) ######## Windows resource files ######## @@ -1231,11 +1238,13 @@ install(TARGETS ${Launcher_Name} FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime ) -if(NOT APPLE OR (DEFINED Launcher_BUILD_UPDATER AND Launcher_BUILD_UPDATER) ) +if(NOT APPLE OR (DEFINED Launcher_BUILD_UPDATER AND Launcher_BUILD_UPDATER)) # Updater add_library(prism_updater_logic STATIC ${PRISMUPDATER_SOURCES} ${TASKS_SOURCES} ${PRISMUPDATER_UI}) target_include_directories(prism_updater_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(prism_updater_logic + QuaZip::QuaZip + ${ZLIB_LIBRARIES} systeminfo BuildConfig ghcFilesystem::ghc_filesystem diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 001ecb10c..2b62feeb3 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -46,8 +46,11 @@ #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif +#include +#include #include #include +#include #endif // Snippet from https://github.com/gulrak/filesystem#using-it-as-single-file-header @@ -100,27 +103,108 @@ void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QSt } } +#if defined Q_OS_WIN32 + +// taken from https://stackoverflow.com/a/25927081 +// getting a proper output to console with redirection support on windows is apearently hell +void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr) +{ + // Re-initialize the C runtime "FILE" handles with clean handles bound to "nul". We do this because it has been + // observed that the file number of our standard handle file objects can be assigned internally to a value of -2 + // when not bound to a valid target, which represents some kind of unknown internal invalid state. In this state our + // call to "_dup2" fails, as it specifically tests to ensure that the target file number isn't equal to this value + // before allowing the operation to continue. We can resolve this issue by first "re-opening" the target files to + // use the "nul" device, which will place them into a valid state, after which we can redirect them to our target + // using the "_dup2" function. + if (bindStdIn) { + FILE* dummyFile; + freopen_s(&dummyFile, "nul", "r", stdin); + } + if (bindStdOut) { + FILE* dummyFile; + freopen_s(&dummyFile, "nul", "w", stdout); + } + if (bindStdErr) { + FILE* dummyFile; + freopen_s(&dummyFile, "nul", "w", stderr); + } + + // Redirect unbuffered stdin from the current standard input handle + if (bindStdIn) { + HANDLE stdHandle = GetStdHandle(STD_INPUT_HANDLE); + if (stdHandle != INVALID_HANDLE_VALUE) { + int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); + if (fileDescriptor != -1) { + FILE* file = _fdopen(fileDescriptor, "r"); + if (file != NULL) { + int dup2Result = _dup2(_fileno(file), _fileno(stdin)); + if (dup2Result == 0) { + setvbuf(stdin, NULL, _IONBF, 0); + } + } + } + } + } + + // Redirect unbuffered stdout to the current standard output handle + if (bindStdOut) { + HANDLE stdHandle = GetStdHandle(STD_OUTPUT_HANDLE); + if (stdHandle != INVALID_HANDLE_VALUE) { + int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); + if (fileDescriptor != -1) { + FILE* file = _fdopen(fileDescriptor, "w"); + if (file != NULL) { + int dup2Result = _dup2(_fileno(file), _fileno(stdout)); + if (dup2Result == 0) { + setvbuf(stdout, NULL, _IONBF, 0); + } + } + } + } + } + + // Redirect unbuffered stderr to the current standard error handle + if (bindStdErr) { + HANDLE stdHandle = GetStdHandle(STD_ERROR_HANDLE); + if (stdHandle != INVALID_HANDLE_VALUE) { + int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); + if (fileDescriptor != -1) { + FILE* file = _fdopen(fileDescriptor, "w"); + if (file != NULL) { + int dup2Result = _dup2(_fileno(file), _fileno(stderr)); + if (dup2Result == 0) { + setvbuf(stderr, NULL, _IONBF, 0); + } + } + } + } + } + + // Clear the error state for each of the C++ standard stream objects. We need to do this, as attempts to access the + // standard streams before they refer to a valid target will cause the iostream objects to enter an error state. In + // versions of Visual Studio after 2005, this seems to always occur during startup regardless of whether anything + // has been read from or written to the targets or not. + if (bindStdIn) { + std::wcin.clear(); + std::cin.clear(); + } + if (bindStdOut) { + std::wcout.clear(); + std::cout.clear(); + } + if (bindStdErr) { + std::wcerr.clear(); + std::cerr.clear(); + } +} +#endif + PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, argv) { #if defined Q_OS_WIN32 // attach the parent console if (AttachConsole(ATTACH_PARENT_PROCESS)) { - FILE* _stream; - errno_t err; - // if attach succeeds, reopen and sync all the i/o - if (err = freopen_s(&_stream, "CON", "w", stdout); err == 0) { - std::cout.sync_with_stdio(); - } - if (err = freopen_s(&_stream, "CON", "w", stderr); err == 0) { - std::cerr.sync_with_stdio(); - } - if (err = freopen_s(&_stream, "CON", "r", stdin); err == 0) { - std::cin.sync_with_stdio(); - } - auto out = GetStdHandle(STD_OUTPUT_HANDLE); - DWORD written; - const char* endline = "\n"; - WriteConsole(out, endline, strlen(endline), &written, NULL); + BindCrtHandlesToStdHandles(true, true, true); consoleAttached = true; } #endif @@ -133,16 +217,20 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar QCommandLineParser parser; parser.setApplicationDescription(QObject::tr("An auto-updater for Prism Launcher")); - parser.addOptions({ { { "d", "dir" }, tr("Use a custom path as application root (use '.' for current directory)."), tr("directory") }, - { { "I", "install-version" }, "Install a specific version.", tr("version name") }, - { { "U", "update-url" }, tr("Update from the specified repo."), tr("github repo url") }, - { { "c", "check-only" }, - tr("Only check if an update is needed. Exit status 100 if true, 0 if false (or non 0 if there was an error).") }, - { { "F", "force" }, tr("Force an update, even if one is not needed.") }, - { { "l", "list" }, tr("List available releases.") }, - { "debug", tr("Log debug to console.") }, - { { "S", "select-ui" }, tr("Select the version to install with a GUI.") }, - { { "D", "allow-downgrade" }, tr("Allow the updater to downgrade to previous versions.") } }); + parser.addOptions( + { { { "d", "dir" }, tr("Use a custom path as application root (use '.' for current directory)."), tr("directory") }, + { { "V", "prism-version" }, + tr("Use this version as the installed launcher version. (provided because stdout can not be reliably captured on windows)"), + tr("installed launcher version") }, + { { "I", "install-version" }, "Install a specific version.", tr("version name") }, + { { "U", "update-url" }, tr("Update from the specified repo."), tr("github repo url") }, + { { "c", "check-only" }, + tr("Only check if an update is needed. Exit status 100 if true, 0 if false (or non 0 if there was an error).") }, + { { "F", "force" }, tr("Force an update, even if one is not needed.") }, + { { "l", "list" }, tr("List available releases.") }, + { "debug", tr("Log debug to console.") }, + { { "S", "select-ui" }, tr("Select the version to install with a GUI.") }, + { { "D", "allow-downgrade" }, tr("Allow the updater to downgrade to previous versions.") } }); parser.addHelpOption(); parser.addVersionOption(); @@ -150,24 +238,31 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar logToConsole = parser.isSet("debug"); - auto prism_executable = QCoreApplication::applicationFilePath(); + auto updater_executable = QCoreApplication::applicationFilePath(); if (BuildConfig.BUILD_PLATFORM.toLower() == "macos") showFatalErrorMessage(tr("MacOS Not Supported"), tr("The updater does not support installations on MacOS")); - if (!QFileInfo(prism_executable).isFile()) - showFatalErrorMessage(tr("Unsupported Installation"), tr("The updater can not find the main executable.")); - - if (prism_executable.startsWith("/tmp/.mount_")) { + if (updater_executable.startsWith("/tmp/.mount_")) { m_isAppimage = true; m_appimagePath = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE")); - if (m_appimagePath.isEmpty()) + if (m_appimagePath.isEmpty()) { showFatalErrorMessage(tr("Unsupported Installation"), tr("Updater is running as misconfigured AppImage? ($APPIMAGE environment variable is missing)")); + } } m_isFlatpak = DesktopServices::isFlatpak(); + QString prism_executable = QCoreApplication::applicationDirPath() + "/" + BuildConfig.LAUNCHER_APP_BINARY_NAME; +#if defined Q_OS_WIN32 + prism_executable += ".exe"; +#endif + + if (!QFileInfo(prism_executable).isFile()) { + showFatalErrorMessage(tr("Unsupported Installation"), tr("The updater can not find the main executable.")); + } + m_prismExecutable = prism_executable; auto prism_update_url = parser.value("update-url"); @@ -185,6 +280,20 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar m_selectUI = parser.isSet("select-ui"); m_allowDowngrade = parser.isSet("allow-downgrade"); + auto version = parser.value("prism-version"); + if (!version.isEmpty()) { + if (version.contains('-')) { + auto index = version.indexOf('-'); + m_prsimVersionChannel = version.mid(index + 1); + version = version.left(index); + } else { + m_prsimVersionChannel = "stable"; + } + auto version_parts = version.split('.'); + m_prismVersionMajor = version_parts.takeFirst().toInt(); + m_prismVersionMinor = version_parts.takeFirst().toInt(); + } + QString origCwdPath = QDir::currentPath(); QString binPath = applicationDirPath(); @@ -338,9 +447,9 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar PrismUpdaterApp::~PrismUpdaterApp() { - qDebug() << "updater shutting down"; // Shut down logger by setting the logger function to nothing qInstallMessageHandler(nullptr); + qDebug() << "updater shutting down"; #if defined Q_OS_WIN32 // Detach from Windows console @@ -392,13 +501,19 @@ void PrismUpdaterApp::run() return exit(0); } - loadPrismVersionFromExe(m_prismExecutable); + if (!loadPrismVersionFromExe(m_prismExecutable)) { + m_prismVersion = BuildConfig.printableVersionString(); + m_prismVersionMajor = BuildConfig.VERSION_MAJOR; + m_prismVersionMinor = BuildConfig.VERSION_MINOR; + m_prsimVersionChannel = BuildConfig.VERSION_CHANNEL; + m_prismGitCommit = BuildConfig.GIT_COMMIT; + } m_status = Succeeded; qDebug() << "Executable reports as:" << m_prismBinaryName << "version:" << m_prismVersion; qDebug() << "Version major:" << m_prismVersionMajor; - qDebug() << "Verison minor:" << m_prismVersionMinor; - qDebug() << "Verison channel:" << m_prsimVersionChannel; + qDebug() << "Version minor:" << m_prismVersionMinor; + qDebug() << "Version channel:" << m_prsimVersionChannel; qDebug() << "Git Commit:" << m_prismGitCommit; auto latest = getLatestRelease(); @@ -420,7 +535,7 @@ void PrismUpdaterApp::run() if (m_isAppimage) { bool result = true; if (need_update) - result = callAppimageUpdate(); + result = callAppImageUpdate(); return exit(result ? 0 : 1); } @@ -528,6 +643,13 @@ QList PrismUpdaterApp::validReleaseArtifacts(const GitHubRel bool for_platform = !BuildConfig.BUILD_PLATFORM.isEmpty() && asset.name.toLower().contains(BuildConfig.BUILD_PLATFORM.toLower()); bool for_portable = asset.name.toLower().contains("portable"); + if (for_platform && asset.name.toLower().contains("legacy") && !BuildConfig.BUILD_PLATFORM.toLower().contains("legacy")) + for_platform = false; + if (for_platform && asset.name.toLower().contains("arm64") && !QSysInfo::buildCpuArchitecture().contains("arm64")) + for_platform = false; + if (for_platform && !asset.name.toLower().contains("arm64") && QSysInfo::buildCpuArchitecture().contains("arm64")) + for_platform = false; + if (((m_isPortable && for_portable) || (!m_isPortable && !for_portable)) && for_platform) { valid.append(asset); } @@ -557,10 +679,11 @@ void PrismUpdaterApp::performUpdate(const GitHubRelease& release) GitHubReleaseAsset selected_asset; if (valid_assets.isEmpty()) { - return showFatalErrorMessage(tr("No Valid Release Assets"), - tr("Github release %1 has no valid assets for this platform: %2") - .arg(release.tag_name) - .arg(tr("%1 portable: %2").arg(BuildConfig.BUILD_PLATFORM).arg(m_isPortable))); + return showFatalErrorMessage( + tr("No Valid Release Assets"), + tr("Github release %1 has no valid assets for this platform: %2") + .arg(release.tag_name) + .arg(tr("%1 portable: %2").arg(BuildConfig.BUILD_PLATFORM).arg(m_isPortable ? tr("yes") : tr("no")))); } else if (valid_assets.length() > 1) { selected_asset = selectAsset(valid_assets); } else { @@ -587,18 +710,21 @@ QFileInfo PrismUpdaterApp::downloadAsset(const GitHubReleaseAsset& asset) auto file_url = QUrl(asset.browser_download_url); auto out_file_path = FS::PathCombine(temp_dir, file_url.fileName()); + qDebug() << "downloading" << file_url << "to" << out_file_path; auto download = Net::Download::makeFile(file_url, out_file_path); - + download->setNetwork(m_network); auto progress_dialog = ProgressDialog(); if (progress_dialog.execWithTask(download.get()) == QDialog::Rejected) showFatalErrorMessage(tr("Download Aborted"), tr("Download of %1 aborted by user").arg(file_url.toString())); + qDebug() << "download complete"; + QFileInfo out_file(out_file_path); return out_file; } -bool PrismUpdaterApp::callAppimageUpdate() +bool PrismUpdaterApp::callAppImageUpdate() { QProcess proc = QProcess(); proc.setProgram("AppImageUpdate"); @@ -614,20 +740,20 @@ void PrismUpdaterApp::clearUpdateLog() void PrismUpdaterApp::logUpdate(const QString& msg) { - qDebug() << msg; + qDebug() << qUtf8Printable(msg); auto update_log_path = FS::PathCombine(m_dataPath, "prism_launcher_update.log"); FS::append(update_log_path, QStringLiteral("%1\n").arg(msg).toUtf8()); } void PrismUpdaterApp::performInstall(QFileInfo file) { + qDebug() << "starting install"; auto update_lock_path = FS::PathCombine(m_dataPath, ".prism_launcher_update.lock"); FS::write(update_lock_path, QStringLiteral("FROM=%1\nTO=%2\n").arg(m_prismVersion).arg(m_install_release.tag_name).toUtf8()); clearUpdateLog(); logUpdate(tr("Updating from %1 to %2").arg(m_prismVersion).arg(m_install_release.tag_name)); - // TODO setup marker file - if (m_isPortable) { + if (m_isPortable || file.suffix().toLower() == "zip") { logUpdate(tr("Updating portable install at %1").arg(applicationDirPath())); unpackAndInstall(file); } else { @@ -635,6 +761,7 @@ void PrismUpdaterApp::performInstall(QFileInfo file) QProcess proc = QProcess(); proc.setProgram(file.absoluteFilePath()); bool result = proc.startDetached(); + logUpdate(tr("Process start result: %1").arg(result ? tr("yes") : tr("no"))); exit(result ? 0 : 1); } } @@ -643,8 +770,24 @@ void PrismUpdaterApp::unpackAndInstall(QFileInfo archive) { logUpdate(tr("Backing up install")); backupAppDir(); - auto loc = unpackArchive(archive); - // TODO: unpack (rename access failures) + + if (auto loc = unpackArchive(archive)) { + auto marker_file_path = loc.value().absoluteFilePath(".prism_launcher_updater_unpack.marker"); + FS::write(marker_file_path, applicationDirPath().toUtf8()); + auto new_updater_path = loc.value().absoluteFilePath("prismlauncher-updater"); +#if defined Q_OS_WIN32 + new_updater_path.append(".exe"); +#endif + logUpdate(tr("Starting new updater at '%1'").arg(new_updater_path)); + QProcess proc = QProcess(); + proc.startDetached(new_updater_path, {}, loc.value().absolutePath()); + if (!proc.waitForStarted(5000)) { + logUpdate(tr("Failed to launch '%1' %2").arg(new_updater_path).arg(proc.errorString())); + return exit(10); + } + return exit(); // up to the new updater now + } + return exit(1); // unpack failure } void PrismUpdaterApp::backupAppDir() @@ -680,6 +823,8 @@ void PrismUpdaterApp::backupAppDir() "styles/*", "styles/*", "tls/*", + "qt.conf", + "Qt*.dll", }); } file_list.append("portable.txt"); @@ -688,7 +833,10 @@ void PrismUpdaterApp::backupAppDir() logUpdate(tr("Backing up:\n %1").arg(file_list.join(",\n "))); QDir app_dir = QCoreApplication::applicationDirPath(); - auto backup_dir = FS::PathCombine(app_dir.absolutePath(), QStringLiteral("backup_") + m_prismVersion + ":" + m_prismGitCommit); + auto backup_dir = FS::PathCombine( + app_dir.absolutePath(), QStringLiteral("backup_") + + QString(m_prismVersion).replace(QRegularExpression("[" + QRegularExpression::escape("\\/:*?\"<>|") + "]"), QString("_")) + + "-" + m_prismGitCommit); FS::ensureFolderPathExists(backup_dir); for (auto glob : file_list) { @@ -723,13 +871,12 @@ std::optional PrismUpdaterApp::unpackArchive(QFileInfo archive) if (archive.fileName().endsWith(".zip")) { auto result = MMCZip::extractDir(archive.absoluteFilePath(), tmp_extract_dir.absolutePath()); if (result) { - logUpdate( - tr("Extracted the following to \"%1\":\n %2").arg(tmp_extract_dir.absolutePath()).arg(result->join("\n "))); + logUpdate(tr("Extracted the following to \"%1\":\n %2").arg(tmp_extract_dir.absolutePath()).arg(result->join("\n "))); } else { logUpdate(tr("Failed to extract %1 to %2").arg(archive.absoluteFilePath()).arg(tmp_extract_dir.absolutePath())); showFatalErrorMessage("Failed to extract archive", tr("Failed to extract %1 to %2").arg(archive.absoluteFilePath()).arg(tmp_extract_dir.absolutePath())); - return {}; + return std::nullopt; } } else if (archive.fileName().endsWith(".tar.gz")) { @@ -739,43 +886,52 @@ std::optional PrismUpdaterApp::unpackArchive(QFileInfo archive) QProcess proc = QProcess(); proc.start(cmd, args); if (!proc.waitForStarted(5000)) { // wait 5 seconds to start - showFatalErrorMessage(tr("Failed extract archive"), - tr("Failed to launcher child process \"%1 %2\".").arg(cmd).arg(args.join(" "))); - return {}; + auto msg = tr("Failed to launcher child process \"%1 %2\".").arg(cmd).arg(args.join(" ")); + logUpdate(msg); + showFatalErrorMessage(tr("Failed extract archive"), msg); + return std::nullopt; } auto result = proc.waitForFinished(5000); auto out = proc.readAll(); logUpdate(out); if (!result) { - showFatalErrorMessage(tr("Failed to extract archive"), tr("Child process \"%1 %2\" failed.").arg(cmd).arg(args.join(" "))); - return {}; + auto msg = tr("Child process \"%1 %2\" failed.").arg(cmd).arg(args.join(" ")); + logUpdate(msg); + showFatalErrorMessage(tr("Failed to extract archive"), msg); + return std::nullopt; } } else { logUpdate(tr("Unknown archive format for %1").arg(archive.absoluteFilePath())); showFatalErrorMessage("Can not extract", QStringLiteral("Unknown archive format %1").arg(archive.absoluteFilePath())); - return {}; + return std::nullopt; } return tmp_extract_dir; } -void PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) +bool PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) { QProcess proc = QProcess(); + proc.setProcessChannelMode(QProcess::MergedChannels); + proc.setReadChannel(QProcess::StandardOutput); proc.start(exe_path, { "-v" }); - if (!proc.waitForStarted(5000)) // wait 5 seconds to start - return showFatalErrorMessage(tr("Failed to Check Version"), tr("Failed to launcher child launcher process to read version.")); - if (!proc.waitForFinished(5000)) - return showFatalErrorMessage(tr("Failed to Check Version"), tr("Child launcher process failed.")); + if (!proc.waitForStarted(5000)) { + showFatalErrorMessage(tr("Failed to Check Version"), tr("Failed to launcher child launcher process to read version.")); + return false; + } // wait 5 seconds to start + if (!proc.waitForFinished(5000)) { + showFatalErrorMessage(tr("Failed to Check Version"), tr("Child launcher process failed.")); + return false; + } auto out = proc.readAll(); auto lines = out.split('\n'); if (lines.length() < 2) - return; + return false; auto first = lines.takeFirst(); auto first_parts = first.split(' '); if (first_parts.length() < 2) - return; + return false; m_prismBinaryName = first_parts.takeFirst(); auto version = first_parts.takeFirst(); m_prismVersion = version; @@ -790,6 +946,7 @@ void PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) m_prismVersionMajor = version_parts.takeFirst().toInt(); m_prismVersionMinor = version_parts.takeFirst().toInt(); m_prismGitCommit = lines.takeFirst().simplified(); + return true; } void PrismUpdaterApp::loadReleaseList() diff --git a/launcher/updater/prismupdater/PrismUpdater.h b/launcher/updater/prismupdater/PrismUpdater.h index af7af7dee..909e36056 100644 --- a/launcher/updater/prismupdater/PrismUpdater.h +++ b/launcher/updater/prismupdater/PrismUpdater.h @@ -60,7 +60,7 @@ class PrismUpdaterApp : public QApplication { void abort(const QString& reason); void showFatalErrorMessage(const QString& title, const QString& content); - void loadPrismVersionFromExe(const QString& exe_path); + bool loadPrismVersionFromExe(const QString& exe_path); void downloadReleasePage(const QString& api_url, int page); int parseReleasePage(const QByteArray* response); @@ -83,7 +83,9 @@ class PrismUpdaterApp : public QApplication { std::optional unpackArchive(QFileInfo file); QFileInfo downloadAsset(const GitHubReleaseAsset& asset); - bool callAppimageUpdate(); + bool callAppImageUpdate(); + + void moveAndPostProcess(QDir target); public slots: void downloadError(QString reason); @@ -114,8 +116,8 @@ class PrismUpdaterApp : public QApplication { QString m_prismBinaryName; QString m_prismVersion; - int m_prismVersionMajor; - int m_prismVersionMinor; + int m_prismVersionMajor = -1; + int m_prismVersionMinor = -1; QString m_prsimVersionChannel; QString m_prismGitCommit; diff --git a/launcher/updater/prismupdater/UpdaterDialogs.cpp b/launcher/updater/prismupdater/UpdaterDialogs.cpp index 72fdf3404..14c584918 100644 --- a/launcher/updater/prismupdater/UpdaterDialogs.cpp +++ b/launcher/updater/prismupdater/UpdaterDialogs.cpp @@ -17,8 +17,8 @@ SelectReleaseDialog::SelectReleaseDialog(const Version& current_version, const Q ui->versionsTree->setColumnCount(2); ui->versionsTree->header()->setSectionResizeMode(0, QHeaderView::Stretch); - ui->versionsTree->header()->setSectionResizeMode(1, QHeaderView::Stretch); - ui->versionsTree->setHeaderLabels({tr("Verison"), tr("Published Date")}); + ui->versionsTree->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); + ui->versionsTree->setHeaderLabels({tr("Version"), tr("Published Date")}); ui->versionsTree->header()->setStretchLastSection(false); ui->eplainLabel->setText(tr("Select a version to install.\n" @@ -90,8 +90,8 @@ SelectReleaseAssetDialog::SelectReleaseAssetDialog( const QListversionsTree->setColumnCount(2); ui->versionsTree->header()->setSectionResizeMode(0, QHeaderView::Stretch); - ui->versionsTree->header()->setSectionResizeMode(1, QHeaderView::Stretch); - ui->versionsTree->setHeaderLabels({tr("Verison"), tr("Published Date")}); + ui->versionsTree->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); + ui->versionsTree->setHeaderLabels({tr("Version"), tr("Published Date")}); ui->versionsTree->header()->setStretchLastSection(false); ui->eplainLabel->setText(tr("Select a version to install.")); From d2a3acd493f3da7dab05c0744aa46d274535b9d2 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sat, 24 Jun 2023 10:53:20 -0700 Subject: [PATCH 054/268] fix: filter archive assets from windows non-portable installs Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/updater/prismupdater/PrismUpdater.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 2b62feeb3..7f065f536 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -640,14 +640,19 @@ QList PrismUpdaterApp::validReleaseArtifacts(const GitHubRel continue; else if (m_isAppimage && !asset.name.toLower().endsWith("appimage")) continue; + auto asset_name = asset.name.toLower(); + auto platform = BuildConfig.BUILD_PLATFORM.toLower(); + auto system_is_arm = QSysInfo::buildCpuArchitecture().contains("arm64"); + auto asset_is_arm = asset_name.contains("arm64"); + auto asset_is_archive = asset_name.endsWith(".zip") || asset_name.endsWith(".tar.gz"); - bool for_platform = !BuildConfig.BUILD_PLATFORM.isEmpty() && asset.name.toLower().contains(BuildConfig.BUILD_PLATFORM.toLower()); - bool for_portable = asset.name.toLower().contains("portable"); - if (for_platform && asset.name.toLower().contains("legacy") && !BuildConfig.BUILD_PLATFORM.toLower().contains("legacy")) + bool for_platform = !platform.isEmpty() && asset_name.contains(platform); + bool for_portable = asset_name.contains("portable"); + if (for_platform && asset_name.contains("legacy") && !platform.contains("legacy")) for_platform = false; - if (for_platform && asset.name.toLower().contains("arm64") && !QSysInfo::buildCpuArchitecture().contains("arm64")) + if (for_platform && ((asset_is_arm && !system_is_arm) || (!asset_is_arm && system_is_arm))) for_platform = false; - if (for_platform && !asset.name.toLower().contains("arm64") && QSysInfo::buildCpuArchitecture().contains("arm64")) + if (for_platform && platform.contains("windows") && !m_isPortable && asset_is_archive) for_platform = false; if (((m_isPortable && for_portable) || (!m_isPortable && !for_portable)) && for_platform) { From 431346658951ef699669b8564fc38366eb62271f Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sat, 24 Jun 2023 22:21:16 -0700 Subject: [PATCH 055/268] feat(updater): final step for portable install Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- CMakeLists.txt | 4 +- .../minecraft/mod/tasks/LocalModParseTask.cpp | 5 +- launcher/modplatform/packwiz/Packwiz.cpp | 7 +- launcher/ui/dialogs/ProgressDialog.cpp | 9 +- launcher/ui/dialogs/ProgressDialog.ui | 7 +- .../updater/prismupdater/PrismUpdater.cpp | 270 +++++++++++++++--- launcher/updater/prismupdater/PrismUpdater.h | 5 +- .../prismupdater/SelectReleaseDialog.ui | 4 +- 8 files changed, 256 insertions(+), 55 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 70a553190..cb75f769c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -148,8 +148,8 @@ set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_M # Build platform. set(Launcher_BUILD_PLATFORM "" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.") -# Channel list URL -set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.") +# Github repo URL with releases for updater +set(Launcher_UPDATER_GITHUB_REPO "" CACHE STRING "Base URL for the updater.") # The metadata server set(Launcher_META_URL "https://meta.prismlauncher.org/v1/" CACHE STRING "URL to fetch Launcher's meta files from.") diff --git a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp index 264019f84..43fc4831d 100644 --- a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp @@ -108,10 +108,11 @@ ModDetails ReadMCModTOML(QByteArray contents) return {}; } #else - tomlData = toml::parse(contents.toStdString()); - if (!tomlData) { + toml::parse_result result = toml::parse(contents.toStdString()); + if (!result) { return {}; } + tomlData = result.table(); #endif // array defined by [[mods]] diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 510c7309d..967b8870c 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -241,12 +241,13 @@ auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod return {}; } #else - table = toml::parse_file(StringUtils::toStdString(index_dir.absoluteFilePath(real_fname))); - if (!table) { + toml::parse_result result = toml::parse_file(StringUtils::toStdString(index_dir.absoluteFilePath(real_fname))); + if (!result) { qWarning() << QString("Could not open file %1!").arg(normalized_fname); - qWarning() << "Reason: " << QString(table.error().what()); + qWarning() << "Reason: " << result.error().description(); return {}; } + table = result.table(); #endif // index_file.close(); diff --git a/launcher/ui/dialogs/ProgressDialog.cpp b/launcher/ui/dialogs/ProgressDialog.cpp index 246a0fd49..93f8076f4 100644 --- a/launcher/ui/dialogs/ProgressDialog.cpp +++ b/launcher/ui/dialogs/ProgressDialog.cpp @@ -68,6 +68,8 @@ ProgressDialog::ProgressDialog(QWidget* parent) : QDialog(parent), ui(new Ui::Pr setAttribute(Qt::WidgetAttribute::WA_QuitOnClose, true); setSkipButton(false); changeProgress(0, 100); + updateSize(); + adjustSize(); } void ProgressDialog::setSkipButton(bool present, QString label) @@ -96,7 +98,11 @@ ProgressDialog::~ProgressDialog() void ProgressDialog::updateSize() { QSize lastSize = this->size(); - QSize qSize = QSize(480, minimumSizeHint().height()); + auto min_height = minimumSizeHint().height(); + if (ui->taskProgressScrollArea->isHidden()) + min_height -= ui->taskProgressScrollArea->minimumSizeHint().height(); + min_height = std::max(min_height, 0); + QSize qSize = QSize(480, min_height); // if the current window is too small if ((lastSize != qSize) && (lastSize.height() < qSize.height())) @@ -111,7 +117,6 @@ void ProgressDialog::updateSize() } setMinimumSize(qSize); - } int ProgressDialog::execWithTask(Task* task) diff --git a/launcher/ui/dialogs/ProgressDialog.ui b/launcher/ui/dialogs/ProgressDialog.ui index a4d08124c..156ff247f 100644 --- a/launcher/ui/dialogs/ProgressDialog.ui +++ b/launcher/ui/dialogs/ProgressDialog.ui @@ -48,6 +48,9 @@ Global Task Status... + + true +
@@ -109,8 +112,8 @@ 0 0 - 464 - 96 + 460 + 108 diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 7f065f536..221c1c649 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -39,7 +39,7 @@ #include #include -#include +#include #include #if defined Q_OS_WIN32 @@ -106,7 +106,7 @@ void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QSt #if defined Q_OS_WIN32 // taken from https://stackoverflow.com/a/25927081 -// getting a proper output to console with redirection support on windows is apearently hell +// getting a proper output to console with redirection support on windows is apparently hell void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr) { // Re-initialize the C runtime "FILE" handles with clean handles bound to "nul". We do this because it has been @@ -266,8 +266,11 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar m_prismExecutable = prism_executable; auto prism_update_url = parser.value("update-url"); + if (prism_update_url.isEmpty()) + prism_update_url = BuildConfig.UPDATER_GITHUB_REPO; if (prism_update_url.isEmpty()) prism_update_url = "https://github.com/PrismLauncher/PrismLauncher"; + m_prismRepoUrl = QUrl::fromUserInput(prism_update_url); m_checkOnly = parser.isSet("check-only"); @@ -334,6 +337,8 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar #endif } + m_updateLogPath = FS::PathCombine(m_dataPath, "prism_launcher_update.log"); + { // setup logging static const QString logBase = BuildConfig.LAUNCHER_NAME + "Updater" + (m_checkOnly ? "-CheckOnly" : "") + "-%0.log"; auto moveFile = [](const QString& oldName, const QString& newName) { @@ -424,14 +429,16 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT; qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC; if (adjustedBy.size()) { - qDebug() << "Work dir before adjustment : " << origCwdPath; - qDebug() << "Work dir after adjustment : " << QDir::currentPath(); + qDebug() << "Data dir before adjustment : " << origCwdPath; + qDebug() << "Data dir after adjustment : " << m_dataPath; qDebug() << "Adjusted by : " << adjustedBy; } else { - qDebug() << "Work dir : " << QDir::currentPath(); + qDebug() << "Data dir : " << QDir::currentPath(); } + qDebug() << "Work dir : " << QDir::currentPath(); qDebug() << "Binary path : " << binPath; qDebug() << "Application root path : " << m_rootPath; + qDebug() << "Portable install : " << m_isPortable; qDebug() << "<> Paths set."; } @@ -442,14 +449,28 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar m_network->setProxy(proxy); } - QMetaObject::invokeMethod(this, &PrismUpdaterApp::loadReleaseList, Qt::QueuedConnection); + auto marker_file_path = QDir(applicationDirPath()).absoluteFilePath(".prism_launcher_updater_unpack.marker"); + auto marker_file = QFileInfo(marker_file_path); + if (marker_file.exists()) { + auto target_dir = QString(FS::read(marker_file_path)).trimmed(); + if (target_dir.isEmpty()) { + qWarning() << "Empty updater marker file contains no install target. making best guess of parent dir"; + target_dir = QDir(applicationDirPath()).absoluteFilePath(".."); + } + + QMetaObject::invokeMethod( + this, [this, target_dir]() { moveAndFinishUpdate(target_dir); }, Qt::QueuedConnection); + + } else { + QMetaObject::invokeMethod(this, &PrismUpdaterApp::loadReleaseList, Qt::QueuedConnection); + } } PrismUpdaterApp::~PrismUpdaterApp() { + qDebug() << "updater shutting down"; // Shut down logger by setting the logger function to nothing qInstallMessageHandler(nullptr); - qDebug() << "updater shutting down"; #if defined Q_OS_WIN32 // Detach from Windows console @@ -576,6 +597,81 @@ void PrismUpdaterApp::run() exit(0); } +void PrismUpdaterApp::moveAndFinishUpdate(QDir target) +{ + logUpdate("Finishing update process"); + auto manifest_path = FS::PathCombine(applicationDirPath(), "manifest.txt"); + QFileInfo manifest(manifest_path); + + auto app_dir = QDir(applicationDirPath()); + + QStringList file_list; + if (manifest.isFile()) { + // load manifest from file + logUpdate(tr("Reading manifest from %1").arg(manifest.absoluteFilePath())); + try { + auto contents = QString::fromUtf8(FS::read(manifest.absoluteFilePath())); + auto files = contents.split('\n'); + for (auto file : files) { + file_list.append(file.trimmed()); + } + } catch (FS::FileSystemException) { + } + } + + if (file_list.isEmpty()) { + logUpdate(tr("Manifest empty, making best guess of the directory contents of %1").arg(applicationDirPath())); + auto entries = target.entryInfoList(QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); + for (auto entry : entries) { + file_list.append(entry.fileName()); + } + } + logUpdate(tr("Installing the following to %1 :\n %2").arg(target.absolutePath()).arg(file_list.join(",\n "))); + + bool error = false; + + QProgressDialog progress(tr("Backing up install at %1").arg(applicationDirPath()), "", 0, file_list.length()); + progress.setCancelButton(nullptr); + progress.show(); + QCoreApplication::processEvents(); + + int i = 0; + for (auto glob : file_list) { + QDirIterator iter(applicationDirPath(), QStringList({ glob }), QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); + progress.setValue(i); + QCoreApplication::processEvents(); + while (iter.hasNext()) { + auto to_install_file = iter.next(); + auto rel_path = app_dir.relativeFilePath(to_install_file); + auto install_path = FS::PathCombine(target.absolutePath(), rel_path); + logUpdate(tr("Installing %1 from %2").arg(install_path).arg(to_install_file)); + FS::ensureFilePathExists(install_path); + auto result = FS::copy(to_install_file, install_path)(); + if (!result) { + error = true; + logUpdate(tr("Failed copy %1 to %2").arg(to_install_file).arg(install_path)); + } + } + i++; + } + progress.setValue(i); + QCoreApplication::processEvents(); + + if (error) { + logUpdate(tr("There were errors installing the update.")); + auto fail_marker = FS::PathCombine(m_dataPath, ".prism_launcher_update.fail"); + FS::move(m_updateLogPath, fail_marker); + } else { + logUpdate(tr("Update succeed.")); + auto success_marker = FS::PathCombine(m_dataPath, ".prism_launcher_update.success"); + FS::move(m_updateLogPath, success_marker); + } + auto update_lock_path = FS::PathCombine(m_dataPath, ".prism_launcher_update.lock"); + FS::deletePath(update_lock_path); + + exit(error ? 1 : 0); +} + void PrismUpdaterApp::printReleases() { for (auto release : m_releases) { @@ -739,24 +835,107 @@ bool PrismUpdaterApp::callAppImageUpdate() void PrismUpdaterApp::clearUpdateLog() { - auto update_log_path = FS::PathCombine(m_dataPath, "prism_launcher_update.log"); - QFile::remove(update_log_path); + QFile::remove(m_updateLogPath); } void PrismUpdaterApp::logUpdate(const QString& msg) { qDebug() << qUtf8Printable(msg); - auto update_log_path = FS::PathCombine(m_dataPath, "prism_launcher_update.log"); - FS::append(update_log_path, QStringLiteral("%1\n").arg(msg).toUtf8()); + FS::append(m_updateLogPath, QStringLiteral("%1\n").arg(msg).toUtf8()); +} + +std::tuple read_lock_File(const QString& path) +{ + auto contents = QString(FS::read(path)); + auto lines = contents.split('\n'); + + QDateTime timestamp; + QString from, to, target, data_path; + for (auto line : lines) { + auto index = line.indexOf("="); + if (index < 0) + continue; + auto left = line.left(index); + auto right = line.mid(index + 1); + if (left.toLower() == "timestamp") { + timestamp = QDateTime::fromString(right, Qt::ISODate); + } else if (left.toLower() == "from") { + from = right; + } else if (left.toLower() == "to") { + to = right; + } else if (left.toLower() == "target") { + target = right; + } else if (left.toLower() == "data_path") { + data_path = right; + } + } + return std::make_tuple(timestamp, from, to, target, data_path); +} + +bool write_lock_file(const QString& path, QDateTime timestamp, QString from, QString to, QString target, QString data_path) +{ + try { + FS::write(path, QStringLiteral("TIMESTAMP=%1\nFROM=%2\nTO=%3\nTARGET=%4\nDATA_PATH=%5\n") + .arg(timestamp.toString(Qt::ISODate)) + .arg(from) + .arg(to) + .arg(target) + .arg(data_path) + .toUtf8()); + } catch (FS::FileSystemException err) { + qWarning() << "Error writing lockfile:" << err.what() << "\n" << err.cause(); + return false; + } + return true; } void PrismUpdaterApp::performInstall(QFileInfo file) { qDebug() << "starting install"; auto update_lock_path = FS::PathCombine(m_dataPath, ".prism_launcher_update.lock"); - FS::write(update_lock_path, QStringLiteral("FROM=%1\nTO=%2\n").arg(m_prismVersion).arg(m_install_release.tag_name).toUtf8()); + QFileInfo update_lock(update_lock_path); + if (update_lock.exists()) { + auto [timestamp, from, to, target, data_path] = read_lock_File(update_lock_path); + auto msg = tr("Update already in progress\n"); + auto infoMsg = + tr("This installation has a update lock file present at: %1\n" + "\n" + "Timestamp: %2\n" + "Updating from version %3 to %4\n" + "Target install path: %5\n" + "Data Path: %6" + "\n" + "This likely means that a previous update attempt failed. Please ensure your installation is in working order before " + "proceeding.\n" + "Check the Prism Launcher updater log at \n" + "%7\n" + "for details on the last update attempt.\n" + "\n" + "To overwrite this lock and proceed with this update anyway, select \"Ignore\" below.") + .arg(update_lock_path) + .arg(timestamp.toString(Qt::ISODate), from, to, target, data_path) + .arg(m_updateLogPath); + QMessageBox msgBox; + msgBox.setText(msg); + msgBox.setInformativeText(infoMsg); + msgBox.setStandardButtons(QMessageBox::Ignore | QMessageBox::Cancel); + msgBox.setDefaultButton(QMessageBox::Cancel); + switch (msgBox.exec()) { + case QMessageBox::Ignore: + break; + case QMessageBox::Cancel: + [[fallthrough]]; + default: + return showFatalErrorMessage(tr("Update Aborted"), tr("The update attempt was aborted")); + } + } + write_lock_file(update_lock_path, QDateTime::currentDateTime(), m_prismVersion, m_install_release.tag_name, applicationDirPath(), + m_dataPath); clearUpdateLog(); + auto changelog_path = FS::PathCombine(m_dataPath, ".prism_launcher_update.changelog"); + FS::write(changelog_path, m_install_release.body.toUtf8()); + logUpdate(tr("Updating from %1 to %2").arg(m_prismVersion).arg(m_install_release.tag_name)); if (m_isPortable || file.suffix().toLower() == "zip") { logUpdate(tr("Updating portable install at %1").arg(applicationDirPath())); @@ -779,14 +958,13 @@ void PrismUpdaterApp::unpackAndInstall(QFileInfo archive) if (auto loc = unpackArchive(archive)) { auto marker_file_path = loc.value().absoluteFilePath(".prism_launcher_updater_unpack.marker"); FS::write(marker_file_path, applicationDirPath().toUtf8()); - auto new_updater_path = loc.value().absoluteFilePath("prismlauncher-updater"); + auto new_updater_path = loc.value().absoluteFilePath("prismlauncher_updater"); #if defined Q_OS_WIN32 new_updater_path.append(".exe"); #endif logUpdate(tr("Starting new updater at '%1'").arg(new_updater_path)); QProcess proc = QProcess(); - proc.startDetached(new_updater_path, {}, loc.value().absolutePath()); - if (!proc.waitForStarted(5000)) { + if (!proc.startDetached(new_updater_path, { "-d", m_dataPath }, loc.value().absolutePath())) { logUpdate(tr("Failed to launch '%1' %2").arg(new_updater_path).arg(proc.errorString())); return exit(10); } @@ -807,7 +985,10 @@ void PrismUpdaterApp::backupAppDir() logUpdate(tr("Reading manifest from %1").arg(manifest.absoluteFilePath())); try { auto contents = QString::fromUtf8(FS::read(manifest.absoluteFilePath())); - file_list.append(contents.split('\n')); + auto files = contents.split('\n'); + for (auto file : files) { + file_list.append(file.trimmed()); + } } catch (FS::FileSystemException) { } } @@ -815,19 +996,19 @@ void PrismUpdaterApp::backupAppDir() if (file_list.isEmpty()) { // best guess if (BuildConfig.BUILD_PLATFORM.toLower() == "linux") { - file_list.append({ "PrismLauncher", "bin/*", "share/*", "lib/*" }); + file_list.append({ "PrismLauncher", "bin", "share", "lib" }); } else { // windows by process of elimination file_list.append({ - "jars/*", + "jars", "prismlauncher.exe", "prismlauncher_filelink.exe", + "prismlauncher_updater.exe", "qtlogging.ini", - "imageformats/*", - "iconengines/*", - "platforms/*", - "styles/*", - "styles/*", - "tls/*", + "imageformats", + "iconengines", + "platforms", + "styles", + "tls", "qt.conf", "Qt*.dll", }); @@ -839,32 +1020,41 @@ void PrismUpdaterApp::backupAppDir() QDir app_dir = QCoreApplication::applicationDirPath(); auto backup_dir = FS::PathCombine( - app_dir.absolutePath(), QStringLiteral("backup_") + - QString(m_prismVersion).replace(QRegularExpression("[" + QRegularExpression::escape("\\/:*?\"<>|") + "]"), QString("_")) + - "-" + m_prismGitCommit); + app_dir.absolutePath(), + QStringLiteral("backup_") + + QString(m_prismVersion).replace(QRegularExpression("[" + QRegularExpression::escape("\\/:*?\"<>|") + "]"), QString("_")) + "-" + + m_prismGitCommit); FS::ensureFolderPathExists(backup_dir); + auto backup_marker_path = FS::PathCombine(m_dataPath, ".prism_launcher_update_backup_path.txt"); + FS::write(backup_marker_path, backup_dir.toUtf8()); + QProgressDialog progress(tr("Backing up install at %1").arg(applicationDirPath()), "", 0, file_list.length()); + progress.setCancelButton(nullptr); + progress.show(); + QCoreApplication::processEvents(); + int i = 0; for (auto glob : file_list) { - QDirIterator iter(app_dir.absolutePath(), QStringList({ glob }), QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot, - QDirIterator::Subdirectories); + QDirIterator iter(app_dir.absolutePath(), QStringList({ glob }), QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); + progress.setValue(i); + QCoreApplication::processEvents(); while (iter.hasNext()) { auto to_bak_file = iter.next(); auto rel_path = app_dir.relativeFilePath(to_bak_file); auto bak_path = FS::PathCombine(backup_dir, rel_path); - - if (QFileInfo(to_bak_file).isFile()) { - logUpdate(tr("Backing up and then removing %1").arg(to_bak_file)); - FS::ensureFilePathExists(bak_path); - auto result = FS::copy(to_bak_file, bak_path)(); - if (!result) { - logUpdate(tr("Failed to backup %1 to %2").arg(to_bak_file).arg(bak_path)); - } else { - if (!FS::deletePath(to_bak_file)) - logUpdate(tr("Failed to remove %1").arg(to_bak_file)); - } + logUpdate(tr("Backing up and then removing %1").arg(to_bak_file)); + FS::ensureFilePathExists(bak_path); + auto result = FS::copy(to_bak_file, bak_path)(); + if (!result) { + logUpdate(tr("Failed to backup %1 to %2").arg(to_bak_file).arg(bak_path)); + } else { + if (!FS::deletePath(to_bak_file)) + logUpdate(tr("Failed to remove %1").arg(to_bak_file)); } } + i++; } + progress.setValue(i); + QCoreApplication::processEvents(); } std::optional PrismUpdaterApp::unpackArchive(QFileInfo archive) diff --git a/launcher/updater/prismupdater/PrismUpdater.h b/launcher/updater/prismupdater/PrismUpdater.h index 909e36056..113747b75 100644 --- a/launcher/updater/prismupdater/PrismUpdater.h +++ b/launcher/updater/prismupdater/PrismUpdater.h @@ -85,7 +85,7 @@ class PrismUpdaterApp : public QApplication { QFileInfo downloadAsset(const GitHubReleaseAsset& asset); bool callAppImageUpdate(); - void moveAndPostProcess(QDir target); + void moveAndFinishUpdate(QDir target); public slots: void downloadError(QString reason); @@ -98,7 +98,6 @@ class PrismUpdaterApp : public QApplication { void clearUpdateLog(); void logUpdate(const QString& msg); - QString m_rootPath; QString m_dataPath; bool m_isPortable = false; @@ -114,6 +113,8 @@ class PrismUpdaterApp : public QApplication { bool m_selectUI; bool m_allowDowngrade; + QString m_updateLogPath; + QString m_prismBinaryName; QString m_prismVersion; int m_prismVersionMajor = -1; diff --git a/launcher/updater/prismupdater/SelectReleaseDialog.ui b/launcher/updater/prismupdater/SelectReleaseDialog.ui index 9d3613727..a1aa38371 100644 --- a/launcher/updater/prismupdater/SelectReleaseDialog.ui +++ b/launcher/updater/prismupdater/SelectReleaseDialog.ui @@ -6,8 +6,8 @@ 0 0 - 478 - 517 + 468 + 385 From e099a3f7e8846a293d30a884498929ae9f5b2f07 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sat, 24 Jun 2023 22:22:24 -0700 Subject: [PATCH 056/268] feat(updater): packaging - know the updater git repo Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/build.yml | 6 +++--- buildconfig/BuildConfig.cpp.in | 4 ++-- buildconfig/BuildConfig.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3f93337b5..48be91ac8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -277,12 +277,12 @@ 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=6 -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 -DLauncher_UPDATER_GITHUB_REPO=https://github.com/${{ github.repository }} -G Ninja - 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}} -DLauncher_FORCE_BUNDLED_LIBS=ON + 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 -DLauncher_UPDATER_GITHUB_REPO=https://github.com/${{ github.repository }} # https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix) if ("${{ env.CCACHE_VAR }}") { @@ -297,7 +297,7 @@ jobs: - name: Configure CMake (Linux) if: runner.os == 'Linux' run: | - cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=Linux -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -G Ninja + cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=Linux -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DLauncher_UPDATER_GITHUB_REPO=https://github.com/${{ github.repository }} -G Ninja ## # BUILD diff --git a/buildconfig/BuildConfig.cpp.in b/buildconfig/BuildConfig.cpp.in index 8a412b7ff..b03867c3c 100644 --- a/buildconfig/BuildConfig.cpp.in +++ b/buildconfig/BuildConfig.cpp.in @@ -60,7 +60,7 @@ Config::Config() BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@"; BUILD_DATE = "@Launcher_BUILD_TIMESTAMP@"; - UPDATER_BASE = "@Launcher_UPDATER_BASE@"; + UPDATER_GITHUB_REPO = "@Launcher_UPDATER_GITHUB_REPO@"; MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@"; MAC_SPARKLE_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@"; @@ -89,7 +89,7 @@ Config::Config() { VERSION_CHANNEL = GIT_REFSPEC; VERSION_CHANNEL.remove("refs/heads/"); - if(!UPDATER_BASE.isEmpty() && !BUILD_PLATFORM.isEmpty()) { + if(!UPDATER_GITHUB_REPO.isEmpty() && !BUILD_PLATFORM.isEmpty()) { UPDATER_ENABLED = true; } } diff --git a/buildconfig/BuildConfig.h b/buildconfig/BuildConfig.h index 8543d7241..b6ae6e6d8 100644 --- a/buildconfig/BuildConfig.h +++ b/buildconfig/BuildConfig.h @@ -75,7 +75,7 @@ class Config { QString BUILD_DATE; /// URL for the updater's channel - QString UPDATER_BASE; + QString UPDATER_GITHUB_REPO; /// The public key used to sign releases for the Sparkle updater appcast QString MAC_SPARKLE_PUB_KEY; From 4b65315cdc18b1a4687d8dd1922dc7b10abc8a51 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 25 Jun 2023 14:21:55 -0700 Subject: [PATCH 058/268] chore: add license to headers Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .../updater/prismupdater/GitHubRelease.cpp | 103 ++++++++++++++---- launcher/updater/prismupdater/GitHubRelease.h | 23 +++- launcher/updater/prismupdater/PrismUpdater.h | 2 +- .../updater/prismupdater/UpdaterDialogs.cpp | 72 +++++++----- .../updater/prismupdater/UpdaterDialogs.h | 24 +++- .../updater/prismupdater/updater_main.cpp | 10 +- 6 files changed, 176 insertions(+), 58 deletions(-) diff --git a/launcher/updater/prismupdater/GitHubRelease.cpp b/launcher/updater/prismupdater/GitHubRelease.cpp index 25e60e885..3beae31b1 100644 --- a/launcher/updater/prismupdater/GitHubRelease.cpp +++ b/launcher/updater/prismupdater/GitHubRelease.cpp @@ -1,36 +1,93 @@ +// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> +// +// SPDX-License-Identifier: GPL-3.0-only + +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + #include "GitHubRelease.h" QDebug operator<<(QDebug debug, const GitHubReleaseAsset& asset) { QDebugStateSaver saver(debug); - debug.nospace() << "GitHubReleaseAsset( " - "id: " << asset.id << ", " - "name " << asset.name << ", " - "label: " << asset.label << ", " - "content_type: " << asset.content_type << ", " - "size: " << asset.size << ", " - "created_at: " << asset.created_at << ", " - "updated_at: " << asset.updated_at << ", " - "browser_download_url: " << asset.browser_download_url << " " - ")"; + debug.nospace() << "GitHubReleaseAsset( " + "id: " + << asset.id + << ", " + "name " + << asset.name + << ", " + "label: " + << asset.label + << ", " + "content_type: " + << asset.content_type + << ", " + "size: " + << asset.size + << ", " + "created_at: " + << asset.created_at + << ", " + "updated_at: " + << asset.updated_at + << ", " + "browser_download_url: " + << asset.browser_download_url + << " " + ")"; return debug; } QDebug operator<<(QDebug debug, const GitHubRelease& rls) { QDebugStateSaver saver(debug); - debug.nospace() << "GitHubRelease( " - "id: " << rls.id << ", " - "name " << rls.name << ", " - "tag_name: " << rls.tag_name << ", " - "created_at: " << rls.created_at << ", " - "published_at: " << rls.published_at << ", " - "prerelease: " << rls.prerelease << ", " - "draft: " << rls.draft << ", " - "version" << rls.version << ", " - "body: " << rls.body << ", " - "assets: " << rls.assets << " " - ")"; + debug.nospace() << "GitHubRelease( " + "id: " + << rls.id + << ", " + "name " + << rls.name + << ", " + "tag_name: " + << rls.tag_name + << ", " + "created_at: " + << rls.created_at + << ", " + "published_at: " + << rls.published_at + << ", " + "prerelease: " + << rls.prerelease + << ", " + "draft: " + << rls.draft + << ", " + "version" + << rls.version + << ", " + "body: " + << rls.body + << ", " + "assets: " + << rls.assets + << " " + ")"; return debug; } - diff --git a/launcher/updater/prismupdater/GitHubRelease.h b/launcher/updater/prismupdater/GitHubRelease.h index 0c190333f..798c6b7ae 100644 --- a/launcher/updater/prismupdater/GitHubRelease.h +++ b/launcher/updater/prismupdater/GitHubRelease.h @@ -1,3 +1,25 @@ +// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> +// +// SPDX-License-Identifier: GPL-3.0-only + +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + #pragma once #include #include @@ -37,4 +59,3 @@ struct GitHubRelease { QDebug operator<<(QDebug debug, const GitHubReleaseAsset& rls); QDebug operator<<(QDebug debug, const GitHubRelease& rls); - diff --git a/launcher/updater/prismupdater/PrismUpdater.h b/launcher/updater/prismupdater/PrismUpdater.h index 113747b75..f879ad7ad 100644 --- a/launcher/updater/prismupdater/PrismUpdater.h +++ b/launcher/updater/prismupdater/PrismUpdater.h @@ -42,7 +42,7 @@ #define PRISM_EXTERNAL_EXE #include "FileSystem.h" -#include "updater/prismupdater/GitHubRelease.h" +#include "GitHubRelease.h" class PrismUpdaterApp : public QApplication { // friends for the purpose of limiting access to deprecated stuff diff --git a/launcher/updater/prismupdater/UpdaterDialogs.cpp b/launcher/updater/prismupdater/UpdaterDialogs.cpp index 14c584918..395b658db 100644 --- a/launcher/updater/prismupdater/UpdaterDialogs.cpp +++ b/launcher/updater/prismupdater/UpdaterDialogs.cpp @@ -1,3 +1,25 @@ +// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> +// +// SPDX-License-Identifier: GPL-3.0-only + +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + #include "UpdaterDialogs.h" #include "ui_SelectReleaseDialog.h" @@ -9,25 +31,25 @@ SelectReleaseDialog::SelectReleaseDialog(const Version& current_version, const Q : QDialog(parent), m_releases(releases), m_currentVersion(current_version), ui(new Ui::SelectReleaseDialog) { ui->setupUi(this); - + ui->changelogTextBrowser->setOpenExternalLinks(true); ui->changelogTextBrowser->setLineWrapMode(QTextBrowser::LineWrapMode::WidgetWidth); ui->changelogTextBrowser->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded); - + ui->versionsTree->setColumnCount(2); ui->versionsTree->header()->setSectionResizeMode(0, QHeaderView::Stretch); - ui->versionsTree->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); - ui->versionsTree->setHeaderLabels({tr("Version"), tr("Published Date")}); + ui->versionsTree->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); + ui->versionsTree->setHeaderLabels({ tr("Version"), tr("Published Date") }); ui->versionsTree->header()->setStretchLastSection(false); - + ui->eplainLabel->setText(tr("Select a version to install.\n" - "\n" - "Currently installed version: %1") - .arg(m_currentVersion.toString())); - + "\n" + "Currently installed version: %1") + .arg(m_currentVersion.toString())); + loadReleases(); - + connect(ui->versionsTree, &QTreeWidget::currentItemChanged, this, &SelectReleaseDialog::selectionChanged); connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &SelectReleaseDialog::accept); @@ -57,10 +79,11 @@ void SelectReleaseDialog::appendRelease(GitHubRelease const& release) ui->versionsTree->addTopLevelItem(rls_item); } -GitHubRelease SelectReleaseDialog::getRelease(QTreeWidgetItem* item) { +GitHubRelease SelectReleaseDialog::getRelease(QTreeWidgetItem* item) +{ int id = item->data(0, Qt::UserRole).toInt(); GitHubRelease release; - for (auto rls: m_releases) { + for (auto rls : m_releases) { if (rls.id == id) release = rls; } @@ -76,30 +99,28 @@ void SelectReleaseDialog::selectionChanged(QTreeWidgetItem* current, QTreeWidget ui->changelogTextBrowser->setHtml(body); } - - -SelectReleaseAssetDialog::SelectReleaseAssetDialog( const QList& assets, QWidget* parent) - : QDialog(parent), m_assets(assets), ui(new Ui::SelectReleaseDialog) +SelectReleaseAssetDialog::SelectReleaseAssetDialog(const QList& assets, QWidget* parent) + : QDialog(parent), m_assets(assets), ui(new Ui::SelectReleaseDialog) { ui->setupUi(this); - + ui->changelogTextBrowser->setOpenExternalLinks(true); ui->changelogTextBrowser->setLineWrapMode(QTextBrowser::LineWrapMode::WidgetWidth); ui->changelogTextBrowser->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded); - + ui->versionsTree->setColumnCount(2); ui->versionsTree->header()->setSectionResizeMode(0, QHeaderView::Stretch); - ui->versionsTree->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); - ui->versionsTree->setHeaderLabels({tr("Version"), tr("Published Date")}); + ui->versionsTree->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); + ui->versionsTree->setHeaderLabels({ tr("Version"), tr("Published Date") }); ui->versionsTree->header()->setStretchLastSection(false); - + ui->eplainLabel->setText(tr("Select a version to install.")); ui->changelogTextBrowser->setHidden(true); - + loadAssets(); - + connect(ui->versionsTree, &QTreeWidget::currentItemChanged, this, &SelectReleaseAssetDialog::selectionChanged); connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &SelectReleaseAssetDialog::accept); @@ -129,10 +150,11 @@ void SelectReleaseAssetDialog::appendAsset(GitHubReleaseAsset const& asset) ui->versionsTree->addTopLevelItem(rls_item); } -GitHubReleaseAsset SelectReleaseAssetDialog::getAsset(QTreeWidgetItem* item) { +GitHubReleaseAsset SelectReleaseAssetDialog::getAsset(QTreeWidgetItem* item) +{ int id = item->data(0, Qt::UserRole).toInt(); GitHubReleaseAsset selected_asset; - for (auto asset: m_assets) { + for (auto asset : m_assets) { if (asset.id == id) selected_asset = asset; } diff --git a/launcher/updater/prismupdater/UpdaterDialogs.h b/launcher/updater/prismupdater/UpdaterDialogs.h index c5e31b5a4..249ca1b5d 100644 --- a/launcher/updater/prismupdater/UpdaterDialogs.h +++ b/launcher/updater/prismupdater/UpdaterDialogs.h @@ -1,10 +1,32 @@ +// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> +// +// SPDX-License-Identifier: GPL-3.0-only + +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + #pragma once #include #include #include "Version.h" -#include "updater/prismupdater/GitHubRelease.h" +#include "GitHubRelease.h" namespace Ui { class SelectReleaseDialog; diff --git a/launcher/updater/prismupdater/updater_main.cpp b/launcher/updater/prismupdater/updater_main.cpp index 6bc7dd208..89c1d1198 100644 --- a/launcher/updater/prismupdater/updater_main.cpp +++ b/launcher/updater/prismupdater/updater_main.cpp @@ -20,17 +20,14 @@ * */ - - -#include "updater/prismupdater/PrismUpdater.h" +#include "PrismUpdater.h" int main(int argc, char* argv[]) { PrismUpdaterApp wUpApp(argc, argv); - switch(wUpApp.status()) { + switch (wUpApp.status()) { case PrismUpdaterApp::Starting: - case PrismUpdaterApp::Initialized: - { + case PrismUpdaterApp::Initialized: { return wUpApp.exec(); } case PrismUpdaterApp::Failed: @@ -40,5 +37,4 @@ int main(int argc, char* argv[]) default: return -1; } - } From 8aeec1d52d30a008c9b7d815d0e3a8eda82878ca Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 25 Jun 2023 14:36:20 -0700 Subject: [PATCH 059/268] fix: use new shared pointer for repsonse buffer Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/updater/prismupdater/PrismUpdater.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 221c1c649..60e970597 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -1166,7 +1166,7 @@ void PrismUpdaterApp::downloadReleasePage(const QString& api_url, int page) int per_page = 30; auto page_url = QString("%1?per_page=%2&page=%3").arg(api_url).arg(QString::number(per_page)).arg(QString::number(page)); auto response = std::make_shared(); - auto download = Net::Download::makeByteArray(page_url, response.get()); + auto download = Net::Download::makeByteArray(page_url, response); download->setNetwork(m_network); m_current_url = page_url; From 10266f65e409da6ef5132a4433b4e09631d6b77e Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 25 Jun 2023 16:10:06 -0700 Subject: [PATCH 060/268] fix: include `^` updater doesn't need to know about mods Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/MMCZip.cpp | 2 ++ launcher/MMCZip.h | 5 +++++ launcher/updater/prismupdater/PrismUpdater.cpp | 4 ++-- launcher/updater/prismupdater/PrismUpdater.h | 1 + 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp index 1a336375b..33776d972 100644 --- a/launcher/MMCZip.cpp +++ b/launcher/MMCZip.cpp @@ -135,6 +135,7 @@ bool MMCZip::compressDirFiles(QString fileCompressed, QString dir, QFileInfoList return result; } +#if defined(LAUNCHER_APPLICATION) // ours bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const QList& mods) { @@ -235,6 +236,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const } return true; } +#endif // ours QString MMCZip::findFolderOfFileInZip(QuaZip* zip, const QString& what, const QStringList& ignore_paths, const QString& root) diff --git a/launcher/MMCZip.h b/launcher/MMCZip.h index 2a78f830f..ef0e09777 100644 --- a/launcher/MMCZip.h +++ b/launcher/MMCZip.h @@ -38,7 +38,10 @@ #include #include #include +#include +#if defined(LAUNCHER_APPLICATION) #include "minecraft/mod/Mod.h" +#endif #include #include @@ -74,10 +77,12 @@ namespace MMCZip */ bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks = false); +#if defined(LAUNCHER_APPLICATION) /** * take a source jar, add mods to it, resulting in target jar */ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList& mods); +#endif /** * Find a single file in archive by file name (not path) diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 60e970597..3ca4f235e 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -72,7 +72,7 @@ namespace fs = std::filesystem; namespace fs = ghc::filesystem; #endif -#include +#include "DesktopServices.h" #include "updater/prismupdater/UpdaterDialogs.h" @@ -83,7 +83,7 @@ namespace fs = ghc::filesystem; #include "net/Download.h" #include "net/RawHeaderProxy.h" -#include +#include "MMCZip.h" /** output to the log file */ void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg) diff --git a/launcher/updater/prismupdater/PrismUpdater.h b/launcher/updater/prismupdater/PrismUpdater.h index f879ad7ad..90ebeb8d2 100644 --- a/launcher/updater/prismupdater/PrismUpdater.h +++ b/launcher/updater/prismupdater/PrismUpdater.h @@ -35,6 +35,7 @@ #include #include #include +#include #include "QObjectPtr.h" #include "net/Download.h" From d8e0b14dc458a662671092b6941609c85348b38a Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 25 Jun 2023 21:36:20 -0700 Subject: [PATCH 061/268] feat(updater): tie in updater part 1 Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/Application.cpp | 15 +- launcher/CMakeLists.txt | 7 + launcher/StringUtils.cpp | 28 ++- launcher/StringUtils.h | 11 +- launcher/updater/PrismExternalUpdater.cpp | 206 ++++++++++++++++++ launcher/updater/PrismExternalUpdater.h | 94 ++++++++ .../updater/prismupdater/PrismUpdater.cpp | 44 +++- launcher/updater/prismupdater/PrismUpdater.h | 1 + 8 files changed, 390 insertions(+), 16 deletions(-) create mode 100644 launcher/updater/PrismExternalUpdater.cpp create mode 100644 launcher/updater/PrismExternalUpdater.h diff --git a/launcher/Application.cpp b/launcher/Application.cpp index f094a8ec9..a5103e8e5 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -132,6 +132,8 @@ #ifdef Q_OS_MAC #include "updater/MacSparkleUpdater.h" +#else +#include "updater/PrismExternalUpdater.h" #endif #if defined Q_OS_WIN32 @@ -271,10 +273,15 @@ void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr Application::Application(int& argc, char** argv) : QApplication(argc, argv) { #if defined Q_OS_WIN32 - // attach the parent console - if (AttachConsole(ATTACH_PARENT_PROCESS)) { + // attach the parent console if stdout not already captured + auto stdout_type = GetFileType(GetStdHandle(STD_OUTPUT_HANDLE)); + if (stdout_type == FILE_TYPE_CHAR || stdout_type == FILE_TYPE_UNKNOWN) { + if (AttachConsole(ATTACH_PARENT_PROCESS)) { + BindCrtHandlesToStdHandles(true, true, true); + consoleAttached = true; + } + } else if (stdout_type == FILE_TYPE_DISK || stdout_type == FILE_TYPE_PIPE ) { BindCrtHandlesToStdHandles(true, true, true); - consoleAttached = true; } #endif setOrganizationName(BuildConfig.LAUNCHER_NAME); @@ -823,6 +830,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) qDebug() << "Initializing updater"; #ifdef Q_OS_MAC m_updater.reset(new MacSparkleUpdater()); +#else + m_updater.reset(new PrismExternalUpdater(m_rootPath, dataPath)); #endif qDebug() << "<> Updater started."; } diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index dc413bb62..86bf8fc48 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -179,6 +179,11 @@ set(MAC_UPDATE_SOURCES updater/MacSparkleUpdater.mm ) +set(PRISM_UPDATE_SOURCES + updater/PrismExternalUpdater.h + updater/PrismExternalUpdater.cpp +) + # Backend for the news bar... there's usually no news. set(NEWS_SOURCES # News System @@ -728,6 +733,8 @@ set(LOGIC_SOURCES if(APPLE) set (LOGIC_SOURCES ${LOGIC_SOURCES} ${MAC_UPDATE_SOURCES}) +else() + set (LOGIC_SOURCES ${LOGIC_SOURCES} ${PRISM_UPDATE_SOURCES}) endif() SET(LAUNCHER_SOURCES diff --git a/launcher/StringUtils.cpp b/launcher/StringUtils.cpp index e08e6fdce..f437b6dac 100644 --- a/launcher/StringUtils.cpp +++ b/launcher/StringUtils.cpp @@ -35,6 +35,7 @@ */ #include "StringUtils.h" +#include #include #include @@ -149,7 +150,7 @@ QString StringUtils::truncateUrlHumanFriendly(QUrl& url, int max_len, bool hard_ } if ((url_compact.length() >= max_len) && hard_limit) { - // still too long, truncate normaly + // still too long, truncate normally url_compact = QString(str_url); auto to_remove = url_compact.length() - max_len + 3; url_compact.remove(url_compact.length() - to_remove - 1, to_remove); @@ -182,3 +183,28 @@ QString StringUtils::getRandomAlphaNumeric() { return QUuid::createUuid().toString(QUuid::Id128); } + +QPair splitFirst(const QString& s, const QString& sep, Qt::CaseSensitivity cs = Qt::CaseSensitive) { + QString left, right; + auto index = s.indexOf(sep, 0, cs); + left = s.mid(0, index); + right = s.mid(index + 1); + return qMakePair(left, right); +} + +QPair splitFirst(const QString& s, QChar sep, Qt::CaseSensitivity cs = Qt::CaseSensitive) { + QString left, right; + auto index = s.indexOf(sep, 0, cs); + left = s.mid(0, index); + right = s.mid(index + 1); + return qMakePair(left, right); +} + +QPair splitFirst(const QString& s, const QRegularExpression& re) { + QString left, right; + auto index = s.indexOf(re); + left = s.mid(0, index); + right = s.mid(index + 1); + return qMakePair(left, right); +} + diff --git a/launcher/StringUtils.h b/launcher/StringUtils.h index f90a6ac75..343ac9e4d 100644 --- a/launcher/StringUtils.h +++ b/launcher/StringUtils.h @@ -37,7 +37,9 @@ #pragma once #include +#include #include +#include namespace StringUtils { @@ -70,8 +72,8 @@ int naturalCompare(const QString& s1, const QString& s2, Qt::CaseSensitivity cs) /** * @brief Truncate a url while keeping its readability py placing the `...` in the middle of the path * @param url Url to truncate - * @param max_len max lenght of url in charaters - * @param hard_limit if truncating the path can't get the url short enough, truncate it normaly. + * @param max_len max length of url in characters + * @param hard_limit if truncating the path can't get the url short enough, truncate it normally. */ QString truncateUrlHumanFriendly(QUrl &url, int max_len, bool hard_limit = false); @@ -79,4 +81,9 @@ QString humanReadableFileSize(double bytes, bool use_si = false, int decimal_poi QString getRandomAlphaNumeric(); + +QPair splitFirst(const QString& s, const QString& sep, Qt::CaseSensitivity cs = Qt::CaseSensitive); +QPair splitFirst(const QString& s, QChar sep, Qt::CaseSensitivity cs = Qt::CaseSensitive); +QPair splitFirst(const QString& s, const QRegularExpression& re); + } // namespace StringUtils diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp new file mode 100644 index 000000000..f510dfc6c --- /dev/null +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -0,0 +1,206 @@ +// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> +// +// SPDX-License-Identifier: GPL-3.0-only + +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "PrismExternalUpdater.h" +#include +#include +#include +#include +#include +#include +#include + +#include "StringUtils.h" + +#include "BuildConfig.h" + +class PrismExternalUpdater::Private { + public: + QDir appDir; + QDir dataDir; + QTimer updateTimer; + bool allowBeta; + bool autoCheck; + double updateInterval; + QDateTime lastCheck; + std::unique_ptr settings; +}; + +PrismExternalUpdater::PrismExternalUpdater(const QString& appDir, const QString& dataDir) +{ + priv = new PrismExternalUpdater::Private(); + priv->appDir = QDir(appDir); + priv->dataDir = QDir(dataDir); + auto settings_file = priv->dataDir.absoluteFilePath("prismlauncher_update.cfg"); + priv->settings = std::make_unique(settings_file, QSettings::Format::IniFormat); + priv->allowBeta = priv->settings->value("allow_beta", false).toBool(); + priv->autoCheck = priv->settings->value("auto_check", false).toBool(); + bool interval_ok; + priv->updateInterval = priv->settings->value("update_interval", 86400).toInt(&interval_ok); + if (!interval_ok) + priv->updateInterval = 86400; + auto last_check = priv->settings->value("last_check"); + if (!last_check.isNull() && last_check.isValid()) { + priv->lastCheck = QDateTime::fromString(last_check.toString(), Qt::ISODate); + } + connectTimer(); + resetAutoCheckTimer(); +} + +PrismExternalUpdater::~PrismExternalUpdater() +{ + if (priv->updateTimer.isActive()) + priv->updateTimer.stop(); + disconnectTimer(); + priv->settings->sync(); + delete priv; +} + +void PrismExternalUpdater::checkForUpdates() +{ + QProgressDialog progress(tr("Checking for updates..."), "", 0, -1); + progress.setCancelButton(nullptr); + progress.show(); + + QProcess proc; + auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); +#if defined Q_OS_WIN32 + exe_name.append(".exe"); +#endif + + QStringList args = { "--check-only" }; + if (priv->allowBeta) + args.append("--pre-release"); + + proc.start(priv->appDir.absoluteFilePath(exe_name), args); + auto result_start = proc.waitForStarted(5000); + if (!result_start) { + auto err = proc.error(); + qDebug() << "Failed to start updater after 5 seconds." << "reason:" << err << proc.errorString(); + } + auto result_finished = proc.waitForFinished(60000); + if (!result_finished) { + auto err = proc.error(); + qDebug() << "Updater failed to close after 60 seconds." << "reason:" << err << proc.errorString(); + } + + auto exit_code = proc.exitCode(); + + auto std_output = proc.readAllStandardOutput(); + auto std_error = proc.readAllStandardError(); + + switch (exit_code) { + case 0: + // no update available + { + qDebug() << "No update available"; + } + break; + case 1: + // there was an error + { + qDebug() << "Updater subprocess error" << std_error; + } + break; + case 100: + // update available + { + auto [first_line, remainder1] = StringUtils::splitFirst(std_output, '\n'); + auto [second_line, remainder2] = StringUtils::splitFirst(remainder1, '\n'); + auto [third_line, changelog] = StringUtils::splitFirst(remainder2, '\n'); + auto version_name = StringUtils::splitFirst(first_line, ": ").second; + auto version_tag = StringUtils::splitFirst(second_line, ": ").second; + auto release_timestamp = QDateTime::fromString(StringUtils::splitFirst(third_line, ": ").second, Qt::ISODate); + qDebug() << "Update available:" << version_name << version_tag << release_timestamp; + qDebug() << "Update changelog:" << changelog; + } + break; + default: + // unknown error code + { + qDebug() << "Updater exited with unknown code" << exit_code; + } + } + priv->lastCheck = QDateTime::currentDateTime(); + priv->settings->setValue("last_check", priv->lastCheck.toString(Qt::ISODate)); + priv->settings->sync(); +} + +bool PrismExternalUpdater::getAutomaticallyChecksForUpdates() { + return priv->autoCheck; +} + +double PrismExternalUpdater::getUpdateCheckInterval() { + return priv->updateInterval; +} + +bool PrismExternalUpdater::getBetaAllowed() { + return priv->allowBeta; +} + +void PrismExternalUpdater::setAutomaticallyChecksForUpdates(bool check) { + priv->autoCheck = check; + priv->settings->setValue("auto_check", check); + priv->settings->sync(); + resetAutoCheckTimer(); +} + +void PrismExternalUpdater::setUpdateCheckInterval(double seconds) { + priv->updateInterval = seconds; + priv->settings->setValue("update_interval", seconds); + priv->settings->sync(); + resetAutoCheckTimer(); +} + +void PrismExternalUpdater::setBetaAllowed(bool allowed) { + priv->allowBeta = allowed; + priv->settings->setValue("auto_beta", allowed); + priv->settings->sync(); +} + +void PrismExternalUpdater::resetAutoCheckTimer() { + int timeoutDuration = 0; + auto now = QDateTime::currentDateTime(); + if (priv->autoCheck) { + if (priv->lastCheck.isValid()) { + auto diff = priv->lastCheck.secsTo(now); + auto secs_left = priv->updateInterval - diff; + if (secs_left < 0) + secs_left = 0; + timeoutDuration = secs_left * 1000; // to msec + } + priv->updateTimer.start(timeoutDuration); + } else { + if (priv->updateTimer.isActive()) + priv->updateTimer.stop(); + } + +} + +void PrismExternalUpdater::connectTimer() { + connect(&priv->updateTimer, &QTimer::timeout, this, &PrismExternalUpdater::autoCheckTimerFired); + +} + +void PrismExternalUpdater::disconnectTimer() { + disconnect(&priv->updateTimer, &QTimer::timeout, this, &PrismExternalUpdater::autoCheckTimerFired); +} diff --git a/launcher/updater/PrismExternalUpdater.h b/launcher/updater/PrismExternalUpdater.h new file mode 100644 index 000000000..852fb7d37 --- /dev/null +++ b/launcher/updater/PrismExternalUpdater.h @@ -0,0 +1,94 @@ +// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> +// +// SPDX-License-Identifier: GPL-3.0-only + +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#pragma once + +#include + +#include "ExternalUpdater.h" + +/*! + * An implementation for the updater on windows and linux that uses out external updater. + */ + +class PrismExternalUpdater : public ExternalUpdater { + Q_OBJECT + + public: + PrismExternalUpdater(const QString& appDir, const QString& dataDir); + ~PrismExternalUpdater() override; + + /*! + * Check for updates manually, showing the user a progress bar and an alert if no updates are found. + */ + void checkForUpdates() override; + + /*! + * Indicates whether or not to check for updates automatically. + */ + bool getAutomaticallyChecksForUpdates() override; + + /*! + * Indicates the current automatic update check interval in seconds. + */ + double getUpdateCheckInterval() override; + + /*! + * Indicates whether or not beta updates should be checked for in addition to regular releases. + */ + bool getBetaAllowed() override; + + /*! + * Set whether or not to check for updates automatically. + * + * The update schedule cycle will be reset in a short delay after the property’s new value is set. This is to allow + * reverting this property without kicking off a schedule change immediately." + */ + void setAutomaticallyChecksForUpdates(bool check) override; + + /*! + * Set the current automatic update check interval in seconds. + * + * The update schedule cycle will be reset in a short delay after the property’s new value is set. This is to allow + * reverting this property without kicking off a schedule change immediately." + */ + void setUpdateCheckInterval(double seconds) override; + + /*! + * Set whether or not beta updates should be checked for in addition to regular releases. + */ + void setBetaAllowed(bool allowed) override; + + void resetAutoCheckTimer(); + void disconnectTimer(); + void connectTimer(); + + void performUpdate(); + + public slots: + void autoCheckTimerFired(); + + private: + class Private; + + Private* priv; +}; diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 3ca4f235e..f04a12c94 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -41,6 +41,7 @@ #include #include +#include #if defined Q_OS_WIN32 #ifndef WIN32_LEAN_AND_MEAN @@ -202,11 +203,17 @@ void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, argv) { #if defined Q_OS_WIN32 - // attach the parent console - if (AttachConsole(ATTACH_PARENT_PROCESS)) { + // attach the parent console if stdout not already captured + auto stdout_type = GetFileType(GetStdHandle(STD_OUTPUT_HANDLE)); + if (stdout_type == FILE_TYPE_CHAR || stdout_type == FILE_TYPE_UNKNOWN) { + if (AttachConsole(ATTACH_PARENT_PROCESS)) { + BindCrtHandlesToStdHandles(true, true, true); + consoleAttached = true; + } + } else if (stdout_type == FILE_TYPE_DISK || stdout_type == FILE_TYPE_PIPE ) { BindCrtHandlesToStdHandles(true, true, true); - consoleAttached = true; } + #endif setOrganizationName(BuildConfig.LAUNCHER_NAME); setOrganizationDomain(BuildConfig.LAUNCHER_DOMAIN); @@ -226,6 +233,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar { { "U", "update-url" }, tr("Update from the specified repo."), tr("github repo url") }, { { "c", "check-only" }, tr("Only check if an update is needed. Exit status 100 if true, 0 if false (or non 0 if there was an error).") }, + { { "p", "pre-release" }, tr("Allow updating to pre-release releases") }, { { "F", "force" }, tr("Force an update, even if one is not needed.") }, { { "l", "list" }, tr("List available releases.") }, { "debug", tr("Log debug to console.") }, @@ -297,6 +305,8 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar m_prismVersionMinor = version_parts.takeFirst().toInt(); } + m_allowPreRelease = parser.isSet("pre-release"); + QString origCwdPath = QDir::currentPath(); QString binPath = applicationDirPath(); @@ -542,10 +552,19 @@ void PrismUpdaterApp::run() auto need_update = needUpdate(latest); if (m_checkOnly) { - if (need_update) + if (need_update) { + QTextStream stdOutStream(stdout); + stdOutStream << "Name: " << latest.name << "\n"; + stdOutStream << "Version: " << latest.tag_name << "\n"; + stdOutStream << "TimeStamp: " << latest.created_at.toString(Qt::ISODate) << "\n"; + stdOutStream << latest.body << "\n"; + stdOutStream.flush(); + return exit(100); - else + } + else { return exit(0); + } } if (m_isFlatpak) { @@ -958,10 +977,11 @@ void PrismUpdaterApp::unpackAndInstall(QFileInfo archive) if (auto loc = unpackArchive(archive)) { auto marker_file_path = loc.value().absoluteFilePath(".prism_launcher_updater_unpack.marker"); FS::write(marker_file_path, applicationDirPath().toUtf8()); - auto new_updater_path = loc.value().absoluteFilePath("prismlauncher_updater"); + auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); #if defined Q_OS_WIN32 - new_updater_path.append(".exe"); + exe_name.append(".exe"); #endif + auto new_updater_path = loc.value().absoluteFilePath(exe_name); logUpdate(tr("Starting new updater at '%1'").arg(new_updater_path)); QProcess proc = QProcess(); if (!proc.startDetached(new_updater_path, { "-d", m_dataPath }, loc.value().absolutePath())) { @@ -1110,7 +1130,7 @@ bool PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) QProcess proc = QProcess(); proc.setProcessChannelMode(QProcess::MergedChannels); proc.setReadChannel(QProcess::StandardOutput); - proc.start(exe_path, { "-v" }); + proc.start(exe_path, { "--version" }); if (!proc.waitForStarted(5000)) { showFatalErrorMessage(tr("Failed to Check Version"), tr("Failed to launcher child launcher process to read version.")); return false; @@ -1119,7 +1139,7 @@ bool PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) showFatalErrorMessage(tr("Failed to Check Version"), tr("Child launcher process failed.")); return false; } - auto out = proc.readAll(); + auto out = proc.readAllStandardOutput(); auto lines = out.split('\n'); if (lines.length() < 2) return false; @@ -1250,7 +1270,11 @@ GitHubRelease PrismUpdaterApp::getLatestRelease() { GitHubRelease latest; for (auto release : m_releases) { - if (!latest.isValid() || (!release.draft && release.version > latest.version)) { + if (release.draft) + continue; + if (release.prerelease && !m_allowPreRelease) + continue; + if (!latest.isValid() || (release.version > latest.version)) { latest = release; } } diff --git a/launcher/updater/prismupdater/PrismUpdater.h b/launcher/updater/prismupdater/PrismUpdater.h index 90ebeb8d2..f3dd6e062 100644 --- a/launcher/updater/prismupdater/PrismUpdater.h +++ b/launcher/updater/prismupdater/PrismUpdater.h @@ -113,6 +113,7 @@ class PrismUpdaterApp : public QApplication { bool m_printOnly; bool m_selectUI; bool m_allowDowngrade; + bool m_allowPreRelease; QString m_updateLogPath; From b7dd32274ca3b04b66ed76f6bb68b77ab5acb338 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 25 Jun 2023 22:30:20 -0700 Subject: [PATCH 062/268] Proper capture on windows Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/Application.cpp | 2 -- launcher/StringUtils.cpp | 6 +++--- launcher/updater/PrismExternalUpdater.cpp | 15 ++++++++++++++- launcher/updater/prismupdater/PrismUpdater.cpp | 6 +----- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index a5103e8e5..4c88a950d 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -280,8 +280,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) BindCrtHandlesToStdHandles(true, true, true); consoleAttached = true; } - } else if (stdout_type == FILE_TYPE_DISK || stdout_type == FILE_TYPE_PIPE ) { - BindCrtHandlesToStdHandles(true, true, true); } #endif setOrganizationName(BuildConfig.LAUNCHER_NAME); diff --git a/launcher/StringUtils.cpp b/launcher/StringUtils.cpp index f437b6dac..b54299721 100644 --- a/launcher/StringUtils.cpp +++ b/launcher/StringUtils.cpp @@ -184,7 +184,7 @@ QString StringUtils::getRandomAlphaNumeric() return QUuid::createUuid().toString(QUuid::Id128); } -QPair splitFirst(const QString& s, const QString& sep, Qt::CaseSensitivity cs = Qt::CaseSensitive) { +QPair StringUtils::splitFirst(const QString& s, const QString& sep, Qt::CaseSensitivity cs) { QString left, right; auto index = s.indexOf(sep, 0, cs); left = s.mid(0, index); @@ -192,7 +192,7 @@ QPair splitFirst(const QString& s, const QString& sep, Qt::Cas return qMakePair(left, right); } -QPair splitFirst(const QString& s, QChar sep, Qt::CaseSensitivity cs = Qt::CaseSensitive) { +QPair StringUtils::splitFirst(const QString& s, QChar sep, Qt::CaseSensitivity cs) { QString left, right; auto index = s.indexOf(sep, 0, cs); left = s.mid(0, index); @@ -200,7 +200,7 @@ QPair splitFirst(const QString& s, QChar sep, Qt::CaseSensitiv return qMakePair(left, right); } -QPair splitFirst(const QString& s, const QRegularExpression& re) { +QPair StringUtils::splitFirst(const QString& s, const QRegularExpression& re) { QString left, right; auto index = s.indexOf(re); left = s.mid(0, index); diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index f510dfc6c..9ec033f02 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include "StringUtils.h" @@ -80,6 +81,8 @@ void PrismExternalUpdater::checkForUpdates() QProgressDialog progress(tr("Checking for updates..."), "", 0, -1); progress.setCancelButton(nullptr); progress.show(); + QCoreApplication::processEvents(); + QProcess proc; auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); @@ -87,7 +90,7 @@ void PrismExternalUpdater::checkForUpdates() exe_name.append(".exe"); #endif - QStringList args = { "--check-only" }; + QStringList args = { "--check-only", "--dir", priv->dataDir.absolutePath(), "--debug" }; if (priv->allowBeta) args.append("--pre-release"); @@ -97,6 +100,8 @@ void PrismExternalUpdater::checkForUpdates() auto err = proc.error(); qDebug() << "Failed to start updater after 5 seconds." << "reason:" << err << proc.errorString(); } + QCoreApplication::processEvents(); + auto result_finished = proc.waitForFinished(60000); if (!result_finished) { auto err = proc.error(); @@ -108,6 +113,9 @@ void PrismExternalUpdater::checkForUpdates() auto std_output = proc.readAllStandardOutput(); auto std_error = proc.readAllStandardError(); + qDebug() << "captured output:" << std_output; + qDebug() << "captured error:" << std_error; + switch (exit_code) { case 0: // no update available @@ -188,6 +196,7 @@ void PrismExternalUpdater::resetAutoCheckTimer() { secs_left = 0; timeoutDuration = secs_left * 1000; // to msec } + qDebug() << "Auto update timer starting," << timeoutDuration / 1000 << "seconds left"; priv->updateTimer.start(timeoutDuration); } else { if (priv->updateTimer.isActive()) @@ -204,3 +213,7 @@ void PrismExternalUpdater::connectTimer() { void PrismExternalUpdater::disconnectTimer() { disconnect(&priv->updateTimer, &QTimer::timeout, this, &PrismExternalUpdater::autoCheckTimerFired); } + +void PrismExternalUpdater::autoCheckTimerFired() { + checkForUpdates(); +} diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index f04a12c94..e01295605 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -41,7 +41,6 @@ #include #include -#include #if defined Q_OS_WIN32 #ifndef WIN32_LEAN_AND_MEAN @@ -210,10 +209,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar BindCrtHandlesToStdHandles(true, true, true); consoleAttached = true; } - } else if (stdout_type == FILE_TYPE_DISK || stdout_type == FILE_TYPE_PIPE ) { - BindCrtHandlesToStdHandles(true, true, true); - } - + } #endif setOrganizationName(BuildConfig.LAUNCHER_NAME); setOrganizationDomain(BuildConfig.LAUNCHER_DOMAIN); From 1f70589debda457b8f43426f7283d9c47a8129f1 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Mon, 26 Jun 2023 01:22:33 -0700 Subject: [PATCH 063/268] feat(updater): tie in part 2, let there be UI! Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/Application.cpp | 115 ++++++++++++- launcher/Application.h | 6 + launcher/CMakeLists.txt | 17 ++ launcher/FileSystem.cpp | 2 +- launcher/ui/dialogs/UpdateAvailableDialog.cpp | 63 +++++++ launcher/ui/dialogs/UpdateAvailableDialog.h | 49 ++++++ launcher/ui/dialogs/UpdateAvailableDialog.ui | 155 ++++++++++++++++++ launcher/updater/PrismExternalUpdater.cpp | 112 ++++++++++--- launcher/updater/PrismExternalUpdater.h | 5 +- .../updater/prismupdater/PrismUpdater.cpp | 24 ++- 10 files changed, 512 insertions(+), 36 deletions(-) create mode 100644 launcher/ui/dialogs/UpdateAvailableDialog.cpp create mode 100644 launcher/ui/dialogs/UpdateAvailableDialog.h create mode 100644 launcher/ui/dialogs/UpdateAvailableDialog.ui diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 4c88a950d..a04f85c28 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -122,6 +122,7 @@ #include #include +#include #include #ifdef Q_OS_LINUX @@ -397,6 +398,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) .arg(dataPath)); return; } + m_dataPath = dataPath; /* * Establish the mechanism for communication with an already running PrismLauncher that uses the same data path. @@ -829,7 +831,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) #ifdef Q_OS_MAC m_updater.reset(new MacSparkleUpdater()); #else - m_updater.reset(new PrismExternalUpdater(m_rootPath, dataPath)); + m_updater.reset(new PrismExternalUpdater(m_rootPath, m_dataPath)); #endif qDebug() << "<> Updater started."; } @@ -1024,9 +1026,120 @@ void Application::setupWizardFinished(int status) performMainStartupAction(); } +std::tuple read_lock_File(const QString& path) +{ + auto contents = QString(FS::read(path)); + auto lines = contents.split('\n'); + + QDateTime timestamp; + QString from, to, target, data_path; + for (auto line : lines) { + auto index = line.indexOf("="); + if (index < 0) + continue; + auto left = line.left(index); + auto right = line.mid(index + 1); + if (left.toLower() == "timestamp") { + timestamp = QDateTime::fromString(right, Qt::ISODate); + } else if (left.toLower() == "from") { + from = right; + } else if (left.toLower() == "to") { + to = right; + } else if (left.toLower() == "target") { + target = right; + } else if (left.toLower() == "data_path") { + data_path = right; + } + } + return std::make_tuple(timestamp, from, to, target, data_path); +} + void Application::performMainStartupAction() { m_status = Application::Initialized; + + auto update_log_path = FS::PathCombine(m_dataPath, "prism_launcher_update.log"); + + auto update_lock = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.lock")); + if (update_lock.exists()) { + auto [timestamp, from, to, target, data_path] = read_lock_File(update_lock.absoluteFilePath()); + auto infoMsg = tr("This installation has a update lock file present at: %1\n" + "\n" + "Timestamp: %2\n" + "Updating from version %3 to %4\n" + "Target install path: %5\n" + "Data Path: %6" + "\n" + "This likely means that a update attempt failed. Please ensure your installation is in working order before " + "proceeding.\n" + "Check the Prism Launcher updater log at: \n" + "%7\n" + "for details on the last update attempt.\n" + "\n" + "To delete this lock and proceed select \"Ignore\" below.") + .arg(update_lock.absoluteFilePath()) + .arg(timestamp.toString(Qt::ISODate), from, to, target, data_path) + .arg(update_log_path); + auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update In Progress"), infoMsg, QMessageBox::Ignore | QMessageBox::Abort); + msgBox.setDefaultButton(QMessageBox::Abort); + msgBox.setModal(true); + switch (msgBox.exec()) { + case QMessageBox::AcceptRole: { + FS::deletePath(update_lock.absoluteFilePath()); + break; + } + case QMessageBox::RejectRole: + [[fallthrough]]; + default: { + qDebug() << "Exiting because update lockfile is present"; + exit(1); + } + } + } + + auto update_fail_marker = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.fail")); + if (update_fail_marker.exists()) { + auto infoMsg = tr("An update attempt failed\n" + "\n" + "Please ensure your installation is in working order before " + "proceeding.\n" + "Check the Prism Launcher updater log at: \n" + "%1\n" + "for details on the last update attempt.") + .arg(update_log_path); + auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update Failed"), infoMsg, QMessageBox::Ignore | QMessageBox::Abort); + msgBox.setDefaultButton(QMessageBox::Abort); + msgBox.setModal(true); + switch (msgBox.exec()) { + case QMessageBox::AcceptRole: { + FS::deletePath(update_fail_marker.absoluteFilePath()); + break; + } + case QMessageBox::RejectRole: + [[fallthrough]]; + default: { + qDebug() << "Exiting because update lockfile is present"; + exit(1); + } + } + } + + auto update_success_marker = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.success")); + if (update_success_marker.exists()) { + auto infoMsg = tr("Update succeeded\n" + "\n" + "You are now running %1 .\n" + "Check the Prism Launcher updater log at: \n" + "%1\n" + "for details.") + .arg(BuildConfig.printableVersionString()) + .arg(update_log_path); + auto msgBox = QMessageBox(QMessageBox::Information, tr("Update Succeeded"), infoMsg, QMessageBox::Ok); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.open(); + FS::deletePath(update_success_marker.absoluteFilePath()); + } + if (!m_instanceIdToLaunch.isEmpty()) { auto inst = instances()->getInstanceById(m_instanceIdToLaunch); if (inst) { diff --git a/launcher/Application.h b/launcher/Application.h index ced0af17d..baf64575d 100644 --- a/launcher/Application.h +++ b/launcher/Application.h @@ -187,6 +187,11 @@ public: return m_rootPath; } + /// the data path the application is using + const QString& dataRoot() { + return m_dataPath; + } + bool isPortable() { return m_portable; } @@ -277,6 +282,7 @@ private: QMap> m_profilers; QString m_rootPath; + QString m_dataPath; Status m_status = Application::StartingUp; Capabilities m_capabilities; bool m_portable = false; diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 86bf8fc48..b68179af8 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -1078,6 +1078,15 @@ SET(LAUNCHER_SOURCES ui/instanceview/VisualGroup.h ) +if (NOT Apple) +set(LAUNCHER_SOURCES + ${LAUNCHER_SOURCES} + + ui/dialogs/UpdateAvailableDialog.h + ui/dialogs/UpdateAvailableDialog.cpp +) +endif() + qt_wrap_ui(LAUNCHER_UI ui/MainWindow.ui ui/setupwizard/PasteWizardPage.ui @@ -1138,6 +1147,14 @@ qt_wrap_ui(LAUNCHER_UI ui/dialogs/ChooseProviderDialog.ui ) +qt_wrap_ui(PRISM_UPDATE_UI + ui/dialogs/UpdateAvailableDialog.ui +) + +if (NOT Apple) + set (LAUNCHER_UI ${LAUNCHER_UI} ${PRISM_UPDATE_UI}) +endif() + qt_add_resources(LAUNCHER_RESOURCES resources/backgrounds/backgrounds.qrc resources/multimc/multimc.qrc diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 2a0ca76b5..7bfb5b1e3 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -199,7 +199,7 @@ void appendSafe(const QString& filename, const QByteArray& data) QByteArray buffer; try { buffer = read(filename); - } catch (FileSystemException) { + } catch (FileSystemException&) { buffer = QByteArray(); } buffer.append(data); diff --git a/launcher/ui/dialogs/UpdateAvailableDialog.cpp b/launcher/ui/dialogs/UpdateAvailableDialog.cpp new file mode 100644 index 000000000..9f7308cba --- /dev/null +++ b/launcher/ui/dialogs/UpdateAvailableDialog.cpp @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> +// +// SPDX-License-Identifier: GPL-3.0-only + +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "UpdateAvailableDialog.h" +#include +#include "Application.h" +#include "BuildConfig.h" +#include "Markdown.h" +#include "ui_UpdateAvailableDialog.h" + +UpdateAvailableDialog::UpdateAvailableDialog(const QString& currentVersion, + const QString& availableVersion, + const QString& releaseNotes, + QWidget* parent) + : QDialog(parent), ui(new Ui::UpdateAvailableDialog) +{ + ui->setupUi(this); + + QString launcherName = BuildConfig.LAUNCHER_DISPLAYNAME; + + ui->headerLabel->setText(tr("A new version of %1 is available!").arg(launcherName)); + ui->versionAvailableLabel->setText( + tr("Version %1 is now available - you have %2 . Would you like to download it now?").arg(availableVersion).arg(currentVersion)); + ui->icon->setPixmap(APPLICATION->getThemedIcon("checkupdate").pixmap(64)); + + auto releaseNotesHtml = markdownToHTML(releaseNotes); + ui->releaseNotes->setHtml(releaseNotesHtml); + ui->releaseNotes->setOpenExternalLinks(true); + + connect(ui->skipButton, &QPushButton::clicked, this, [this](){ + this->setResult(DialogCode::Skip); + this->close(); + }); + + connect(ui->delayButton, &QPushButton::clicked, this, [this](){ + this->setResult(DialogCode::DontInstall); + this->close(); + }); + + connect(ui->installButton, &QPushButton::clicked, this, [this](){ + this->setResult(DialogCode::Install); + this->close(); + }); +} diff --git a/launcher/ui/dialogs/UpdateAvailableDialog.h b/launcher/ui/dialogs/UpdateAvailableDialog.h new file mode 100644 index 000000000..d37839fda --- /dev/null +++ b/launcher/ui/dialogs/UpdateAvailableDialog.h @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> +// +// SPDX-License-Identifier: GPL-3.0-only + +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +#pragma once + +#include + +namespace Ui { +class UpdateAvailableDialog; +} + +class UpdateAvailableDialog : public QDialog { + Q_OBJECT + + public: + + enum DialogCode { + Install, + DontInstall, + Skip, + }; + + explicit UpdateAvailableDialog(const QString& currentVersion, + const QString& availableVersion, + const QString& releaseNotes, + QWidget* parent = 0); + ~UpdateAvailableDialog(); + + private: + Ui::UpdateAvailableDialog* ui; +}; diff --git a/launcher/ui/dialogs/UpdateAvailableDialog.ui b/launcher/ui/dialogs/UpdateAvailableDialog.ui new file mode 100644 index 000000000..b0d85f6f0 --- /dev/null +++ b/launcher/ui/dialogs/UpdateAvailableDialog.ui @@ -0,0 +1,155 @@ + + + UpdateAvailableDialog + + + + 0 + 0 + 636 + 352 + + + + Update Available + + + + + + + + + + + 64 + 64 + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + 9 + + + 9 + + + 9 + + + 9 + + + + + + 11 + 75 + true + + + + A new version is available! + + + + + + + Version %1 is now available - you have %2 . Would you like to download it now? + + + + + + + + 75 + true + + + + Release Notes: + + + + + + + + + + + + + + + + Skip This Version + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Remind Me Later + + + false + + + false + + + + + + + Install Update + + + true + + + + + + + + + + diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index 9ec033f02..f2c88cf6a 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -21,19 +21,22 @@ */ #include "PrismExternalUpdater.h" -#include +#include #include -#include #include #include -#include +#include #include -#include +#include +#include +#include #include "StringUtils.h" #include "BuildConfig.h" +#include "ui/dialogs/UpdateAvailableDialog.h" + class PrismExternalUpdater::Private { public: QDir appDir; @@ -78,12 +81,11 @@ PrismExternalUpdater::~PrismExternalUpdater() void PrismExternalUpdater::checkForUpdates() { - QProgressDialog progress(tr("Checking for updates..."), "", 0, -1); + QProgressDialog progress(tr("Checking for updates..."), "", 0, 0); progress.setCancelButton(nullptr); progress.show(); QCoreApplication::processEvents(); - QProcess proc; auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); #if defined Q_OS_WIN32 @@ -98,14 +100,16 @@ void PrismExternalUpdater::checkForUpdates() auto result_start = proc.waitForStarted(5000); if (!result_start) { auto err = proc.error(); - qDebug() << "Failed to start updater after 5 seconds." << "reason:" << err << proc.errorString(); + qDebug() << "Failed to start updater after 5 seconds." + << "reason:" << err << proc.errorString(); } QCoreApplication::processEvents(); auto result_finished = proc.waitForFinished(60000); if (!result_finished) { auto err = proc.error(); - qDebug() << "Updater failed to close after 60 seconds." << "reason:" << err << proc.errorString(); + qDebug() << "Updater failed to close after 60 seconds." + << "reason:" << err << proc.errorString(); } auto exit_code = proc.exitCode(); @@ -116,6 +120,9 @@ void PrismExternalUpdater::checkForUpdates() qDebug() << "captured output:" << std_output; qDebug() << "captured error:" << std_error; + progress.hide(); + QCoreApplication::processEvents(); + switch (exit_code) { case 0: // no update available @@ -134,12 +141,14 @@ void PrismExternalUpdater::checkForUpdates() { auto [first_line, remainder1] = StringUtils::splitFirst(std_output, '\n'); auto [second_line, remainder2] = StringUtils::splitFirst(remainder1, '\n'); - auto [third_line, changelog] = StringUtils::splitFirst(remainder2, '\n'); + auto [third_line, release_notes] = StringUtils::splitFirst(remainder2, '\n'); auto version_name = StringUtils::splitFirst(first_line, ": ").second; auto version_tag = StringUtils::splitFirst(second_line, ": ").second; auto release_timestamp = QDateTime::fromString(StringUtils::splitFirst(third_line, ": ").second, Qt::ISODate); qDebug() << "Update available:" << version_name << version_tag << release_timestamp; - qDebug() << "Update changelog:" << changelog; + qDebug() << "Update release notes:" << release_notes; + + offerUpdate(version_name, version_tag, release_notes); } break; default: @@ -153,48 +162,55 @@ void PrismExternalUpdater::checkForUpdates() priv->settings->sync(); } -bool PrismExternalUpdater::getAutomaticallyChecksForUpdates() { +bool PrismExternalUpdater::getAutomaticallyChecksForUpdates() +{ return priv->autoCheck; } -double PrismExternalUpdater::getUpdateCheckInterval() { +double PrismExternalUpdater::getUpdateCheckInterval() +{ return priv->updateInterval; } -bool PrismExternalUpdater::getBetaAllowed() { +bool PrismExternalUpdater::getBetaAllowed() +{ return priv->allowBeta; } -void PrismExternalUpdater::setAutomaticallyChecksForUpdates(bool check) { +void PrismExternalUpdater::setAutomaticallyChecksForUpdates(bool check) +{ priv->autoCheck = check; priv->settings->setValue("auto_check", check); priv->settings->sync(); resetAutoCheckTimer(); } -void PrismExternalUpdater::setUpdateCheckInterval(double seconds) { +void PrismExternalUpdater::setUpdateCheckInterval(double seconds) +{ priv->updateInterval = seconds; priv->settings->setValue("update_interval", seconds); priv->settings->sync(); resetAutoCheckTimer(); } -void PrismExternalUpdater::setBetaAllowed(bool allowed) { +void PrismExternalUpdater::setBetaAllowed(bool allowed) +{ priv->allowBeta = allowed; priv->settings->setValue("auto_beta", allowed); priv->settings->sync(); } -void PrismExternalUpdater::resetAutoCheckTimer() { +void PrismExternalUpdater::resetAutoCheckTimer() +{ int timeoutDuration = 0; auto now = QDateTime::currentDateTime(); if (priv->autoCheck) { - if (priv->lastCheck.isValid()) { + if (priv->lastCheck.isValid()) { auto diff = priv->lastCheck.secsTo(now); auto secs_left = priv->updateInterval - diff; if (secs_left < 0) secs_left = 0; - timeoutDuration = secs_left * 1000; // to msec + timeoutDuration = secs_left * 1000; // to msec } qDebug() << "Auto update timer starting," << timeoutDuration / 1000 << "seconds left"; priv->updateTimer.start(timeoutDuration); @@ -202,18 +218,66 @@ void PrismExternalUpdater::resetAutoCheckTimer() { if (priv->updateTimer.isActive()) priv->updateTimer.stop(); } - } -void PrismExternalUpdater::connectTimer() { +void PrismExternalUpdater::connectTimer() +{ connect(&priv->updateTimer, &QTimer::timeout, this, &PrismExternalUpdater::autoCheckTimerFired); - } -void PrismExternalUpdater::disconnectTimer() { +void PrismExternalUpdater::disconnectTimer() +{ disconnect(&priv->updateTimer, &QTimer::timeout, this, &PrismExternalUpdater::autoCheckTimerFired); } -void PrismExternalUpdater::autoCheckTimerFired() { +void PrismExternalUpdater::autoCheckTimerFired() +{ checkForUpdates(); } + +void PrismExternalUpdater::offerUpdate(const QString& version_name, const QString& version_tag, const QString& release_notes) +{ + priv->settings->beginGroup("skip"); + auto should_skip = priv->settings->value(version_tag, false).toBool(); + priv->settings->endGroup(); + + if (should_skip) + return; + + UpdateAvailableDialog dlg(BuildConfig.printableVersionString(), version_name, release_notes); + + auto result = dlg.exec(); + switch (result) { + case UpdateAvailableDialog::Install: { + performUpdate(version_tag); + } + case UpdateAvailableDialog::Skip: { + priv->settings->beginGroup("skip"); + priv->settings->setValue(version_tag, true); + priv->settings->endGroup(); + priv->settings->sync(); + return; + } + case UpdateAvailableDialog::DontInstall: { + return; + } + } +} + +void PrismExternalUpdater::performUpdate(const QString& version_tag) { + QProcess proc; + auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); +#if defined Q_OS_WIN32 + exe_name.append(".exe"); +#endif + + QStringList args = { "--dir", priv->dataDir.absolutePath(), "--install-version", version_tag }; + if (priv->allowBeta) + args.append("--pre-release"); + + auto result = proc.startDetached(priv->appDir.absoluteFilePath(exe_name), args); + if (!result) { + qDebug() << "Failed to start updater:" << proc.error() << proc.errorString(); + } + QCoreApplication::exit(); +} diff --git a/launcher/updater/PrismExternalUpdater.h b/launcher/updater/PrismExternalUpdater.h index 852fb7d37..f8ed29ccc 100644 --- a/launcher/updater/PrismExternalUpdater.h +++ b/launcher/updater/PrismExternalUpdater.h @@ -34,7 +34,7 @@ class PrismExternalUpdater : public ExternalUpdater { Q_OBJECT public: - PrismExternalUpdater(const QString& appDir, const QString& dataDir); + PrismExternalUpdater(const QString& appDir, const QString& dataDir); ~PrismExternalUpdater() override; /*! @@ -82,7 +82,8 @@ class PrismExternalUpdater : public ExternalUpdater { void disconnectTimer(); void connectTimer(); - void performUpdate(); + void offerUpdate(const QString& version_name, const QString& version_tag, const QString& release_notes); + void performUpdate(const QString& version_tag); public slots: void autoCheckTimerFired(); diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index e01295605..658b1a57b 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -630,7 +630,7 @@ void PrismUpdaterApp::moveAndFinishUpdate(QDir target) for (auto file : files) { file_list.append(file.trimmed()); } - } catch (FS::FileSystemException) { + } catch (FS::FileSystemException&) { } } @@ -675,15 +675,23 @@ void PrismUpdaterApp::moveAndFinishUpdate(QDir target) if (error) { logUpdate(tr("There were errors installing the update.")); auto fail_marker = FS::PathCombine(m_dataPath, ".prism_launcher_update.fail"); - FS::move(m_updateLogPath, fail_marker); + FS::copy(m_updateLogPath, fail_marker)(); } else { logUpdate(tr("Update succeed.")); auto success_marker = FS::PathCombine(m_dataPath, ".prism_launcher_update.success"); - FS::move(m_updateLogPath, success_marker); + FS::copy(m_updateLogPath, success_marker)(); } auto update_lock_path = FS::PathCombine(m_dataPath, ".prism_launcher_update.lock"); FS::deletePath(update_lock_path); + QProcess proc; + auto app_exe_name = BuildConfig.LAUNCHER_APP_BINARY_NAME; +#if defined Q_OS_WIN32 + app_exe_name.append(".exe"); +#endif + auto app_exe_path = target.absoluteFilePath(app_exe_name); + proc.startDetached(app_exe_path); + exit(error ? 1 : 0); } @@ -897,7 +905,7 @@ bool write_lock_file(const QString& path, QDateTime timestamp, QString from, QSt .arg(target) .arg(data_path) .toUtf8()); - } catch (FS::FileSystemException err) { + } catch (FS::FileSystemException& err) { qWarning() << "Error writing lockfile:" << err.what() << "\n" << err.cause(); return false; } @@ -922,7 +930,7 @@ void PrismUpdaterApp::performInstall(QFileInfo file) "\n" "This likely means that a previous update attempt failed. Please ensure your installation is in working order before " "proceeding.\n" - "Check the Prism Launcher updater log at \n" + "Check the Prism Launcher updater log at: \n" "%7\n" "for details on the last update attempt.\n" "\n" @@ -936,9 +944,9 @@ void PrismUpdaterApp::performInstall(QFileInfo file) msgBox.setStandardButtons(QMessageBox::Ignore | QMessageBox::Cancel); msgBox.setDefaultButton(QMessageBox::Cancel); switch (msgBox.exec()) { - case QMessageBox::Ignore: + case QMessageBox::AcceptRole: break; - case QMessageBox::Cancel: + case QMessageBox::RejectRole: [[fallthrough]]; default: return showFatalErrorMessage(tr("Update Aborted"), tr("The update attempt was aborted")); @@ -1005,7 +1013,7 @@ void PrismUpdaterApp::backupAppDir() for (auto file : files) { file_list.append(file.trimmed()); } - } catch (FS::FileSystemException) { + } catch (FS::FileSystemException&) { } } From c0f046255084ce88323375af665c2d6bf27e2385 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Mon, 26 Jun 2023 01:41:51 -0700 Subject: [PATCH 064/268] fix(updater): logs/ folder Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/Application.cpp | 10 ++++++++-- launcher/ui/dialogs/UpdateAvailableDialog.h | 2 +- launcher/updater/prismupdater/PrismUpdater.cpp | 5 +++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index a04f85c28..e172472a1 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -831,7 +831,13 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) #ifdef Q_OS_MAC m_updater.reset(new MacSparkleUpdater()); #else - m_updater.reset(new PrismExternalUpdater(m_rootPath, m_dataPath)); + auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); +#if defined Q_OS_WIN32 + exe_name.append(".exe"); +#endif + auto updater_binary = QFileInfo(QDir(m_rootPath).absoluteFilePath(exe_name)); + if (updater_binary.isFile()) + m_updater.reset(new PrismExternalUpdater(m_rootPath, m_dataPath)); #endif qDebug() << "<> Updater started."; } @@ -1058,7 +1064,7 @@ void Application::performMainStartupAction() { m_status = Application::Initialized; - auto update_log_path = FS::PathCombine(m_dataPath, "prism_launcher_update.log"); + auto update_log_path = FS::PathCombine(m_dataPath, "logs", "prism_launcher_update.log"); auto update_lock = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.lock")); if (update_lock.exists()) { diff --git a/launcher/ui/dialogs/UpdateAvailableDialog.h b/launcher/ui/dialogs/UpdateAvailableDialog.h index d37839fda..7a14c01da 100644 --- a/launcher/ui/dialogs/UpdateAvailableDialog.h +++ b/launcher/ui/dialogs/UpdateAvailableDialog.h @@ -42,7 +42,7 @@ class UpdateAvailableDialog : public QDialog { const QString& availableVersion, const QString& releaseNotes, QWidget* parent = 0); - ~UpdateAvailableDialog(); + ~UpdateAvailableDialog() = default; private: Ui::UpdateAvailableDialog* ui; diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 658b1a57b..439457bae 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -343,10 +343,11 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar #endif } - m_updateLogPath = FS::PathCombine(m_dataPath, "prism_launcher_update.log"); + m_updateLogPath = FS::PathCombine(m_dataPath, "logs", "prism_launcher_update.log"); { // setup logging - static const QString logBase = BuildConfig.LAUNCHER_NAME + "Updater" + (m_checkOnly ? "-CheckOnly" : "") + "-%0.log"; + static const QString baseLogFile = BuildConfig.LAUNCHER_NAME + "Updater" + (m_checkOnly ? "-CheckOnly" : "") + "-%0.log"; + static const QString logBase = FS::PathCombine("logs", baseLogFile); auto moveFile = [](const QString& oldName, const QString& newName) { QFile::remove(newName); QFile::copy(oldName, newName); From 6f7454243ef8d6c9b317ee7e83cf0c361f863c14 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Mon, 26 Jun 2023 23:24:46 -0700 Subject: [PATCH 065/268] fix: prep for changes in #1276 - sign updater Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/build.yml | 9 +- CMakeLists.txt | 10 +- buildconfig/BuildConfig.cpp.in | 22 ++ buildconfig/BuildConfig.h | 36 ++- launcher/Application.cpp | 229 +++++++++--------- .../updater/prismupdater/PrismUpdater.cpp | 16 +- 6 files changed, 197 insertions(+), 125 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 777e7a79b..5a1554fc6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -283,12 +283,12 @@ 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=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -DLauncher_UPDATER_GITHUB_REPO=https://github.com/${{ github.repository }} -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 -DLauncher_BUILD_ARTIFACT=${{ matrix.name }} -G Ninja - 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}} -DLauncher_FORCE_BUNDLED_LIBS=ON -DLauncher_UPDATER_GITHUB_REPO=https://github.com/${{ github.repository }} + 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 -DLauncher_BUILD_ARTIFACT=${{ matrix.name }} # https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix) if ("${{ env.CCACHE_VAR }}") { @@ -303,7 +303,7 @@ jobs: - name: Configure CMake (Linux) if: runner.os == 'Linux' run: | - cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=Linux -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DLauncher_UPDATER_GITHUB_REPO=https://github.com/${{ github.repository }} -G Ninja + cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=Linux -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DLauncher_BUILD_ARTIFACT=${{ matrix.name }} -G Ninja ## # BUILD @@ -391,7 +391,6 @@ jobs: if: runner.os == 'Windows' && matrix.msystem == '' run: | cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build_type }} - Get-Content ${{ env.BUILD_DIR }}/install_manifest.txt | %{ $_.TrimStart("$pwd/") } | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('./') } | Out-File -Append -FilePath ${{ env.INSTALL_DIR }}/manifest.txt cd ${{ env.INSTALL_DIR }} if ("${{ matrix.qt_ver }}" -eq "5") @@ -416,7 +415,7 @@ jobs: if (Get-Content ./codesign.pfx){ cd ${{ env.INSTALL_DIR }} # We ship the exact same executable for portable and non-portable editions, so signing just once is fine - SignTool sign /fd sha256 /td sha256 /f ../codesign.pfx /p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' /tr http://timestamp.digicert.com prismlauncher.exe prismlauncher_filelink.exe + SignTool sign /fd sha256 /td sha256 /f ../codesign.pfx /p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' /tr http://timestamp.digicert.com prismlauncher.exe prismlauncher_updater.exe prismlauncher_filelink.exe } else { ":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY } diff --git a/CMakeLists.txt b/CMakeLists.txt index cb75f769c..bbe8bfbbc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -149,7 +149,10 @@ set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_M set(Launcher_BUILD_PLATFORM "" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.") # Github repo URL with releases for updater -set(Launcher_UPDATER_GITHUB_REPO "" CACHE STRING "Base URL for the updater.") +set(Launcher_UPDATER_GITHUB_REPO "https://github.com/PrismLauncher/PrismLauncher" CACHE STRING "Base github URL for the updater.") + +# Name to help updater identify valid artifacts +set(Launcher_BUILD_ARTIFACT "" CACHE STRING "Artifact name to help the updater identify valid artifacts.") # The metadata server set(Launcher_META_URL "https://meta.prismlauncher.org/v1/" CACHE STRING "URL to fetch Launcher's meta files from.") @@ -193,6 +196,11 @@ set(Launcher_MSA_CLIENT_ID "c36a9fb6-4f2a-41ff-90bd-ae7cc92031eb" CACHE STRING " # This key was issued specifically for Prism Launcher set(Launcher_CURSEFORGE_API_KEY "$2a$10$wuAJuNZuted3NORVmpgUC.m8sI.pv1tOPKZyBgLFGjxFp/br0lZCC" CACHE STRING "API key for the CurseForge platform") +set(Launcher_COMPILER_NAME ${CMAKE_CXX_COMPILER_ID}) +set(Launcher_COMPILER_VERSION ${CMAKE_CXX_COMPILER_VERSION}) +set(Launcher_COMPILER_TARGET_SYSTEM ${CMAKE_SYSTEM_NAME}) +set(Launcher_COMPILER_TARGET_SYSTEM_VERSION ${CMAKE_SYSTEM_VERSION}) +set(Launcher_COMPILER_TARGET_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR}) #### Check the current Git commit and branch include(GetGitRevisionDescription) diff --git a/buildconfig/BuildConfig.cpp.in b/buildconfig/BuildConfig.cpp.in index b03867c3c..e8e8a4ef8 100644 --- a/buildconfig/BuildConfig.cpp.in +++ b/buildconfig/BuildConfig.cpp.in @@ -33,6 +33,7 @@ * limitations under the License. */ +#include #include "BuildConfig.h" #include @@ -59,9 +60,17 @@ Config::Config() VERSION_MINOR = @Launcher_VERSION_MINOR@; BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@"; + BUILD_ARTIFACT = "@Launcher_BUILD_ARTIFACT@"; BUILD_DATE = "@Launcher_BUILD_TIMESTAMP@"; UPDATER_GITHUB_REPO = "@Launcher_UPDATER_GITHUB_REPO@"; + COMPILER_NAME = "@Launcher_COMPILER_NAME@"; + COMPILER_VERSION = "@Launcher_COMPILER_VERSION@"; + + COMPILER_TARGET_SYSTEM = "@Launcher_COMPILER_TARGET_SYSTEM@"; + COMPILER_TARGET_SYSTEM_VERSION = "@Launcher_COMPILER_TARGET_SYSTEM_VERSION@"; + COMPILER_TARGET_SYSTEM_PROCESSOR = "@Launcher_COMPILER_TARGET_PROCESSOR@"; + MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@"; MAC_SPARKLE_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@"; @@ -133,3 +142,16 @@ QString Config::printableVersionString() const } return vstr; } + +QString Config::compilerID() const +{ + if (COMPILER_VERSION.isEmpty()) + return COMPILER_NAME; + return QStringLiteral("%1 - %2").arg(COMPILER_NAME).arg(COMPILER_VERSION); +} + +QString Config::systemID() const +{ + return QStringLiteral("%1 %2 %3").arg(COMPILER_TARGET_SYSTEM, COMPILER_TARGET_SYSTEM_VERSION, COMPILER_TARGET_SYSTEM_PROCESSOR); +} + diff --git a/buildconfig/BuildConfig.h b/buildconfig/BuildConfig.h index b6ae6e6d8..723b2e714 100644 --- a/buildconfig/BuildConfig.h +++ b/buildconfig/BuildConfig.h @@ -36,8 +36,8 @@ */ #pragma once -#include #include +#include /** * \brief The Config class holds all the build-time information passed from the build system. @@ -71,9 +71,27 @@ class Config { /// A short string identifying this build's platform. For example, "lin64" or "win32". QString BUILD_PLATFORM; + /// A short string identifying this build's valid artifacts int he updater. For example, "lin64" or "win32". + QString BUILD_ARTIFACT; + /// A string containing the build timestamp QString BUILD_DATE; + /// A string identifying the compiler use to build + QString COMPILER_NAME; + + /// A string identifying the compiler version used to build + QString COMPILER_VERSION; + + /// A string identifying the compiler target system os + QString COMPILER_TARGET_SYSTEM; + + /// A String identifying the compiler target system version + QString COMPILER_TARGET_SYSTEM_VERSION; + + /// A String identifying the compiler target processor + QString COMPILER_TARGET_SYSTEM_PROCESSOR; + /// URL for the updater's channel QString UPDATER_GITHUB_REPO; @@ -145,7 +163,7 @@ class Config { QString AUTH_BASE = "https://authserver.mojang.com/"; QString IMGUR_BASE_URL = "https://api.imgur.com/3/"; QString FMLLIBS_BASE_URL = "https://files.prismlauncher.org/fmllibs/"; // FIXME: move into CMakeLists - QString TRANSLATIONS_BASE_URL = "https://i18n.prismlauncher.org/"; // FIXME: move into CMakeLists + QString TRANSLATIONS_BASE_URL = "https://i18n.prismlauncher.org/"; // FIXME: move into CMakeLists QString MODPACKSCH_API_BASE_URL = "https://api.modpacks.ch/"; @@ -162,7 +180,7 @@ class Config { QString MODRINTH_STAGING_URL = "https://staging-api.modrinth.com/v2"; QString MODRINTH_PROD_URL = "https://api.modrinth.com/v2"; - QStringList MODRINTH_MRPACK_HOSTS{"cdn.modrinth.com", "github.com", "raw.githubusercontent.com", "gitlab.com"}; + QStringList MODRINTH_MRPACK_HOSTS{ "cdn.modrinth.com", "github.com", "raw.githubusercontent.com", "gitlab.com" }; QString FLAME_BASE_URL = "https://api.curseforge.com/v1"; @@ -172,6 +190,18 @@ class Config { * \return The version number in string format (major.minor.revision.build). */ QString printableVersionString() const; + + /** + * \brief Compiler ID String + * \return a string of the form "Name - Version" of just "Name" if the version is empty + */ + QString compilerID() const; + + /** + * \brief System ID String + * \return a string of the form "OS Verison Processor" + */ + QString systemID() const; }; extern const Config BuildConfig; diff --git a/launcher/Application.cpp b/launcher/Application.cpp index e172472a1..a8b26ed74 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -175,6 +175,34 @@ void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QSt } // namespace +std::tuple read_lock_File(const QString& path) +{ + auto contents = QString(FS::read(path)); + auto lines = contents.split('\n'); + + QDateTime timestamp; + QString from, to, target, data_path; + for (auto line : lines) { + auto index = line.indexOf("="); + if (index < 0) + continue; + auto left = line.left(index); + auto right = line.mid(index + 1); + if (left.toLower() == "timestamp") { + timestamp = QDateTime::fromString(right, Qt::ISODate); + } else if (left.toLower() == "from") { + from = right; + } else if (left.toLower() == "to") { + to = right; + } else if (left.toLower() == "target") { + target = right; + } else if (left.toLower() == "data_path") { + data_path = right; + } + } + return std::make_tuple(timestamp, from, to, target, data_path); +} + #if defined Q_OS_WIN32 // taken from https://stackoverflow.com/a/25927081 @@ -554,6 +582,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) qDebug() << "Version : " << BuildConfig.printableVersionString(); qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT; qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC; + qDebug() << "Compiled for : " << BuildConfig.systemID(); + qDebug() << "Compiled by : " << BuildConfig.compilerID(); if (adjustedBy.size()) { qDebug() << "Work dir before adjustment : " << origcwdPath; qDebug() << "Work dir after adjustment : " << QDir::currentPath(); @@ -942,6 +972,95 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) applyCurrentlySelectedTheme(true); + // check update locks + { + auto update_log_path = FS::PathCombine(m_dataPath, "logs", "prism_launcher_update.log"); + + auto update_lock = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.lock")); + if (update_lock.exists()) { + auto [timestamp, from, to, target, data_path] = read_lock_File(update_lock.absoluteFilePath()); + auto infoMsg = tr("This installation has a update lock file present at: %1\n" + "\n" + "Timestamp: %2\n" + "Updating from version %3 to %4\n" + "Target install path: %5\n" + "Data Path: %6" + "\n" + "This likely means that a update attempt failed. Please ensure your installation is in working order before " + "proceeding.\n" + "Check the Prism Launcher updater log at: \n" + "%7\n" + "for details on the last update attempt.\n" + "\n" + "To delete this lock and proceed select \"Ignore\" below.") + .arg(update_lock.absoluteFilePath()) + .arg(timestamp.toString(Qt::ISODate), from, to, target, data_path) + .arg(update_log_path); + auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update In Progress"), infoMsg, QMessageBox::Ignore | QMessageBox::Abort); + msgBox.setDefaultButton(QMessageBox::Abort); + msgBox.setModal(true); + auto res = msgBox.exec(); + switch (res) { + case QMessageBox::Ignore: { + FS::deletePath(update_lock.absoluteFilePath()); + break; + } + case QMessageBox::Abort: + [[fallthrough]]; + default: { + qDebug() << "Exiting because update lockfile is present"; + QMetaObject::invokeMethod(this, [](){ exit(1); }, Qt::QueuedConnection); + return; + } + } + } + + auto update_fail_marker = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.fail")); + if (update_fail_marker.exists()) { + auto infoMsg = tr("An update attempt failed\n" + "\n" + "Please ensure your installation is in working order before " + "proceeding.\n" + "Check the Prism Launcher updater log at: \n" + "%1\n" + "for details on the last update attempt.") + .arg(update_log_path); + auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update Failed"), infoMsg, QMessageBox::Ignore | QMessageBox::Abort); + msgBox.setDefaultButton(QMessageBox::Abort); + msgBox.setModal(true); + auto res = msgBox.exec(); + switch (res) { + case QMessageBox::Ignore: { + FS::deletePath(update_fail_marker.absoluteFilePath()); + break; + } + case QMessageBox::Abort: + [[fallthrough]]; + default: { + qDebug() << "Exiting because update lockfile is present"; + QMetaObject::invokeMethod(this, [](){ exit(1); }, Qt::QueuedConnection); + return; + } + } + } + + auto update_success_marker = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.success")); + if (update_success_marker.exists()) { + auto infoMsg = tr("Update succeeded\n" + "\n" + "You are now running %1 .\n" + "Check the Prism Launcher updater log at: \n" + "%1\n" + "for details.") + .arg(BuildConfig.printableVersionString()) + .arg(update_log_path); + auto msgBox = QMessageBox(QMessageBox::Information, tr("Update Succeeded"), infoMsg, QMessageBox::Ok); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.open(); + FS::deletePath(update_success_marker.absoluteFilePath()); + } + } + updateCapabilities(); if (createSetupWizard()) { @@ -1032,119 +1151,11 @@ void Application::setupWizardFinished(int status) performMainStartupAction(); } -std::tuple read_lock_File(const QString& path) -{ - auto contents = QString(FS::read(path)); - auto lines = contents.split('\n'); - - QDateTime timestamp; - QString from, to, target, data_path; - for (auto line : lines) { - auto index = line.indexOf("="); - if (index < 0) - continue; - auto left = line.left(index); - auto right = line.mid(index + 1); - if (left.toLower() == "timestamp") { - timestamp = QDateTime::fromString(right, Qt::ISODate); - } else if (left.toLower() == "from") { - from = right; - } else if (left.toLower() == "to") { - to = right; - } else if (left.toLower() == "target") { - target = right; - } else if (left.toLower() == "data_path") { - data_path = right; - } - } - return std::make_tuple(timestamp, from, to, target, data_path); -} - void Application::performMainStartupAction() { m_status = Application::Initialized; - auto update_log_path = FS::PathCombine(m_dataPath, "logs", "prism_launcher_update.log"); - - auto update_lock = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.lock")); - if (update_lock.exists()) { - auto [timestamp, from, to, target, data_path] = read_lock_File(update_lock.absoluteFilePath()); - auto infoMsg = tr("This installation has a update lock file present at: %1\n" - "\n" - "Timestamp: %2\n" - "Updating from version %3 to %4\n" - "Target install path: %5\n" - "Data Path: %6" - "\n" - "This likely means that a update attempt failed. Please ensure your installation is in working order before " - "proceeding.\n" - "Check the Prism Launcher updater log at: \n" - "%7\n" - "for details on the last update attempt.\n" - "\n" - "To delete this lock and proceed select \"Ignore\" below.") - .arg(update_lock.absoluteFilePath()) - .arg(timestamp.toString(Qt::ISODate), from, to, target, data_path) - .arg(update_log_path); - auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update In Progress"), infoMsg, QMessageBox::Ignore | QMessageBox::Abort); - msgBox.setDefaultButton(QMessageBox::Abort); - msgBox.setModal(true); - switch (msgBox.exec()) { - case QMessageBox::AcceptRole: { - FS::deletePath(update_lock.absoluteFilePath()); - break; - } - case QMessageBox::RejectRole: - [[fallthrough]]; - default: { - qDebug() << "Exiting because update lockfile is present"; - exit(1); - } - } - } - - auto update_fail_marker = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.fail")); - if (update_fail_marker.exists()) { - auto infoMsg = tr("An update attempt failed\n" - "\n" - "Please ensure your installation is in working order before " - "proceeding.\n" - "Check the Prism Launcher updater log at: \n" - "%1\n" - "for details on the last update attempt.") - .arg(update_log_path); - auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update Failed"), infoMsg, QMessageBox::Ignore | QMessageBox::Abort); - msgBox.setDefaultButton(QMessageBox::Abort); - msgBox.setModal(true); - switch (msgBox.exec()) { - case QMessageBox::AcceptRole: { - FS::deletePath(update_fail_marker.absoluteFilePath()); - break; - } - case QMessageBox::RejectRole: - [[fallthrough]]; - default: { - qDebug() << "Exiting because update lockfile is present"; - exit(1); - } - } - } - - auto update_success_marker = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.success")); - if (update_success_marker.exists()) { - auto infoMsg = tr("Update succeeded\n" - "\n" - "You are now running %1 .\n" - "Check the Prism Launcher updater log at: \n" - "%1\n" - "for details.") - .arg(BuildConfig.printableVersionString()) - .arg(update_log_path); - auto msgBox = QMessageBox(QMessageBox::Information, tr("Update Succeeded"), infoMsg, QMessageBox::Ok); - msgBox.setDefaultButton(QMessageBox::Ok); - msgBox.open(); - FS::deletePath(update_success_marker.absoluteFilePath()); - } + if (!m_instanceIdToLaunch.isEmpty()) { auto inst = instances()->getInstanceById(m_instanceIdToLaunch); diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 439457bae..3fd832b24 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -244,7 +244,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar auto updater_executable = QCoreApplication::applicationFilePath(); - if (BuildConfig.BUILD_PLATFORM.toLower() == "macos") + if (BuildConfig.BUILD_ARTIFACT.toLower() == "macos") showFatalErrorMessage(tr("MacOS Not Supported"), tr("The updater does not support installations on MacOS")); if (updater_executable.startsWith("/tmp/.mount_")) { @@ -435,6 +435,8 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar qDebug() << "Version : " << BuildConfig.printableVersionString(); qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT; qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC; + qDebug() << "Compiled for : " << BuildConfig.systemID(); + qDebug() << "Compiled by : " << BuildConfig.compilerID(); if (adjustedBy.size()) { qDebug() << "Data dir before adjustment : " << origCwdPath; qDebug() << "Data dir after adjustment : " << m_dataPath; @@ -576,7 +578,7 @@ void PrismUpdaterApp::run() return exit(result ? 0 : 1); } - if (BuildConfig.BUILD_PLATFORM.toLower() == "linux" && !m_isPortable) { + if (BuildConfig.BUILD_ARTIFACT.toLower() == "linux" && !m_isPortable) { showFatalErrorMessage(tr("Updating Not Supported"), tr("Updating non-portable linux installations is not supported. Please use your system package manager")); return; @@ -751,9 +753,9 @@ QList PrismUpdaterApp::validReleaseArtifacts(const GitHubRel { QList valid; - qDebug() << "Selecting best asset from" << release.tag_name << "for platform" << BuildConfig.BUILD_PLATFORM + qDebug() << "Selecting best asset from" << release.tag_name << "for platform" << BuildConfig.BUILD_ARTIFACT << "portable:" << m_isPortable; - if (BuildConfig.BUILD_PLATFORM.isEmpty()) + if (BuildConfig.BUILD_ARTIFACT.isEmpty()) qWarning() << "Build platform is not set!"; for (auto asset : release.assets) { if (!m_isAppimage && asset.name.toLower().endsWith("appimage")) @@ -761,7 +763,7 @@ QList PrismUpdaterApp::validReleaseArtifacts(const GitHubRel else if (m_isAppimage && !asset.name.toLower().endsWith("appimage")) continue; auto asset_name = asset.name.toLower(); - auto platform = BuildConfig.BUILD_PLATFORM.toLower(); + auto platform = BuildConfig.BUILD_ARTIFACT.toLower(); auto system_is_arm = QSysInfo::buildCpuArchitecture().contains("arm64"); auto asset_is_arm = asset_name.contains("arm64"); auto asset_is_archive = asset_name.endsWith(".zip") || asset_name.endsWith(".tar.gz"); @@ -808,7 +810,7 @@ void PrismUpdaterApp::performUpdate(const GitHubRelease& release) tr("No Valid Release Assets"), tr("Github release %1 has no valid assets for this platform: %2") .arg(release.tag_name) - .arg(tr("%1 portable: %2").arg(BuildConfig.BUILD_PLATFORM).arg(m_isPortable ? tr("yes") : tr("no")))); + .arg(tr("%1 portable: %2").arg(BuildConfig.BUILD_ARTIFACT).arg(m_isPortable ? tr("yes") : tr("no")))); } else if (valid_assets.length() > 1) { selected_asset = selectAsset(valid_assets); } else { @@ -1020,7 +1022,7 @@ void PrismUpdaterApp::backupAppDir() if (file_list.isEmpty()) { // best guess - if (BuildConfig.BUILD_PLATFORM.toLower() == "linux") { + if (BuildConfig.BUILD_ARTIFACT.toLower() == "linux") { file_list.append({ "PrismLauncher", "bin", "share", "lib" }); } else { // windows by process of elimination file_list.append({ From cb35fb8d0ed9836c49d0f50f78fdd3f449244035 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Tue, 27 Jun 2023 19:56:02 -0700 Subject: [PATCH 066/268] add optional pre-release tag Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- CMakeLists.txt | 3 ++- buildconfig/BuildConfig.cpp.in | 3 ++- buildconfig/BuildConfig.h | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bbe8bfbbc..c67febdb2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -140,8 +140,9 @@ set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRIN ######## Set version numbers ######## set(Launcher_VERSION_MAJOR 8) set(Launcher_VERSION_MINOR 0) +set(Launcher_VERSION_PRERELEASE "") -set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}") +set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}${Launcher_VERSION_PRERELEASE}") set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.0.0") set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_MINOR},0,0") diff --git a/buildconfig/BuildConfig.cpp.in b/buildconfig/BuildConfig.cpp.in index e8e8a4ef8..3e0547046 100644 --- a/buildconfig/BuildConfig.cpp.in +++ b/buildconfig/BuildConfig.cpp.in @@ -58,6 +58,7 @@ Config::Config() // Version information VERSION_MAJOR = @Launcher_VERSION_MAJOR@; VERSION_MINOR = @Launcher_VERSION_MINOR@; + VERSION_PRERELEASE = "@Launcher_VERSION_PRERELEASE@"; BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@"; BUILD_ARTIFACT = "@Launcher_BUILD_ARTIFACT@"; @@ -128,7 +129,7 @@ Config::Config() QString Config::versionString() const { - return QString("%1.%2").arg(VERSION_MAJOR).arg(VERSION_MINOR); + return QString("%1.%2%3").arg(VERSION_MAJOR).arg(VERSION_MINOR).arg(VERSION_PRERELEASE); } QString Config::printableVersionString() const diff --git a/buildconfig/BuildConfig.h b/buildconfig/BuildConfig.h index 723b2e714..f8835f1b3 100644 --- a/buildconfig/BuildConfig.h +++ b/buildconfig/BuildConfig.h @@ -60,6 +60,8 @@ class Config { /// The minor version number. int VERSION_MINOR; + QString VERSION_PRERELEASE; + /** * The version channel * This is used by the updater to determine what channel the current version came from. From 4123343130a1b5e6f443a465b719775f2520c045 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 28 Jun 2023 00:25:09 -0700 Subject: [PATCH 067/268] no need for pre release tag Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- CMakeLists.txt | 3 +- buildconfig/BuildConfig.cpp.in | 3 +- buildconfig/BuildConfig.h | 2 - launcher/filelink/FileLink.cpp | 121 +++++++++++++++--- .../updater/prismupdater/PrismUpdater.cpp | 21 ++- 5 files changed, 117 insertions(+), 33 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c67febdb2..bbe8bfbbc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -140,9 +140,8 @@ set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRIN ######## Set version numbers ######## set(Launcher_VERSION_MAJOR 8) set(Launcher_VERSION_MINOR 0) -set(Launcher_VERSION_PRERELEASE "") -set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}${Launcher_VERSION_PRERELEASE}") +set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}") set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.0.0") set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_MINOR},0,0") diff --git a/buildconfig/BuildConfig.cpp.in b/buildconfig/BuildConfig.cpp.in index 3e0547046..e8e8a4ef8 100644 --- a/buildconfig/BuildConfig.cpp.in +++ b/buildconfig/BuildConfig.cpp.in @@ -58,7 +58,6 @@ Config::Config() // Version information VERSION_MAJOR = @Launcher_VERSION_MAJOR@; VERSION_MINOR = @Launcher_VERSION_MINOR@; - VERSION_PRERELEASE = "@Launcher_VERSION_PRERELEASE@"; BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@"; BUILD_ARTIFACT = "@Launcher_BUILD_ARTIFACT@"; @@ -129,7 +128,7 @@ Config::Config() QString Config::versionString() const { - return QString("%1.%2%3").arg(VERSION_MAJOR).arg(VERSION_MINOR).arg(VERSION_PRERELEASE); + return QString("%1.%2").arg(VERSION_MAJOR).arg(VERSION_MINOR); } QString Config::printableVersionString() const diff --git a/buildconfig/BuildConfig.h b/buildconfig/BuildConfig.h index f8835f1b3..723b2e714 100644 --- a/buildconfig/BuildConfig.h +++ b/buildconfig/BuildConfig.h @@ -60,8 +60,6 @@ class Config { /// The minor version number. int VERSION_MINOR; - QString VERSION_PRERELEASE; - /** * The version channel * This is used by the updater to determine what channel the current version came from. diff --git a/launcher/filelink/FileLink.cpp b/launcher/filelink/FileLink.cpp index 79b30c665..be69de007 100644 --- a/launcher/filelink/FileLink.cpp +++ b/launcher/filelink/FileLink.cpp @@ -40,8 +40,11 @@ #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif +#include +#include #include #include +#include #endif // Snippet from https://github.com/gulrak/filesystem#using-it-as-single-file-header @@ -63,26 +66,112 @@ namespace fs = std::filesystem; namespace fs = ghc::filesystem; #endif +#if defined Q_OS_WIN32 + +// taken from https://stackoverflow.com/a/25927081 +// getting a proper output to console with redirection support on windows is apparently hell +void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr) +{ + // Re-initialize the C runtime "FILE" handles with clean handles bound to "nul". We do this because it has been + // observed that the file number of our standard handle file objects can be assigned internally to a value of -2 + // when not bound to a valid target, which represents some kind of unknown internal invalid state. In this state our + // call to "_dup2" fails, as it specifically tests to ensure that the target file number isn't equal to this value + // before allowing the operation to continue. We can resolve this issue by first "re-opening" the target files to + // use the "nul" device, which will place them into a valid state, after which we can redirect them to our target + // using the "_dup2" function. + if (bindStdIn) { + FILE* dummyFile; + freopen_s(&dummyFile, "nul", "r", stdin); + } + if (bindStdOut) { + FILE* dummyFile; + freopen_s(&dummyFile, "nul", "w", stdout); + } + if (bindStdErr) { + FILE* dummyFile; + freopen_s(&dummyFile, "nul", "w", stderr); + } + + // Redirect unbuffered stdin from the current standard input handle + if (bindStdIn) { + HANDLE stdHandle = GetStdHandle(STD_INPUT_HANDLE); + if (stdHandle != INVALID_HANDLE_VALUE) { + int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); + if (fileDescriptor != -1) { + FILE* file = _fdopen(fileDescriptor, "r"); + if (file != NULL) { + int dup2Result = _dup2(_fileno(file), _fileno(stdin)); + if (dup2Result == 0) { + setvbuf(stdin, NULL, _IONBF, 0); + } + } + } + } + } + + // Redirect unbuffered stdout to the current standard output handle + if (bindStdOut) { + HANDLE stdHandle = GetStdHandle(STD_OUTPUT_HANDLE); + if (stdHandle != INVALID_HANDLE_VALUE) { + int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); + if (fileDescriptor != -1) { + FILE* file = _fdopen(fileDescriptor, "w"); + if (file != NULL) { + int dup2Result = _dup2(_fileno(file), _fileno(stdout)); + if (dup2Result == 0) { + setvbuf(stdout, NULL, _IONBF, 0); + } + } + } + } + } + + // Redirect unbuffered stderr to the current standard error handle + if (bindStdErr) { + HANDLE stdHandle = GetStdHandle(STD_ERROR_HANDLE); + if (stdHandle != INVALID_HANDLE_VALUE) { + int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); + if (fileDescriptor != -1) { + FILE* file = _fdopen(fileDescriptor, "w"); + if (file != NULL) { + int dup2Result = _dup2(_fileno(file), _fileno(stderr)); + if (dup2Result == 0) { + setvbuf(stderr, NULL, _IONBF, 0); + } + } + } + } + } + + // Clear the error state for each of the C++ standard stream objects. We need to do this, as attempts to access the + // standard streams before they refer to a valid target will cause the iostream objects to enter an error state. In + // versions of Visual Studio after 2005, this seems to always occur during startup regardless of whether anything + // has been read from or written to the targets or not. + if (bindStdIn) { + std::wcin.clear(); + std::cin.clear(); + } + if (bindStdOut) { + std::wcout.clear(); + std::cout.clear(); + } + if (bindStdErr) { + std::wcerr.clear(); + std::cerr.clear(); + } +} +#endif + FileLinkApp::FileLinkApp(int& argc, char** argv) : QCoreApplication(argc, argv), socket(new QLocalSocket(this)) { #if defined Q_OS_WIN32 - // attach the parent console - if (AttachConsole(ATTACH_PARENT_PROCESS)) { - // if attach succeeds, reopen and sync all the i/o - if (freopen("CON", "w", stdout)) { - std::cout.sync_with_stdio(); + // attach the parent console if stdout not already captured + auto stdout_type = GetFileType(GetStdHandle(STD_OUTPUT_HANDLE)); + if (stdout_type == FILE_TYPE_CHAR || stdout_type == FILE_TYPE_UNKNOWN) { + if (AttachConsole(ATTACH_PARENT_PROCESS)) { + BindCrtHandlesToStdHandles(true, true, true); + consoleAttached = true; } - if (freopen("CON", "w", stderr)) { - std::cerr.sync_with_stdio(); - } - if (freopen("CON", "r", stdin)) { - std::cin.sync_with_stdio(); - } - auto out = GetStdHandle(STD_OUTPUT_HANDLE); - DWORD written; - const char* endline = "\n"; - WriteConsole(out, endline, strlen(endline), &written, NULL); - consoleAttached = true; } #endif setOrganizationName(BuildConfig.LAUNCHER_NAME); diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 3fd832b24..614174446 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -39,8 +39,8 @@ #include #include -#include #include +#include #if defined Q_OS_WIN32 #ifndef WIN32_LEAN_AND_MEAN @@ -209,7 +209,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar BindCrtHandlesToStdHandles(true, true, true); consoleAttached = true; } - } + } #endif setOrganizationName(BuildConfig.LAUNCHER_NAME); setOrganizationDomain(BuildConfig.LAUNCHER_DOMAIN); @@ -272,8 +272,6 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar auto prism_update_url = parser.value("update-url"); if (prism_update_url.isEmpty()) prism_update_url = BuildConfig.UPDATER_GITHUB_REPO; - if (prism_update_url.isEmpty()) - prism_update_url = "https://github.com/PrismLauncher/PrismLauncher"; m_prismRepoUrl = QUrl::fromUserInput(prism_update_url); @@ -354,18 +352,20 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar QFile::remove(oldName); }; - moveFile(logBase.arg(1), logBase.arg(2)); - moveFile(logBase.arg(0), logBase.arg(1)); + if (FS::ensureFolderPathExists("logs")) { + moveFile(logBase.arg(1), logBase.arg(2)); + moveFile(logBase.arg(0), logBase.arg(1)); + } logFile = std::unique_ptr(new QFile(logBase.arg(0))); if (!logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { showFatalErrorMessage(tr("The launcher data folder is not writable!"), - tr("The launcher couldn't create a log file - the data folder is not writable.\n" + tr("The updater couldn't create a log file - the data folder is not writable.\n" "\n" "Make sure you have write permissions to the data folder.\n" "(%1)\n" "\n" - "The launcher cannot continue until you fix this problem.") + "The updater cannot continue until you fix this problem.") .arg(m_dataPath)); return; } @@ -560,8 +560,7 @@ void PrismUpdaterApp::run() stdOutStream.flush(); return exit(100); - } - else { + } else { return exit(0); } } @@ -1155,7 +1154,7 @@ bool PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) if (first_parts.length() < 2) return false; m_prismBinaryName = first_parts.takeFirst(); - auto version = first_parts.takeFirst(); + auto version = first_parts.takeFirst().trimmed(); m_prismVersion = version; if (version.contains('-')) { auto index = version.indexOf('-'); From 5f1074471d254ff82af1980e15a53b71ae121186 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 28 Jun 2023 13:35:42 +0300 Subject: [PATCH 068/268] Corected variable name Signed-off-by: Trial97 --- .../minecraft/auth/steps/XboxUserStep.cpp | 2 +- launcher/modplatform/CheckUpdateTask.h | 4 ++-- launcher/modplatform/ModIndex.h | 2 +- launcher/modplatform/flame/FlameAPI.cpp | 2 +- .../modplatform/flame/FlameCheckUpdate.cpp | 2 +- launcher/modplatform/flame/FlameModIndex.cpp | 4 ++-- .../modrinth/ModrinthCheckUpdate.cpp | 2 +- .../modrinth/ModrinthPackIndex.cpp | 4 ++-- launcher/ui/dialogs/ModUpdateDialog.cpp | 20 ++++++++++--------- launcher/ui/pages/modplatform/ModPage.cpp | 2 +- .../ui/pages/modplatform/ResourcePage.cpp | 4 ++-- .../ui/pages/modplatform/flame/FlamePage.cpp | 11 ++++------ .../modplatform/modrinth/ModrinthPage.cpp | 5 +++-- 13 files changed, 32 insertions(+), 32 deletions(-) diff --git a/launcher/minecraft/auth/steps/XboxUserStep.cpp b/launcher/minecraft/auth/steps/XboxUserStep.cpp index 842eb60ff..06b67e81f 100644 --- a/launcher/minecraft/auth/steps/XboxUserStep.cpp +++ b/launcher/minecraft/auth/steps/XboxUserStep.cpp @@ -38,7 +38,7 @@ void XboxUserStep::perform() { QNetworkRequest request = QNetworkRequest(QUrl("https://user.auth.xboxlive.com/user/authenticate")); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setRawHeader("Accept", "application/json"); - // set contract-verison header (prevent err 400 bad-request?) + // set contract-version header (prevent err 400 bad-request?) // https://learn.microsoft.com/en-us/gaming/gdk/_content/gc/reference/live/rest/additional/httpstandardheaders request.setRawHeader("x-xbl-contract-version", "1"); diff --git a/launcher/modplatform/CheckUpdateTask.h b/launcher/modplatform/CheckUpdateTask.h index fbafedd77..65c80d268 100644 --- a/launcher/modplatform/CheckUpdateTask.h +++ b/launcher/modplatform/CheckUpdateTask.h @@ -23,7 +23,7 @@ class CheckUpdateTask : public Task { QString old_hash; QString old_version; QString new_version; - std::optional new_verison_type; + std::optional new_version_type; QString changelog; ModPlatform::ResourceProvider provider; shared_qobject_ptr download; @@ -41,7 +41,7 @@ class CheckUpdateTask : public Task { , old_hash(old_h) , old_version(old_v) , new_version(new_v) - , new_verison_type(new_v_type) + , new_version_type(new_v_type) , changelog(changelog) , provider(p) , download(t) diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index b47d09487..4ed9e7ebe 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -96,7 +96,7 @@ struct IndexedVersion { QVariant fileId; QString version; QString version_number = {}; - IndexedVersionType verison_type; + IndexedVersionType version_type; QStringList mcVersion; QString downloadUrl; QString date; diff --git a/launcher/modplatform/flame/FlameAPI.cpp b/launcher/modplatform/flame/FlameAPI.cpp index 434703009..885c46873 100644 --- a/launcher/modplatform/flame/FlameAPI.cpp +++ b/launcher/modplatform/flame/FlameAPI.cpp @@ -135,7 +135,7 @@ auto FlameAPI::getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::Indexe for (auto file : arr) { auto file_obj = Json::requireObject(file); auto file_tmp = FlameMod::loadIndexedPackVersion(file_obj); - bool better_release = file_tmp.verison_type <= ver_tmp.verison_type; + bool better_release = file_tmp.version_type <= ver_tmp.version_type; if (file_tmp.date > ver_tmp.date && better_release) { ver_tmp = file_tmp; latest_file_obj = file_obj; diff --git a/launcher/modplatform/flame/FlameCheckUpdate.cpp b/launcher/modplatform/flame/FlameCheckUpdate.cpp index cf159cad7..5ff833cfa 100644 --- a/launcher/modplatform/flame/FlameCheckUpdate.cpp +++ b/launcher/modplatform/flame/FlameCheckUpdate.cpp @@ -173,7 +173,7 @@ void FlameCheckUpdate::executeTask() } auto download_task = makeShared(pack, latest_ver, m_mods_folder); - m_updatable.emplace_back(pack->name, mod->metadata()->hash, old_version, latest_ver.version, latest_ver.verison_type, + m_updatable.emplace_back(pack->name, mod->metadata()->hash, old_version, latest_ver.version, latest_ver.version_type, api.getModFileChangelog(latest_ver.addonId.toInt(), latest_ver.fileId.toInt()), ModPlatform::ResourceProvider::FLAME, download_task); } diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index 108d8b37f..5a44d4db7 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -94,7 +94,7 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, } auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool { - bool a_better_release = a.verison_type <= b.verison_type; + bool a_better_release = a.version_type <= b.version_type; // dates are in RFC 3339 format return a.date > b.date && a_better_release; }; @@ -124,7 +124,7 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) -> file.version = Json::requireString(obj, "displayName"); file.downloadUrl = Json::ensureString(obj, "downloadUrl"); file.fileName = Json::requireString(obj, "fileName"); - file.verison_type = ModPlatform::IndexedVersionType(Json::requireInteger(obj, "releaseType")); + file.version_type = ModPlatform::IndexedVersionType(Json::requireInteger(obj, "releaseType")); auto hash_list = Json::ensureArray(obj, "hashes"); for (auto h : hash_list) { diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp index 3ee22818a..845e0c61e 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp @@ -162,7 +162,7 @@ void ModrinthCheckUpdate::executeTask() auto download_task = makeShared(pack, project_ver, m_mods_folder); - m_updatable.emplace_back(pack->name, hash, mod->version(), project_ver.version_number, project_ver.verison_type, + m_updatable.emplace_back(pack->name, hash, mod->version(), project_ver.version_number, project_ver.version_type, project_ver.changelog, ModPlatform::ResourceProvider::MODRINTH, download_task); } } diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index 4d0882f64..d93d870cf 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -109,7 +109,7 @@ void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, unsortedVersions.append(file); } auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool { - bool a_better_release = a.verison_type <= b.verison_type; + bool a_better_release = a.version_type <= b.version_type; // dates are in RFC 3339 format return a.date > b.date && a_better_release; }; @@ -139,7 +139,7 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t } file.version = Json::requireString(obj, "name"); file.version_number = Json::requireString(obj, "version_number"); - file.verison_type = ModPlatform::IndexedVersionType(Json::requireString(obj, "version_type")); + file.version_type = ModPlatform::IndexedVersionType(Json::requireString(obj, "version_type")); file.changelog = Json::requireString(obj, "changelog"); diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index 9e010444c..79622ff74 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -89,15 +89,17 @@ void ModUpdateDialog::checkCandidates() if (!m_modrinth_to_update.empty()) { m_modrinth_check_task.reset(new ModrinthCheckUpdate(m_modrinth_to_update, versions, loaders, m_mod_model)); - connect(m_modrinth_check_task.get(), &CheckUpdateTask::checkFailed, this, - [this](Mod* mod, QString reason, QUrl recover_url) { m_failed_check_update.append({mod, reason, recover_url}); }); + connect(m_modrinth_check_task.get(), &CheckUpdateTask::checkFailed, this, [this](Mod* mod, QString reason, QUrl recover_url) { + m_failed_check_update.append({ mod, reason, recover_url }); + }); check_task.addTask(m_modrinth_check_task); } if (!m_flame_to_update.empty()) { m_flame_check_task.reset(new FlameCheckUpdate(m_flame_to_update, versions, loaders, m_mod_model)); - connect(m_flame_check_task.get(), &CheckUpdateTask::checkFailed, this, - [this](Mod* mod, QString reason, QUrl recover_url) { m_failed_check_update.append({mod, reason, recover_url}); }); + connect(m_flame_check_task.get(), &CheckUpdateTask::checkFailed, this, [this](Mod* mod, QString reason, QUrl recover_url) { + m_failed_check_update.append({ mod, reason, recover_url }); + }); check_task.addTask(m_flame_check_task); } @@ -162,7 +164,7 @@ void ModUpdateDialog::checkCandidates() if (!recover_url.isEmpty()) //: %1 is the link to download it manually text += tr("Possible solution: Getting the latest version manually:
%1
") - .arg(QString("%1").arg(recover_url.toString())); + .arg(QString("%1").arg(recover_url.toString())); text += "
"; } @@ -342,7 +344,7 @@ void ModUpdateDialog::onMetadataFailed(Mod* mod, bool try_others, ModPlatform::R } else { QString reason{ tr("Couldn't find a valid version on the selected mod provider(s)") }; - m_failed_metadata.append({mod, reason}); + m_failed_metadata.append({ mod, reason }); } } @@ -362,11 +364,11 @@ void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info) auto new_version_item = new QTreeWidgetItem(item_top); new_version_item->setText(0, tr("New version: %1").arg(info.new_version)); - if (info.new_verison_type.has_value()) { + if (info.new_version_type.has_value()) { auto new_version_type_itme = new QTreeWidgetItem(item_top); - new_version_type_itme->setText(0, tr("New Version Type: %1").arg(info.new_verison_type.value().toString())); + new_version_type_itme->setText(0, tr("New Version Type: %1").arg(info.new_version_type.value().toString())); } - + auto changelog_item = new QTreeWidgetItem(item_top); changelog_item->setText(0, tr("Changelog of the latest version")); diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index 9c9954a44..9b178048f 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -133,7 +133,7 @@ void ModPage::updateVersionList() // Only add the version if it's valid or using the 'Any' filter, but never if the version is opted out if ((valid || m_filter->versions.empty()) && !optedOut(version)) { - auto release_type = version.verison_type.isValid() ? QString(" : %1").arg(version.verison_type.toString()) : ""; + auto release_type = version.version_type.isValid() ? QString(" [%1]").arg(version.version_type.toString()) : ""; m_ui->versionSelectionBox->addItem(QString("%1%2").arg(version.version, release_type), QVariant(i)); } } diff --git a/launcher/ui/pages/modplatform/ResourcePage.cpp b/launcher/ui/pages/modplatform/ResourcePage.cpp index 5c8f23d22..3619c51a4 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.cpp +++ b/launcher/ui/pages/modplatform/ResourcePage.cpp @@ -268,8 +268,8 @@ void ResourcePage::updateVersionList() if (optedOut(version)) continue; - auto release_type = current_pack->versions[i].verison_type.isValid() - ? QString(" : %1").arg(current_pack->versions[i].verison_type.toString()) + auto release_type = current_pack->versions[i].version_type.isValid() + ? QString(" [%1]").arg(current_pack->versions[i].version_type.toString()) : ""; m_ui->versionSelectionBox->addItem(current_pack->versions[i].version, QVariant(i)); } diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.cpp b/launcher/ui/pages/modplatform/flame/FlamePage.cpp index 575211515..aad376942 100644 --- a/launcher/ui/pages/modplatform/flame/FlamePage.cpp +++ b/launcher/ui/pages/modplatform/flame/FlamePage.cpp @@ -42,9 +42,9 @@ #include "FlameModel.h" #include "InstanceImportTask.h" #include "Json.h" +#include "modplatform/flame/FlameAPI.h" #include "ui/dialogs/NewInstanceDialog.h" #include "ui/widgets/ProjectItem.h" -#include "modplatform/flame/FlameAPI.h" static FlameAPI api; @@ -155,7 +155,7 @@ void FlamePage::onSelectionChanged(QModelIndex curr, QModelIndex prev) } for (auto version : current.versions) { - auto release_type = version.version_type.isValid() ? QString(" : %1").arg(version.version_type.toString()) : ""; + auto release_type = version.version_type.isValid() ? QString(" [%1]").arg(version.version_type.toString()) : ""; ui->versionSelectionBox->addItem(QString("%1%2").arg(version.version, release_type), QVariant(version.downloadUrl)); } @@ -253,10 +253,8 @@ void FlamePage::updateUi() text += "
" + tr(" by ") + authorStrs.join(", "); } - if(current.extraInfoLoaded) { - if (!current.extra.issuesUrl.isEmpty() - || !current.extra.sourceUrl.isEmpty() - || !current.extra.wikiUrl.isEmpty()) { + if (current.extraInfoLoaded) { + if (!current.extra.issuesUrl.isEmpty() || !current.extra.sourceUrl.isEmpty() || !current.extra.wikiUrl.isEmpty()) { text += "

" + tr("External links:") + "
"; } @@ -268,7 +266,6 @@ void FlamePage::updateUi() text += "- " + tr("Source code: %1").arg(current.extra.sourceUrl) + "
"; } - text += "
"; text += api.getModDescription(current.addonId).toUtf8(); diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index 9ca26ea9a..c41f118a6 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -199,9 +199,10 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, QModelIndex prev) qWarning() << "Error while reading modrinth modpack version: " << e.cause(); } for (auto version : current.versions) { - auto release_type = version.version_type.isValid() ? QString(" : %1").arg(version.version_type.toString()) : ""; + auto release_type = version.version_type.isValid() ? QString(" [%1]").arg(version.version_type.toString()) : ""; if (!version.name.contains(version.version)) - ui->versionSelectionBox->addItem(QString("%1 — %2%3").arg(version.name, version.version, release_type), QVariant(version.id)); + ui->versionSelectionBox->addItem(QString("%1 — %2%3").arg(version.name, version.version, release_type), + QVariant(version.id)); else ui->versionSelectionBox->addItem(QString("%1%2").arg(version.name, release_type), QVariant(version.id)); } From 3ad559ab22b6b20264eebb826efc4227374a64cc Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 28 Jun 2023 17:43:09 +0300 Subject: [PATCH 069/268] Added version type to review message dialog Signed-off-by: Trial97 --- launcher/ui/dialogs/ResourceDownloadDialog.cpp | 3 ++- launcher/ui/dialogs/ReviewMessageBox.cpp | 4 ++++ launcher/ui/dialogs/ReviewMessageBox.h | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp index 4f59f5605..4b29df0ad 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp +++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp @@ -187,7 +187,8 @@ void ResourceDownloadDialog::confirm() }); for (auto& task : selected) { confirm_dialog->appendResource({ task->getName(), task->getFilename(), task->getCustomPath(), - ProviderCaps.name(task->getProvider()), getRequiredBy(selected, task) }); + ProviderCaps.name(task->getProvider()), getRequiredBy(selected, task), + task->getVersion().version_type.toString() }); } if (confirm_dialog->exec()) { diff --git a/launcher/ui/dialogs/ReviewMessageBox.cpp b/launcher/ui/dialogs/ReviewMessageBox.cpp index 7b33765fd..d1b7bd156 100644 --- a/launcher/ui/dialogs/ReviewMessageBox.cpp +++ b/launcher/ui/dialogs/ReviewMessageBox.cpp @@ -77,6 +77,10 @@ void ReviewMessageBox::appendResource(ResourceInformation&& info) itemTop->insertChildren(childIndx++, { requiredByItem }); } + auto versionTypeItem = new QTreeWidgetItem(itemTop); + versionTypeItem->setText(0, tr("Version Type: %1").arg(info.version_type)); + itemTop->insertChildren(childIndx++, { versionTypeItem }); + ui->modTreeWidget->addTopLevelItem(itemTop); } diff --git a/launcher/ui/dialogs/ReviewMessageBox.h b/launcher/ui/dialogs/ReviewMessageBox.h index a520cc2a6..596f39c8e 100644 --- a/launcher/ui/dialogs/ReviewMessageBox.h +++ b/launcher/ui/dialogs/ReviewMessageBox.h @@ -18,6 +18,7 @@ class ReviewMessageBox : public QDialog { QString custom_file_path{}; QString provider; QStringList required_by; + QString version_type; }; void appendResource(ResourceInformation&& info); From 46e840fdf12a039e4e7f5e4ee49f834099e3d774 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 28 Jun 2023 09:18:07 -0700 Subject: [PATCH 070/268] fix: add thread sleep to wait for resources - add detail text from logs Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/Application.cpp | 5 ++++- launcher/updater/PrismExternalUpdater.cpp | 18 +++++++++++------- launcher/updater/prismupdater/PrismUpdater.cpp | 4 ++++ 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index a8b26ed74..4c3e36d64 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -999,6 +999,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update In Progress"), infoMsg, QMessageBox::Ignore | QMessageBox::Abort); msgBox.setDefaultButton(QMessageBox::Abort); msgBox.setModal(true); + msgBox.setDetailedText(FS::read(update_log_path)); auto res = msgBox.exec(); switch (res) { case QMessageBox::Ignore: { @@ -1028,6 +1029,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update Failed"), infoMsg, QMessageBox::Ignore | QMessageBox::Abort); msgBox.setDefaultButton(QMessageBox::Abort); msgBox.setModal(true); + msgBox.setDetailedText(FS::read(update_log_path)); auto res = msgBox.exec(); switch (res) { case QMessageBox::Ignore: { @@ -1056,7 +1058,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) .arg(update_log_path); auto msgBox = QMessageBox(QMessageBox::Information, tr("Update Succeeded"), infoMsg, QMessageBox::Ok); msgBox.setDefaultButton(QMessageBox::Ok); - msgBox.open(); + msgBox.setDetailedText(FS::read(update_log_path)); + msgBox.exec(); FS::deletePath(update_success_marker.absoluteFilePath()); } } diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index f2c88cf6a..42515332e 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -23,13 +23,14 @@ #include "PrismExternalUpdater.h" #include #include +#include #include +#include #include #include #include #include #include -#include #include "StringUtils.h" @@ -117,9 +118,6 @@ void PrismExternalUpdater::checkForUpdates() auto std_output = proc.readAllStandardOutput(); auto std_error = proc.readAllStandardError(); - qDebug() << "captured output:" << std_output; - qDebug() << "captured error:" << std_error; - progress.hide(); QCoreApplication::processEvents(); @@ -128,12 +126,17 @@ void PrismExternalUpdater::checkForUpdates() // no update available { qDebug() << "No update available"; + auto msgBox = QMessageBox(QMessageBox::Information, tr("No Update Available"), tr("You are running the latest version.")); + msgBox.exec(); } break; case 1: // there was an error { - qDebug() << "Updater subprocess error" << std_error; + qDebug() << "Updater subprocess error" << qPrintable(std_error); + auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update Check Error"), tr("There was an error running the update check.")); + msgBox.setDetailedText(std_error); + msgBox.exec(); } break; case 100: @@ -264,14 +267,15 @@ void PrismExternalUpdater::offerUpdate(const QString& version_name, const QStrin } } -void PrismExternalUpdater::performUpdate(const QString& version_tag) { +void PrismExternalUpdater::performUpdate(const QString& version_tag) +{ QProcess proc; auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); #if defined Q_OS_WIN32 exe_name.append(".exe"); #endif - QStringList args = { "--dir", priv->dataDir.absolutePath(), "--install-version", version_tag }; + QStringList args = { "--dir", priv->dataDir.absolutePath(), "--install-version", version_tag }; if (priv->allowBeta) args.append("--pre-release"); diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 614174446..4df103517 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -617,6 +617,10 @@ void PrismUpdaterApp::run() void PrismUpdaterApp::moveAndFinishUpdate(QDir target) { logUpdate("Finishing update process"); + + logUpdate("Waiting 2 seconds for resources to free"); + this->thread()->sleep(2); + auto manifest_path = FS::PathCombine(applicationDirPath(), "manifest.txt"); QFileInfo manifest(manifest_path); From e29126ca26a6817d2f5060aacd6a9f1eb5a0eb6d Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 28 Jun 2023 10:56:16 -0700 Subject: [PATCH 071/268] fix: add split markdown source Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 9534c3a3f..9ac496361 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -600,6 +600,7 @@ set(PRISMUPDATER_SOURCES Version.h Version.cpp Markdown.h + Markdown.cpp # Zip MMCZip.h From f287d9deaca3970592edf5ae99f190dc12f11a36 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 28 Jun 2023 11:09:59 -0700 Subject: [PATCH 072/268] fix add NetRequest source Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 080882c6e..4b0e98f83 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -623,6 +623,8 @@ set(PRISMUPDATER_SOURCES net/Logging.h net/Logging.cpp net/NetAction.h + net/NetRequest.cpp + net/NetRequest.h net/NetJob.cpp net/NetJob.h net/NetUtils.h From c1235583146e6f1f6fae997b8ea2808687dbb372 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 28 Jun 2023 11:25:59 -0700 Subject: [PATCH 073/268] fix: add build config header & trimlines form stdout Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/net/NetRequest.cpp | 1 + launcher/updater/PrismExternalUpdater.cpp | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/launcher/net/NetRequest.cpp b/launcher/net/NetRequest.cpp index 304577121..66307099c 100644 --- a/launcher/net/NetRequest.cpp +++ b/launcher/net/NetRequest.cpp @@ -46,6 +46,7 @@ #if defined(LAUNCHER_APPLICATION) #include "Application.h" #endif +#include "BuildConfig.h" #include "net/NetAction.h" diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index 42515332e..9b99b2008 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -145,9 +145,9 @@ void PrismExternalUpdater::checkForUpdates() auto [first_line, remainder1] = StringUtils::splitFirst(std_output, '\n'); auto [second_line, remainder2] = StringUtils::splitFirst(remainder1, '\n'); auto [third_line, release_notes] = StringUtils::splitFirst(remainder2, '\n'); - auto version_name = StringUtils::splitFirst(first_line, ": ").second; - auto version_tag = StringUtils::splitFirst(second_line, ": ").second; - auto release_timestamp = QDateTime::fromString(StringUtils::splitFirst(third_line, ": ").second, Qt::ISODate); + auto version_name = StringUtils::splitFirst(first_line, ": ").second.trimmed(); + auto version_tag = StringUtils::splitFirst(second_line, ": ").second.trimmed(); + auto release_timestamp = QDateTime::fromString(StringUtils::splitFirst(third_line, ": ").second.trimmed(), Qt::ISODate); qDebug() << "Update available:" << version_name << version_tag << release_timestamp; qDebug() << "Update release notes:" << release_notes; From 400a2f7201ecb05af769dff5ea8738dde19205ed Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 28 Jun 2023 12:46:28 -0700 Subject: [PATCH 074/268] fix: final fixes - use `done(code)` for offer dialog Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/ui/dialogs/UpdateAvailableDialog.cpp | 12 ++++++------ launcher/ui/dialogs/UpdateAvailableDialog.h | 8 ++++---- launcher/updater/PrismExternalUpdater.cpp | 10 ++++++++-- launcher/updater/prismupdater/PrismUpdater.cpp | 9 +++++++-- 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/launcher/ui/dialogs/UpdateAvailableDialog.cpp b/launcher/ui/dialogs/UpdateAvailableDialog.cpp index 9f7308cba..28f7167cb 100644 --- a/launcher/ui/dialogs/UpdateAvailableDialog.cpp +++ b/launcher/ui/dialogs/UpdateAvailableDialog.cpp @@ -47,17 +47,17 @@ UpdateAvailableDialog::UpdateAvailableDialog(const QString& currentVersion, ui->releaseNotes->setOpenExternalLinks(true); connect(ui->skipButton, &QPushButton::clicked, this, [this](){ - this->setResult(DialogCode::Skip); - this->close(); + setResult(ResultCode::Skip); + done(ResultCode::Skip); }); connect(ui->delayButton, &QPushButton::clicked, this, [this](){ - this->setResult(DialogCode::DontInstall); - this->close(); + setResult(ResultCode::DontInstall); + done(ResultCode::DontInstall); }); connect(ui->installButton, &QPushButton::clicked, this, [this](){ - this->setResult(DialogCode::Install); - this->close(); + setResult(ResultCode::Install); + done(ResultCode::Install); }); } diff --git a/launcher/ui/dialogs/UpdateAvailableDialog.h b/launcher/ui/dialogs/UpdateAvailableDialog.h index 7a14c01da..f3ea5cbb1 100644 --- a/launcher/ui/dialogs/UpdateAvailableDialog.h +++ b/launcher/ui/dialogs/UpdateAvailableDialog.h @@ -32,10 +32,10 @@ class UpdateAvailableDialog : public QDialog { public: - enum DialogCode { - Install, - DontInstall, - Skip, + enum ResultCode { + Install = 10, + DontInstall = 11, + Skip = 12, }; explicit UpdateAvailableDialog(const QString& currentVersion, diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index 9b99b2008..969897405 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -134,7 +134,8 @@ void PrismExternalUpdater::checkForUpdates() // there was an error { qDebug() << "Updater subprocess error" << qPrintable(std_error); - auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update Check Error"), tr("There was an error running the update check.")); + auto msgBox = + QMessageBox(QMessageBox::Warning, tr("Update Check Error"), tr("There was an error running the update check.")); msgBox.setDetailedText(std_error); msgBox.exec(); } @@ -244,15 +245,20 @@ void PrismExternalUpdater::offerUpdate(const QString& version_name, const QStrin auto should_skip = priv->settings->value(version_tag, false).toBool(); priv->settings->endGroup(); - if (should_skip) + if (should_skip) { + auto msgBox = QMessageBox(QMessageBox::Information, tr("No Update Available"), tr("There are no new updates available.")); + msgBox.exec(); return; + } UpdateAvailableDialog dlg(BuildConfig.printableVersionString(), version_name, release_notes); auto result = dlg.exec(); + qDebug() << "offer dlg result" << result; switch (result) { case UpdateAvailableDialog::Install: { performUpdate(version_tag); + return; } case UpdateAvailableDialog::Skip: { priv->settings->beginGroup("skip"); diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 4df103517..f8819d940 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -327,6 +327,11 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar // on command line adjustedBy = "Command line"; m_dataPath = dirParam; +#ifndef Q_OS_MACOS + if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) { + m_isPortable = true; + } +#endif } else { QDir foo(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "..")); m_dataPath = foo.absolutePath(); @@ -958,8 +963,6 @@ void PrismUpdaterApp::performInstall(QFileInfo file) return showFatalErrorMessage(tr("Update Aborted"), tr("The update attempt was aborted")); } } - write_lock_file(update_lock_path, QDateTime::currentDateTime(), m_prismVersion, m_install_release.tag_name, applicationDirPath(), - m_dataPath); clearUpdateLog(); auto changelog_path = FS::PathCombine(m_dataPath, ".prism_launcher_update.changelog"); @@ -967,6 +970,8 @@ void PrismUpdaterApp::performInstall(QFileInfo file) logUpdate(tr("Updating from %1 to %2").arg(m_prismVersion).arg(m_install_release.tag_name)); if (m_isPortable || file.suffix().toLower() == "zip") { + write_lock_file(update_lock_path, QDateTime::currentDateTime(), m_prismVersion, m_install_release.tag_name, applicationDirPath(), + m_dataPath); logUpdate(tr("Updating portable install at %1").arg(applicationDirPath())); unpackAndInstall(file); } else { From cb7ff81ade4bdc125d19b85aa48a67a493994305 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 28 Jun 2023 13:51:58 -0700 Subject: [PATCH 075/268] fix: copy needs to overwrite Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/FileSystem.cpp | 3 +++ launcher/FileSystem.h | 6 ++++++ launcher/updater/prismupdater/PrismUpdater.cpp | 8 ++++---- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index eff4884db..6dfb69bfa 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -297,6 +297,9 @@ bool copy::operator()(const QString& offset, bool dryRun) if (!m_followSymlinks) opt |= copy_opts::copy_symlinks; + if (m_overwrite) + opt |= copy_opts::overwrite_existing; + // 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)) diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h index ca26c1ee7..ab29cf936 100644 --- a/launcher/FileSystem.h +++ b/launcher/FileSystem.h @@ -120,6 +120,11 @@ class copy : public QObject { m_whitelist = whitelist; return *this; } + copy& overwrite(const bool overwrite) + { + m_overwrite = overwrite; + return *this; + } bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); } @@ -136,6 +141,7 @@ class copy : public QObject { bool m_followSymlinks = true; const IPathMatcher* m_matcher = nullptr; bool m_whitelist = false; + bool m_overwrite = false; QDir m_src; QDir m_dst; int m_copied; diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index f8819d940..ef7e505b9 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -672,7 +672,7 @@ void PrismUpdaterApp::moveAndFinishUpdate(QDir target) auto install_path = FS::PathCombine(target.absolutePath(), rel_path); logUpdate(tr("Installing %1 from %2").arg(install_path).arg(to_install_file)); FS::ensureFilePathExists(install_path); - auto result = FS::copy(to_install_file, install_path)(); + auto result = FS::copy(to_install_file, install_path).overwrite(true)(); if (!result) { error = true; logUpdate(tr("Failed copy %1 to %2").arg(to_install_file).arg(install_path)); @@ -686,11 +686,11 @@ void PrismUpdaterApp::moveAndFinishUpdate(QDir target) if (error) { logUpdate(tr("There were errors installing the update.")); auto fail_marker = FS::PathCombine(m_dataPath, ".prism_launcher_update.fail"); - FS::copy(m_updateLogPath, fail_marker)(); + FS::copy(m_updateLogPath, fail_marker).overwrite(true)(); } else { logUpdate(tr("Update succeed.")); auto success_marker = FS::PathCombine(m_dataPath, ".prism_launcher_update.success"); - FS::copy(m_updateLogPath, success_marker)(); + FS::copy(m_updateLogPath, success_marker).overwrite(true)(); } auto update_lock_path = FS::PathCombine(m_dataPath, ".prism_launcher_update.lock"); FS::deletePath(update_lock_path); @@ -1078,7 +1078,7 @@ void PrismUpdaterApp::backupAppDir() auto bak_path = FS::PathCombine(backup_dir, rel_path); logUpdate(tr("Backing up and then removing %1").arg(to_bak_file)); FS::ensureFilePathExists(bak_path); - auto result = FS::copy(to_bak_file, bak_path)(); + auto result = FS::copy(to_bak_file, bak_path).overwrite(true)(); if (!result) { logUpdate(tr("Failed to backup %1 to %2").arg(to_bak_file).arg(bak_path)); } else { From e38adf6006e36928af92b317a1605eee063d70ed Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Thu, 29 Jun 2023 14:01:11 -0700 Subject: [PATCH 076/268] fix(updater) fixes form first round of testing - reset update time after check Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- buildconfig/BuildConfig.cpp.in | 7 ++- launcher/Application.cpp | 40 +++++++++++----- launcher/Application.h | 3 ++ launcher/ui/MainWindow.cpp | 8 ++-- launcher/updater/PrismExternalUpdater.cpp | 46 +++++++++++++++++-- .../updater/prismupdater/PrismUpdater.cpp | 21 +++++++-- 6 files changed, 95 insertions(+), 30 deletions(-) diff --git a/buildconfig/BuildConfig.cpp.in b/buildconfig/BuildConfig.cpp.in index e8e8a4ef8..b1c9b5bef 100644 --- a/buildconfig/BuildConfig.cpp.in +++ b/buildconfig/BuildConfig.cpp.in @@ -77,6 +77,8 @@ Config::Config() if (BUILD_PLATFORM == "macOS" && !MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty()) { UPDATER_ENABLED = true; + } else if(!UPDATER_GITHUB_REPO.isEmpty() && !BUILD_ARTIFACT.isEmpty()) { + UPDATER_ENABLED = true; } GIT_COMMIT = "@Launcher_GIT_COMMIT@"; @@ -97,10 +99,7 @@ Config::Config() if (GIT_REFSPEC.startsWith("refs/heads/")) { VERSION_CHANNEL = GIT_REFSPEC; - VERSION_CHANNEL.remove("refs/heads/"); - if(!UPDATER_GITHUB_REPO.isEmpty() && !BUILD_PLATFORM.isEmpty()) { - UPDATER_ENABLED = true; - } + VERSION_CHANNEL.remove("refs/heads/"); } else if (!GIT_COMMIT.isEmpty()) { diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 1a381a045..c59bea8d6 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -584,6 +584,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC; qDebug() << "Compiled for : " << BuildConfig.systemID(); qDebug() << "Compiled by : " << BuildConfig.compilerID(); + qDebug() << "Build Artifact : " << BuildConfig.BUILD_ARTIFACT; + qDebug() << "Updates Enabeled : " << (updaterEnabled() ? "Yes" : "No"); if (adjustedBy.size()) { qDebug() << "Work dir before adjustment : " << origcwdPath; qDebug() << "Work dir after adjustment : " << QDir::currentPath(); @@ -856,18 +858,12 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) } // initialize the updater - if (BuildConfig.UPDATER_ENABLED) { + if (updaterEnabled()) { qDebug() << "Initializing updater"; #ifdef Q_OS_MAC m_updater.reset(new MacSparkleUpdater()); #else - auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); -#if defined Q_OS_WIN32 - exe_name.append(".exe"); -#endif - auto updater_binary = QFileInfo(QDir(m_rootPath).absoluteFilePath(exe_name)); - if (updater_binary.isFile()) - m_updater.reset(new PrismExternalUpdater(m_rootPath, m_dataPath)); + m_updater.reset(new PrismExternalUpdater(applicationDirPath(), m_dataPath)); #endif qDebug() << "<> Updater started."; } @@ -1000,6 +996,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) msgBox.setDefaultButton(QMessageBox::Abort); msgBox.setModal(true); msgBox.setDetailedText(FS::read(update_log_path)); + msgBox.adjustSize(); auto res = msgBox.exec(); switch (res) { case QMessageBox::Ignore: { @@ -1030,6 +1027,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) msgBox.setDefaultButton(QMessageBox::Abort); msgBox.setModal(true); msgBox.setDetailedText(FS::read(update_log_path)); + msgBox.adjustSize(); auto res = msgBox.exec(); switch (res) { case QMessageBox::Ignore: { @@ -1056,10 +1054,12 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) "for details.") .arg(BuildConfig.printableVersionString()) .arg(update_log_path); - auto msgBox = QMessageBox(QMessageBox::Information, tr("Update Succeeded"), infoMsg, QMessageBox::Ok); - msgBox.setDefaultButton(QMessageBox::Ok); - msgBox.setDetailedText(FS::read(update_log_path)); - msgBox.exec(); + auto msgBox = new QMessageBox(QMessageBox::Information, tr("Update Succeeded"), infoMsg, QMessageBox::Ok); + msgBox->setDefaultButton(QMessageBox::Ok); + msgBox->setDetailedText(FS::read(update_log_path)); + msgBox->setAttribute(Qt::WA_DeleteOnClose); + msgBox->adjustSize(); + msgBox->open(); FS::deletePath(update_success_marker.absoluteFilePath()); } } @@ -1127,6 +1127,22 @@ bool Application::createSetupWizard() return false; } +bool Application::updaterEnabled() { +#if defined(Q_OS_MAC) + return BuildConfig.UPDATER_ENABLED; +#else + return BuildConfig.UPDATER_ENABLED && QFileInfo(updaterBinaryName()).isFile(); +#endif +} + +QString Application::updaterBinaryName() { + auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); +#if defined Q_OS_WIN32 + exe_name.append(".exe"); +#endif + return exe_name; +} + bool Application::event(QEvent* event) { #ifdef Q_OS_MACOS diff --git a/launcher/Application.h b/launcher/Application.h index baf64575d..c87ee2652 100644 --- a/launcher/Application.h +++ b/launcher/Application.h @@ -216,6 +216,9 @@ public: int suitableMaxMem(); + bool updaterEnabled(); + QString updaterBinaryName(); + signals: void updateAllowedChanged(bool status); void globalSettingsAboutToOpen(); diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 496738e32..16d696dc8 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -215,7 +215,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi ui->actionDISCORD->setVisible(!BuildConfig.DISCORD_URL.isEmpty()); ui->actionREDDIT->setVisible(!BuildConfig.SUBREDDIT_URL.isEmpty()); - ui->actionCheckUpdate->setVisible(BuildConfig.UPDATER_ENABLED); + ui->actionCheckUpdate->setVisible(APPLICATION->updaterEnabled()); #ifndef Q_OS_MAC ui->actionAddToPATH->setVisible(false); @@ -388,7 +388,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi updateNewsLabel(); } - if (BuildConfig.UPDATER_ENABLED) { + if (APPLICATION->updaterEnabled()) { bool updatesAllowed = APPLICATION->updatesAreAllowed(); updatesAllowedChanged(updatesAllowed); @@ -781,7 +781,7 @@ void MainWindow::repopulateAccountsMenu() void MainWindow::updatesAllowedChanged(bool allowed) { - if(!BuildConfig.UPDATER_ENABLED) + if(!APPLICATION->updaterEnabled()) { return; } @@ -1259,7 +1259,7 @@ void MainWindow::on_actionViewCentralModsFolder_triggered() void MainWindow::checkForUpdates() { - if(BuildConfig.UPDATER_ENABLED) + if(APPLICATION->updaterEnabled()) { APPLICATION->triggerUpdateCheck(); } diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index 969897405..7f987204a 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -40,7 +40,7 @@ class PrismExternalUpdater::Private { public: - QDir appDir; + QDir binDir; QDir dataDir; QTimer updateTimer; bool allowBeta; @@ -50,10 +50,10 @@ class PrismExternalUpdater::Private { std::unique_ptr settings; }; -PrismExternalUpdater::PrismExternalUpdater(const QString& appDir, const QString& dataDir) +PrismExternalUpdater::PrismExternalUpdater(const QString& binDir, const QString& dataDir) { priv = new PrismExternalUpdater::Private(); - priv->appDir = QDir(appDir); + priv->binDir = QDir(binDir); priv->dataDir = QDir(dataDir); auto settings_file = priv->dataDir.absoluteFilePath("prismlauncher_update.cfg"); priv->settings = std::make_unique(settings_file, QSettings::Format::IniFormat); @@ -97,20 +97,42 @@ void PrismExternalUpdater::checkForUpdates() if (priv->allowBeta) args.append("--pre-release"); - proc.start(priv->appDir.absoluteFilePath(exe_name), args); + proc.start(priv->binDir.absoluteFilePath(exe_name), args); auto result_start = proc.waitForStarted(5000); if (!result_start) { auto err = proc.error(); qDebug() << "Failed to start updater after 5 seconds." << "reason:" << err << proc.errorString(); + auto msgBox = QMessageBox(QMessageBox::Information, tr("Update Check Failed"), + tr("Failed to start after 5 seconds\nReason: %1.").arg(proc.errorString())); + + msgBox.adjustSize(); + msgBox.exec(); + priv->lastCheck = QDateTime::currentDateTime(); + priv->settings->setValue("last_check", priv->lastCheck.toString(Qt::ISODate)); + priv->settings->sync(); + resetAutoCheckTimer(); + return; } QCoreApplication::processEvents(); auto result_finished = proc.waitForFinished(60000); if (!result_finished) { + proc.kill(); auto err = proc.error(); + auto output = proc.readAll(); qDebug() << "Updater failed to close after 60 seconds." << "reason:" << err << proc.errorString(); + auto msgBox = QMessageBox(QMessageBox::Information, tr("Update Check Failed"), + tr("Updater failed to close 60 seconds\nReason: %1.").arg(proc.errorString())); + msgBox.setDetailedText(output); + msgBox.adjustSize(); + msgBox.exec(); + priv->lastCheck = QDateTime::currentDateTime(); + priv->settings->setValue("last_check", priv->lastCheck.toString(Qt::ISODate)); + priv->settings->sync(); + resetAutoCheckTimer(); + return; } auto exit_code = proc.exitCode(); @@ -127,6 +149,8 @@ void PrismExternalUpdater::checkForUpdates() { qDebug() << "No update available"; auto msgBox = QMessageBox(QMessageBox::Information, tr("No Update Available"), tr("You are running the latest version.")); + + msgBox.adjustSize(); msgBox.exec(); } break; @@ -137,6 +161,8 @@ void PrismExternalUpdater::checkForUpdates() auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update Check Error"), tr("There was an error running the update check.")); msgBox.setDetailedText(std_error); + + msgBox.adjustSize(); msgBox.exec(); } break; @@ -159,11 +185,19 @@ void PrismExternalUpdater::checkForUpdates() // unknown error code { qDebug() << "Updater exited with unknown code" << exit_code; + auto msgBox = QMessageBox(QMessageBox::Information, tr("Unknown Update Error"), + tr("The updater exited with an unknown condition.\nExit Code: %1").arg(exit_code)); + auto detail_txt = tr("StdOut: %1\nStdErr: %2").arg(std_output).arg(std_error); + msgBox.setDetailedText(detail_txt); + + msgBox.adjustSize(); + msgBox.exec(); } } priv->lastCheck = QDateTime::currentDateTime(); priv->settings->setValue("last_check", priv->lastCheck.toString(Qt::ISODate)); priv->settings->sync(); + resetAutoCheckTimer(); } bool PrismExternalUpdater::getAutomaticallyChecksForUpdates() @@ -247,6 +281,8 @@ void PrismExternalUpdater::offerUpdate(const QString& version_name, const QStrin if (should_skip) { auto msgBox = QMessageBox(QMessageBox::Information, tr("No Update Available"), tr("There are no new updates available.")); + + msgBox.adjustSize(); msgBox.exec(); return; } @@ -285,7 +321,7 @@ void PrismExternalUpdater::performUpdate(const QString& version_tag) if (priv->allowBeta) args.append("--pre-release"); - auto result = proc.startDetached(priv->appDir.absoluteFilePath(exe_name), args); + auto result = proc.startDetached(priv->binDir.absoluteFilePath(exe_name), args); if (!result) { qDebug() << "Failed to start updater:" << proc.error() << proc.errorString(); } diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index ef7e505b9..1859d5262 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -350,14 +350,14 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar { // setup logging static const QString baseLogFile = BuildConfig.LAUNCHER_NAME + "Updater" + (m_checkOnly ? "-CheckOnly" : "") + "-%0.log"; - static const QString logBase = FS::PathCombine("logs", baseLogFile); + static const QString logBase = FS::PathCombine(m_dataPath, "logs", baseLogFile); auto moveFile = [](const QString& oldName, const QString& newName) { QFile::remove(newName); QFile::copy(oldName, newName); QFile::remove(oldName); }; - if (FS::ensureFolderPathExists("logs")) { + if (FS::ensureFolderPathExists("logs")) { // enough history to track both launches of the updater during a portable install moveFile(logBase.arg(1), logBase.arg(2)); moveFile(logBase.arg(0), logBase.arg(1)); } @@ -442,6 +442,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC; qDebug() << "Compiled for : " << BuildConfig.systemID(); qDebug() << "Compiled by : " << BuildConfig.compilerID(); + qDebug() << "Build Artifact : " << BuildConfig.BUILD_ARTIFACT; if (adjustedBy.size()) { qDebug() << "Data dir before adjustment : " << origCwdPath; qDebug() << "Data dir after adjustment : " << m_dataPath; @@ -521,6 +522,7 @@ void PrismUpdaterApp::showFatalErrorMessage(const QString& title, const QString& msgBox->setDefaultButton(QMessageBox::Ok); msgBox->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextBrowserInteraction); msgBox->setIcon(QMessageBox::Critical); + msgBox->adjustSize(); msgBox->exec(); exit(1); } @@ -658,6 +660,7 @@ void PrismUpdaterApp::moveAndFinishUpdate(QDir target) QProgressDialog progress(tr("Backing up install at %1").arg(applicationDirPath()), "", 0, file_list.length()); progress.setCancelButton(nullptr); + progress.adjustSize(); progress.show(); QCoreApplication::processEvents(); @@ -766,10 +769,13 @@ QList PrismUpdaterApp::validReleaseArtifacts(const GitHubRel if (BuildConfig.BUILD_ARTIFACT.isEmpty()) qWarning() << "Build platform is not set!"; for (auto asset : release.assets) { - if (!m_isAppimage && asset.name.toLower().endsWith("appimage")) + if (!m_isAppimage && asset.name.toLower().endsWith("appimage")) { + qDebug() << "Rejecting" << asset.name << "because it is an AppImage"; continue; - else if (m_isAppimage && !asset.name.toLower().endsWith("appimage")) + } else if (m_isAppimage && !asset.name.toLower().endsWith("appimage")) { + qDebug() << "Rejecting" << asset.name << "because it is not an AppImage"; continue; + } auto asset_name = asset.name.toLower(); auto platform = BuildConfig.BUILD_ARTIFACT.toLower(); auto system_is_arm = QSysInfo::buildCpuArchitecture().contains("arm64"); @@ -786,6 +792,8 @@ QList PrismUpdaterApp::validReleaseArtifacts(const GitHubRel for_platform = false; if (((m_isPortable && for_portable) || (!m_isPortable && !for_portable)) && for_platform) { + qDebug() << "Rejecting" << asset.name << "|" + << "For Platform:" << (for_platform ? "Yes" : "No") << "For Portable:" << (for_portable ? "Yes" : "No"); valid.append(asset); } } @@ -849,6 +857,7 @@ QFileInfo PrismUpdaterApp::downloadAsset(const GitHubReleaseAsset& asset) auto download = Net::Download::makeFile(file_url, out_file_path); download->setNetwork(m_network); auto progress_dialog = ProgressDialog(); + progress_dialog.adjustSize(); if (progress_dialog.execWithTask(download.get()) == QDialog::Rejected) showFatalErrorMessage(tr("Download Aborted"), tr("Download of %1 aborted by user").arg(file_url.toString())); @@ -954,6 +963,7 @@ void PrismUpdaterApp::performInstall(QFileInfo file) msgBox.setInformativeText(infoMsg); msgBox.setStandardButtons(QMessageBox::Ignore | QMessageBox::Cancel); msgBox.setDefaultButton(QMessageBox::Cancel); + msgBox.adjustSize(); switch (msgBox.exec()) { case QMessageBox::AcceptRole: break; @@ -971,7 +981,7 @@ void PrismUpdaterApp::performInstall(QFileInfo file) logUpdate(tr("Updating from %1 to %2").arg(m_prismVersion).arg(m_install_release.tag_name)); if (m_isPortable || file.suffix().toLower() == "zip") { write_lock_file(update_lock_path, QDateTime::currentDateTime(), m_prismVersion, m_install_release.tag_name, applicationDirPath(), - m_dataPath); + m_dataPath); logUpdate(tr("Updating portable install at %1").arg(applicationDirPath())); unpackAndInstall(file); } else { @@ -1065,6 +1075,7 @@ void PrismUpdaterApp::backupAppDir() QProgressDialog progress(tr("Backing up install at %1").arg(applicationDirPath()), "", 0, file_list.length()); progress.setCancelButton(nullptr); + progress.adjustSize(); progress.show(); QCoreApplication::processEvents(); int i = 0; From a6c8a37a5deca0fcdb14f53a5f91aa83c3d558b5 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Thu, 29 Jun 2023 14:06:14 -0700 Subject: [PATCH 077/268] fixa(updater): better update timer logs Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/updater/PrismExternalUpdater.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index 7f987204a..d51ad690e 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -60,6 +60,7 @@ PrismExternalUpdater::PrismExternalUpdater(const QString& binDir, const QString& priv->allowBeta = priv->settings->value("allow_beta", false).toBool(); priv->autoCheck = priv->settings->value("auto_check", false).toBool(); bool interval_ok; + // default once per day priv->updateInterval = priv->settings->value("update_interval", 86400).toInt(&interval_ok); if (!interval_ok) priv->updateInterval = 86400; @@ -240,9 +241,9 @@ void PrismExternalUpdater::setBetaAllowed(bool allowed) void PrismExternalUpdater::resetAutoCheckTimer() { - int timeoutDuration = 0; - auto now = QDateTime::currentDateTime(); if (priv->autoCheck) { + int timeoutDuration = 0; + auto now = QDateTime::currentDateTime(); if (priv->lastCheck.isValid()) { auto diff = priv->lastCheck.secsTo(now); auto secs_left = priv->updateInterval - diff; @@ -270,6 +271,7 @@ void PrismExternalUpdater::disconnectTimer() void PrismExternalUpdater::autoCheckTimerFired() { + qDebug() << "Auto update Timer fired"; checkForUpdates(); } From 494483fd2b8b3ecc8c7ad19d493a8f7552cc95e2 Mon Sep 17 00:00:00 2001 From: flow Date: Thu, 29 Jun 2023 19:09:52 -0300 Subject: [PATCH 078/268] fix(updater): add parent to dialog to fix issues in tilling WMs QProgressDialog without parents in Qt tend to cause graphical issues on some systems, so we add the main window as a parent here! Signed-off-by: flow --- launcher/Application.cpp | 38 +++++++++++------------ launcher/updater/PrismExternalUpdater.cpp | 7 +++-- launcher/updater/PrismExternalUpdater.h | 2 +- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index c59bea8d6..8007f26a9 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -857,17 +857,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) qDebug() << "<> Translations loaded."; } - // initialize the updater - if (updaterEnabled()) { - qDebug() << "Initializing updater"; -#ifdef Q_OS_MAC - m_updater.reset(new MacSparkleUpdater()); -#else - m_updater.reset(new PrismExternalUpdater(applicationDirPath(), m_dataPath)); -#endif - qDebug() << "<> Updater started."; - } - // Instance icons { auto setting = APPLICATION->settings()->getSetting("IconsDir"); @@ -968,6 +957,25 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) applyCurrentlySelectedTheme(true); + updateCapabilities(); + + if (createSetupWizard()) { + return; + } + + performMainStartupAction(); + + // initialize the updater + if (updaterEnabled()) { + qDebug() << "Initializing updater"; +#ifdef Q_OS_MAC + m_updater.reset(new MacSparkleUpdater()); +#else + m_updater.reset(new PrismExternalUpdater(m_mainWindow, applicationDirPath(), m_dataPath)); +#endif + qDebug() << "<> Updater started."; + } + // check update locks { auto update_log_path = FS::PathCombine(m_dataPath, "logs", "prism_launcher_update.log"); @@ -1063,14 +1071,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) FS::deletePath(update_success_marker.absoluteFilePath()); } } - - updateCapabilities(); - - if (createSetupWizard()) { - return; - } - - performMainStartupAction(); } bool Application::createSetupWizard() diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index d51ad690e..792dd2a52 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -48,9 +48,11 @@ class PrismExternalUpdater::Private { double updateInterval; QDateTime lastCheck; std::unique_ptr settings; + + QWidget* parent; }; -PrismExternalUpdater::PrismExternalUpdater(const QString& binDir, const QString& dataDir) +PrismExternalUpdater::PrismExternalUpdater(QWidget* parent, const QString& binDir, const QString& dataDir) { priv = new PrismExternalUpdater::Private(); priv->binDir = QDir(binDir); @@ -68,6 +70,7 @@ PrismExternalUpdater::PrismExternalUpdater(const QString& binDir, const QString& if (!last_check.isNull() && last_check.isValid()) { priv->lastCheck = QDateTime::fromString(last_check.toString(), Qt::ISODate); } + priv->parent = parent; connectTimer(); resetAutoCheckTimer(); } @@ -83,7 +86,7 @@ PrismExternalUpdater::~PrismExternalUpdater() void PrismExternalUpdater::checkForUpdates() { - QProgressDialog progress(tr("Checking for updates..."), "", 0, 0); + QProgressDialog progress(tr("Checking for updates..."), "", 0, 0, priv->parent); progress.setCancelButton(nullptr); progress.show(); QCoreApplication::processEvents(); diff --git a/launcher/updater/PrismExternalUpdater.h b/launcher/updater/PrismExternalUpdater.h index f8ed29ccc..bfe94c149 100644 --- a/launcher/updater/PrismExternalUpdater.h +++ b/launcher/updater/PrismExternalUpdater.h @@ -34,7 +34,7 @@ class PrismExternalUpdater : public ExternalUpdater { Q_OBJECT public: - PrismExternalUpdater(const QString& appDir, const QString& dataDir); + PrismExternalUpdater(QWidget* parent, const QString& appDir, const QString& dataDir); ~PrismExternalUpdater() override; /*! From 41cb8d7ec609b91e191e2c89ef651d2f141140cf Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Thu, 29 Jun 2023 15:24:23 -0700 Subject: [PATCH 079/268] fix: adjust dialog size, add parent to msgboxes Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/updater/PrismExternalUpdater.cpp | 24 ++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index 792dd2a52..c3b3bcfee 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -88,6 +88,7 @@ void PrismExternalUpdater::checkForUpdates() { QProgressDialog progress(tr("Checking for updates..."), "", 0, 0, priv->parent); progress.setCancelButton(nullptr); + progress.adjustSize(); progress.show(); QCoreApplication::processEvents(); @@ -107,8 +108,9 @@ void PrismExternalUpdater::checkForUpdates() auto err = proc.error(); qDebug() << "Failed to start updater after 5 seconds." << "reason:" << err << proc.errorString(); - auto msgBox = QMessageBox(QMessageBox::Information, tr("Update Check Failed"), - tr("Failed to start after 5 seconds\nReason: %1.").arg(proc.errorString())); + auto msgBox = + QMessageBox(QMessageBox::Information, tr("Update Check Failed"), + tr("Failed to start after 5 seconds\nReason: %1.").arg(proc.errorString()), QMessageBox::Ok, priv->parent); msgBox.adjustSize(); msgBox.exec(); @@ -127,8 +129,9 @@ void PrismExternalUpdater::checkForUpdates() auto output = proc.readAll(); qDebug() << "Updater failed to close after 60 seconds." << "reason:" << err << proc.errorString(); - auto msgBox = QMessageBox(QMessageBox::Information, tr("Update Check Failed"), - tr("Updater failed to close 60 seconds\nReason: %1.").arg(proc.errorString())); + auto msgBox = + QMessageBox(QMessageBox::Information, tr("Update Check Failed"), + tr("Updater failed to close 60 seconds\nReason: %1.").arg(proc.errorString()), QMessageBox::Ok, priv->parent); msgBox.setDetailedText(output); msgBox.adjustSize(); msgBox.exec(); @@ -152,7 +155,8 @@ void PrismExternalUpdater::checkForUpdates() // no update available { qDebug() << "No update available"; - auto msgBox = QMessageBox(QMessageBox::Information, tr("No Update Available"), tr("You are running the latest version.")); + auto msgBox = QMessageBox(QMessageBox::Information, tr("No Update Available"), tr("You are running the latest version."), + QMessageBox::Ok, priv->parent); msgBox.adjustSize(); msgBox.exec(); @@ -162,8 +166,8 @@ void PrismExternalUpdater::checkForUpdates() // there was an error { qDebug() << "Updater subprocess error" << qPrintable(std_error); - auto msgBox = - QMessageBox(QMessageBox::Warning, tr("Update Check Error"), tr("There was an error running the update check.")); + auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update Check Error"), + tr("There was an error running the update check."), QMessageBox::Ok, priv->parent); msgBox.setDetailedText(std_error); msgBox.adjustSize(); @@ -190,7 +194,8 @@ void PrismExternalUpdater::checkForUpdates() { qDebug() << "Updater exited with unknown code" << exit_code; auto msgBox = QMessageBox(QMessageBox::Information, tr("Unknown Update Error"), - tr("The updater exited with an unknown condition.\nExit Code: %1").arg(exit_code)); + tr("The updater exited with an unknown condition.\nExit Code: %1").arg(exit_code), + QMessageBox::Ok, priv->parent); auto detail_txt = tr("StdOut: %1\nStdErr: %2").arg(std_output).arg(std_error); msgBox.setDetailedText(detail_txt); @@ -285,7 +290,8 @@ void PrismExternalUpdater::offerUpdate(const QString& version_name, const QStrin priv->settings->endGroup(); if (should_skip) { - auto msgBox = QMessageBox(QMessageBox::Information, tr("No Update Available"), tr("There are no new updates available.")); + auto msgBox = QMessageBox(QMessageBox::Information, tr("No Update Available"), tr("There are no new updates available."), + QMessageBox::Ok, priv->parent); msgBox.adjustSize(); msgBox.exec(); From 109ae5bae0f487e8218d3e4c12b610399541205a Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Thu, 29 Jun 2023 15:42:36 -0700 Subject: [PATCH 080/268] fix(updater): convert int to string Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/updater/PrismExternalUpdater.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index c3b3bcfee..585d8f8bf 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -193,9 +193,10 @@ void PrismExternalUpdater::checkForUpdates() // unknown error code { qDebug() << "Updater exited with unknown code" << exit_code; - auto msgBox = QMessageBox(QMessageBox::Information, tr("Unknown Update Error"), - tr("The updater exited with an unknown condition.\nExit Code: %1").arg(exit_code), - QMessageBox::Ok, priv->parent); + auto msgBox = + QMessageBox(QMessageBox::Information, tr("Unknown Update Error"), + tr("The updater exited with an unknown condition.\nExit Code: %1").arg(QString::number(exit_code)), + QMessageBox::Ok, priv->parent); auto detail_txt = tr("StdOut: %1\nStdErr: %2").arg(std_output).arg(std_error); msgBox.setDetailedText(detail_txt); From 603e3e7e2edb53ee2cf7f15846b04c4acd29e9df Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Thu, 29 Jun 2023 15:56:44 -0700 Subject: [PATCH 081/268] fix(updater): set minimum dialog / msgbox sizes Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/Application.cpp | 17 +++++++++++------ launcher/updater/PrismExternalUpdater.cpp | 11 ++++++----- launcher/updater/prismupdater/PrismUpdater.cpp | 4 ++++ 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 8007f26a9..1993a1491 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -1004,6 +1004,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) msgBox.setDefaultButton(QMessageBox::Abort); msgBox.setModal(true); msgBox.setDetailedText(FS::read(update_log_path)); + msgBox.setMinimumWidth(460); msgBox.adjustSize(); auto res = msgBox.exec(); switch (res) { @@ -1015,7 +1016,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) [[fallthrough]]; default: { qDebug() << "Exiting because update lockfile is present"; - QMetaObject::invokeMethod(this, [](){ exit(1); }, Qt::QueuedConnection); + QMetaObject::invokeMethod( + this, []() { exit(1); }, Qt::QueuedConnection); return; } } @@ -1035,6 +1037,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) msgBox.setDefaultButton(QMessageBox::Abort); msgBox.setModal(true); msgBox.setDetailedText(FS::read(update_log_path)); + msgBox.setMinimumWidth(460); msgBox.adjustSize(); auto res = msgBox.exec(); switch (res) { @@ -1046,7 +1049,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) [[fallthrough]]; default: { qDebug() << "Exiting because update lockfile is present"; - QMetaObject::invokeMethod(this, [](){ exit(1); }, Qt::QueuedConnection); + QMetaObject::invokeMethod( + this, []() { exit(1); }, Qt::QueuedConnection); return; } } @@ -1066,6 +1070,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) msgBox->setDefaultButton(QMessageBox::Ok); msgBox->setDetailedText(FS::read(update_log_path)); msgBox->setAttribute(Qt::WA_DeleteOnClose); + msgBox->setMinimumWidth(460); msgBox->adjustSize(); msgBox->open(); FS::deletePath(update_success_marker.absoluteFilePath()); @@ -1127,7 +1132,8 @@ bool Application::createSetupWizard() return false; } -bool Application::updaterEnabled() { +bool Application::updaterEnabled() +{ #if defined(Q_OS_MAC) return BuildConfig.UPDATER_ENABLED; #else @@ -1135,7 +1141,8 @@ bool Application::updaterEnabled() { #endif } -QString Application::updaterBinaryName() { +QString Application::updaterBinaryName() +{ auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); #if defined Q_OS_WIN32 exe_name.append(".exe"); @@ -1174,8 +1181,6 @@ void Application::performMainStartupAction() { m_status = Application::Initialized; - - if (!m_instanceIdToLaunch.isEmpty()) { auto inst = instances()->getInstanceById(m_instanceIdToLaunch); if (inst) { diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index 585d8f8bf..29d411a1e 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -111,7 +111,7 @@ void PrismExternalUpdater::checkForUpdates() auto msgBox = QMessageBox(QMessageBox::Information, tr("Update Check Failed"), tr("Failed to start after 5 seconds\nReason: %1.").arg(proc.errorString()), QMessageBox::Ok, priv->parent); - + msgBox.setMinimumWidth(460); msgBox.adjustSize(); msgBox.exec(); priv->lastCheck = QDateTime::currentDateTime(); @@ -133,6 +133,7 @@ void PrismExternalUpdater::checkForUpdates() QMessageBox(QMessageBox::Information, tr("Update Check Failed"), tr("Updater failed to close 60 seconds\nReason: %1.").arg(proc.errorString()), QMessageBox::Ok, priv->parent); msgBox.setDetailedText(output); + msgBox.setMinimumWidth(460); msgBox.adjustSize(); msgBox.exec(); priv->lastCheck = QDateTime::currentDateTime(); @@ -157,7 +158,7 @@ void PrismExternalUpdater::checkForUpdates() qDebug() << "No update available"; auto msgBox = QMessageBox(QMessageBox::Information, tr("No Update Available"), tr("You are running the latest version."), QMessageBox::Ok, priv->parent); - + msgBox.setMinimumWidth(460); msgBox.adjustSize(); msgBox.exec(); } @@ -169,7 +170,7 @@ void PrismExternalUpdater::checkForUpdates() auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update Check Error"), tr("There was an error running the update check."), QMessageBox::Ok, priv->parent); msgBox.setDetailedText(std_error); - + msgBox.setMinimumWidth(460); msgBox.adjustSize(); msgBox.exec(); } @@ -199,7 +200,7 @@ void PrismExternalUpdater::checkForUpdates() QMessageBox::Ok, priv->parent); auto detail_txt = tr("StdOut: %1\nStdErr: %2").arg(std_output).arg(std_error); msgBox.setDetailedText(detail_txt); - + msgBox.setMinimumWidth(460); msgBox.adjustSize(); msgBox.exec(); } @@ -293,7 +294,7 @@ void PrismExternalUpdater::offerUpdate(const QString& version_name, const QStrin if (should_skip) { auto msgBox = QMessageBox(QMessageBox::Information, tr("No Update Available"), tr("There are no new updates available."), QMessageBox::Ok, priv->parent); - + msgBox.setMinimumWidth(460); msgBox.adjustSize(); msgBox.exec(); return; diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 1859d5262..91cc87ec8 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -522,6 +522,7 @@ void PrismUpdaterApp::showFatalErrorMessage(const QString& title, const QString& msgBox->setDefaultButton(QMessageBox::Ok); msgBox->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextBrowserInteraction); msgBox->setIcon(QMessageBox::Critical); + msgBox->setMinimumWidth(460); msgBox->adjustSize(); msgBox->exec(); exit(1); @@ -660,6 +661,7 @@ void PrismUpdaterApp::moveAndFinishUpdate(QDir target) QProgressDialog progress(tr("Backing up install at %1").arg(applicationDirPath()), "", 0, file_list.length()); progress.setCancelButton(nullptr); + progress.setMinimumWidth(400); progress.adjustSize(); progress.show(); QCoreApplication::processEvents(); @@ -963,6 +965,7 @@ void PrismUpdaterApp::performInstall(QFileInfo file) msgBox.setInformativeText(infoMsg); msgBox.setStandardButtons(QMessageBox::Ignore | QMessageBox::Cancel); msgBox.setDefaultButton(QMessageBox::Cancel); + msgBox.setMinimumWidth(460); msgBox.adjustSize(); switch (msgBox.exec()) { case QMessageBox::AcceptRole: @@ -1075,6 +1078,7 @@ void PrismUpdaterApp::backupAppDir() QProgressDialog progress(tr("Backing up install at %1").arg(applicationDirPath()), "", 0, file_list.length()); progress.setCancelButton(nullptr); + progress.setMinimumWidth(400); progress.adjustSize(); progress.show(); QCoreApplication::processEvents(); From 4320830a861d16c8fe0b1593e1ee6d022b2d1404 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Thu, 29 Jun 2023 17:59:14 -0700 Subject: [PATCH 082/268] fix(updater): Explicit conversion to string for QByteArray Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/updater/PrismExternalUpdater.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index 29d411a1e..02e3a3dec 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -169,7 +169,7 @@ void PrismExternalUpdater::checkForUpdates() qDebug() << "Updater subprocess error" << qPrintable(std_error); auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update Check Error"), tr("There was an error running the update check."), QMessageBox::Ok, priv->parent); - msgBox.setDetailedText(std_error); + msgBox.setDetailedText(QString(std_error)); msgBox.setMinimumWidth(460); msgBox.adjustSize(); msgBox.exec(); @@ -198,7 +198,7 @@ void PrismExternalUpdater::checkForUpdates() QMessageBox(QMessageBox::Information, tr("Unknown Update Error"), tr("The updater exited with an unknown condition.\nExit Code: %1").arg(QString::number(exit_code)), QMessageBox::Ok, priv->parent); - auto detail_txt = tr("StdOut: %1\nStdErr: %2").arg(std_output).arg(std_error); + auto detail_txt = tr("StdOut: %1\nStdErr: %2").arg(QString(std_output)).arg(QString(std_error)); msgBox.setDetailedText(detail_txt); msgBox.setMinimumWidth(460); msgBox.adjustSize(); From 3db83a189c4c52437bbab94f80d8e0d663d21e75 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Thu, 29 Jun 2023 22:38:16 -0700 Subject: [PATCH 083/268] fix(updater): include updater in setup.exe Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- program_info/win_install.nsi.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/program_info/win_install.nsi.in b/program_info/win_install.nsi.in index d3b5c256f..1e4c29ad7 100644 --- a/program_info/win_install.nsi.in +++ b/program_info/win_install.nsi.in @@ -350,6 +350,7 @@ Section "@Launcher_DisplayName@" File "@Launcher_APP_BINARY_NAME@.exe" File "@Launcher_APP_BINARY_NAME@_filelink.exe" + File "@Launcher_APP_BINARY_NAME@_updater.exe" File "qt.conf" File "qtlogging.ini" File *.dll @@ -431,6 +432,7 @@ Section "Uninstall" Delete $INSTDIR\@Launcher_APP_BINARY_NAME@.exe Delete $INSTDIR\@Launcher_APP_BINARY_NAME@_filelink.exe + Delete $INSTDIR\@Launcher_APP_BINARY_NAME@_updater.exe Delete $INSTDIR\qt.conf Delete $INSTDIR\*.dll From 8dd3a02747ab49a205903b362bf3519ce79205bd Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Thu, 29 Jun 2023 23:49:31 -0700 Subject: [PATCH 084/268] Update launcher/Application.cpp Co-authored-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 1993a1491..e98cc0081 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -585,7 +585,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) qDebug() << "Compiled for : " << BuildConfig.systemID(); qDebug() << "Compiled by : " << BuildConfig.compilerID(); qDebug() << "Build Artifact : " << BuildConfig.BUILD_ARTIFACT; - qDebug() << "Updates Enabeled : " << (updaterEnabled() ? "Yes" : "No"); + qDebug() << "Updates Enabled : " << (updaterEnabled() ? "Yes" : "No"); if (adjustedBy.size()) { qDebug() << "Work dir before adjustment : " << origcwdPath; qDebug() << "Work dir after adjustment : " << QDir::currentPath(); From 00f75e2d5425edec250134de96f83e0a53cd6500 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 30 Jun 2023 00:54:29 -0700 Subject: [PATCH 085/268] fix(updater): avoid windows installer detection with "asInvoker" in xml exe.manifest Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/CMakeLists.txt | 1 + .../updater/prismupdater/updater.exe.manifest | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 launcher/updater/prismupdater/updater.exe.manifest diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 4b0e98f83..da4d66b34 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -1285,6 +1285,7 @@ if(NOT APPLE OR (DEFINED Launcher_BUILD_UPDATER AND Launcher_BUILD_UPDATER)) ) add_executable("${Launcher_Name}_updater" WIN32 updater/prismupdater/updater_main.cpp) + target_sources("${Launcher_Name}_updater" PRIVATE updater/prismupdater/updater.exe.manifest) target_link_libraries("${Launcher_Name}_updater" prism_updater_logic) if(DEFINED Launcher_APP_BINARY_NAME) diff --git a/launcher/updater/prismupdater/updater.exe.manifest b/launcher/updater/prismupdater/updater.exe.manifest new file mode 100644 index 000000000..2bce76b77 --- /dev/null +++ b/launcher/updater/prismupdater/updater.exe.manifest @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + From a01a48793c6670743452038f9851c533c1734b0a Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 30 Jun 2023 03:00:26 -0700 Subject: [PATCH 086/268] fix(updater): force `asInvoker` for updater on windows - use `__COMPAT_LAYER` env var in windows to bypass Installer Detection and run as a normal user anyway - set up updater directly after main windows is created - check update locks before main window Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/Application.cpp | 35 ++++++++++--------- launcher/updater/PrismExternalUpdater.cpp | 8 +++++ .../updater/prismupdater/PrismUpdater.cpp | 19 +++++++++- 3 files changed, 44 insertions(+), 18 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index e98cc0081..c1d78c5da 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -959,23 +959,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) updateCapabilities(); - if (createSetupWizard()) { - return; - } - - performMainStartupAction(); - - // initialize the updater - if (updaterEnabled()) { - qDebug() << "Initializing updater"; -#ifdef Q_OS_MAC - m_updater.reset(new MacSparkleUpdater()); -#else - m_updater.reset(new PrismExternalUpdater(m_mainWindow, applicationDirPath(), m_dataPath)); -#endif - qDebug() << "<> Updater started."; - } - // check update locks { auto update_log_path = FS::PathCombine(m_dataPath, "logs", "prism_launcher_update.log"); @@ -1076,6 +1059,12 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) FS::deletePath(update_success_marker.absoluteFilePath()); } } + + if (createSetupWizard()) { + return; + } + + performMainStartupAction(); } bool Application::createSetupWizard() @@ -1219,6 +1208,18 @@ void Application::performMainStartupAction() showMainWindow(false); qDebug() << "<> Main window shown."; } + + // initialize the updater + if (updaterEnabled()) { + qDebug() << "Initializing updater"; +#ifdef Q_OS_MAC + m_updater.reset(new MacSparkleUpdater()); +#else + m_updater.reset(new PrismExternalUpdater(m_mainWindow, applicationDirPath(), m_dataPath)); +#endif + qDebug() << "<> Updater started."; + } + if (!m_zipsToImport.isEmpty()) { qDebug() << "<> Importing from zip:" << m_zipsToImport; m_mainWindow->processURLs(m_zipsToImport); diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index 02e3a3dec..8de2a8119 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -96,6 +96,10 @@ void PrismExternalUpdater::checkForUpdates() auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); #if defined Q_OS_WIN32 exe_name.append(".exe"); + + auto env = QProcessEnvironment::systemEnvironment(); + env.insert("__COMPAT_LAYER", "RUNASINVOKER"); + proc.setProcessEnvironment(env); #endif QStringList args = { "--check-only", "--dir", priv->dataDir.absolutePath(), "--debug" }; @@ -328,6 +332,10 @@ void PrismExternalUpdater::performUpdate(const QString& version_tag) auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); #if defined Q_OS_WIN32 exe_name.append(".exe"); + + auto env = QProcessEnvironment::systemEnvironment(); + env.insert("__COMPAT_LAYER", "RUNASINVOKER"); + proc.setProcessEnvironment(env); #endif QStringList args = { "--dir", priv->dataDir.absolutePath(), "--install-version", version_tag }; diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 91cc87ec8..771c6f754 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -704,7 +704,12 @@ void PrismUpdaterApp::moveAndFinishUpdate(QDir target) auto app_exe_name = BuildConfig.LAUNCHER_APP_BINARY_NAME; #if defined Q_OS_WIN32 app_exe_name.append(".exe"); + + auto env = QProcessEnvironment::systemEnvironment(); + env.insert("__COMPAT_LAYER", "RUNASINVOKER"); + proc.setProcessEnvironment(env); #endif + auto app_exe_path = target.absoluteFilePath(app_exe_name); proc.startDetached(app_exe_path); @@ -990,6 +995,11 @@ void PrismUpdaterApp::performInstall(QFileInfo file) } else { logUpdate(tr("Running installer file at %1").arg(file.absoluteFilePath())); QProcess proc = QProcess(); +#if defined Q_OS_WIN + auto env = QProcessEnvironment::systemEnvironment(); + env.insert("__COMPAT_LAYER", "RUNASINVOKER"); + proc.setProcessEnvironment(env); +#endif proc.setProgram(file.absoluteFilePath()); bool result = proc.startDetached(); logUpdate(tr("Process start result: %1").arg(result ? tr("yes") : tr("no"))); @@ -1005,13 +1015,20 @@ void PrismUpdaterApp::unpackAndInstall(QFileInfo archive) if (auto loc = unpackArchive(archive)) { auto marker_file_path = loc.value().absoluteFilePath(".prism_launcher_updater_unpack.marker"); FS::write(marker_file_path, applicationDirPath().toUtf8()); + + QProcess proc = QProcess(); + auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); #if defined Q_OS_WIN32 exe_name.append(".exe"); + + auto env = QProcessEnvironment::systemEnvironment(); + env.insert("__COMPAT_LAYER", "RUNASINVOKER"); + proc.setProcessEnvironment(env); #endif + auto new_updater_path = loc.value().absoluteFilePath(exe_name); logUpdate(tr("Starting new updater at '%1'").arg(new_updater_path)); - QProcess proc = QProcess(); if (!proc.startDetached(new_updater_path, { "-d", m_dataPath }, loc.value().absolutePath())) { logUpdate(tr("Failed to launch '%1' %2").arg(new_updater_path).arg(proc.errorString())); return exit(10); From 6476023cf7041340c4e05b020e5c08065578b2d1 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 30 Jun 2023 10:57:53 -0700 Subject: [PATCH 087/268] fix(updater): check the app root dir for binary Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index c1d78c5da..69cd34abc 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -1126,7 +1126,7 @@ bool Application::updaterEnabled() #if defined(Q_OS_MAC) return BuildConfig.UPDATER_ENABLED; #else - return BuildConfig.UPDATER_ENABLED && QFileInfo(updaterBinaryName()).isFile(); + return BuildConfig.UPDATER_ENABLED && QFileInfo(FS::PathCombine(applicationDirPath(), updaterBinaryName())).isFile(); #endif } From cd527c44a431e337b58e6888225842b9eb99ff12 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 30 Jun 2023 18:17:06 -0700 Subject: [PATCH 088/268] fix(updater): build atrifact fix on linux ci + add qt-ver to artifact name Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/build.yml | 6 ++-- launcher/StringUtils.cpp | 20 ++++++----- .../updater/prismupdater/PrismUpdater.cpp | 33 ++++++++++++++----- 3 files changed, 40 insertions(+), 19 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5a1554fc6..de908d335 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -283,12 +283,12 @@ 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=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -DLauncher_BUILD_ARTIFACT=${{ matrix.name }} -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 -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }} -G Ninja - 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}} -DLauncher_FORCE_BUNDLED_LIBS=ON -DLauncher_BUILD_ARTIFACT=${{ matrix.name }} + 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 -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }} # https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix) if ("${{ env.CCACHE_VAR }}") { @@ -303,7 +303,7 @@ jobs: - name: Configure CMake (Linux) if: runner.os == 'Linux' run: | - cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=Linux -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DLauncher_BUILD_ARTIFACT=${{ matrix.name }} -G Ninja + cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=Linux -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DLauncher_BUILD_ARTIFACT=Linux-Qt${{ matrix.qt_ver }} -G Ninja ## # BUILD diff --git a/launcher/StringUtils.cpp b/launcher/StringUtils.cpp index b54299721..72ccdfbff 100644 --- a/launcher/StringUtils.cpp +++ b/launcher/StringUtils.cpp @@ -184,27 +184,31 @@ QString StringUtils::getRandomAlphaNumeric() return QUuid::createUuid().toString(QUuid::Id128); } -QPair StringUtils::splitFirst(const QString& s, const QString& sep, Qt::CaseSensitivity cs) { +QPair StringUtils::splitFirst(const QString& s, const QString& sep, Qt::CaseSensitivity cs) +{ QString left, right; auto index = s.indexOf(sep, 0, cs); left = s.mid(0, index); - right = s.mid(index + 1); + right = s.mid(index + sep.length()); return qMakePair(left, right); } -QPair StringUtils::splitFirst(const QString& s, QChar sep, Qt::CaseSensitivity cs) { +QPair StringUtils::splitFirst(const QString& s, QChar sep, Qt::CaseSensitivity cs) +{ QString left, right; auto index = s.indexOf(sep, 0, cs); left = s.mid(0, index); - right = s.mid(index + 1); + right = s.mid(left.length() + 1); return qMakePair(left, right); } -QPair StringUtils::splitFirst(const QString& s, const QRegularExpression& re) { +QPair StringUtils::splitFirst(const QString& s, const QRegularExpression& re) +{ QString left, right; - auto index = s.indexOf(re); + QRegularExpressionMatch match; + auto index = s.indexOf(re, 0, &match); left = s.mid(0, index); - right = s.mid(index + 1); + auto end = match.hasMatch() ? left.length() + match.capturedLength() : left.length() + 1; + right = s.mid(end); return qMakePair(left, right); } - diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 771c6f754..3683286d8 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -784,23 +784,41 @@ QList PrismUpdaterApp::validReleaseArtifacts(const GitHubRel continue; } auto asset_name = asset.name.toLower(); - auto platform = BuildConfig.BUILD_ARTIFACT.toLower(); + auto [platform, platform_qt_ver] = StringUtils::splitFirst(BuildConfig.BUILD_ARTIFACT.toLower(), "-qt"); auto system_is_arm = QSysInfo::buildCpuArchitecture().contains("arm64"); auto asset_is_arm = asset_name.contains("arm64"); auto asset_is_archive = asset_name.endsWith(".zip") || asset_name.endsWith(".tar.gz"); bool for_platform = !platform.isEmpty() && asset_name.contains(platform); + if (!for_platform) { + qDebug() << "Rejecting" << asset.name << "because platforms do not match"; + } bool for_portable = asset_name.contains("portable"); - if (for_platform && asset_name.contains("legacy") && !platform.contains("legacy")) + if (for_platform && asset_name.contains("legacy") && !platform.contains("legacy")) { + qDebug() << "Rejecting" << asset.name << "because platforms do not match"; for_platform = false; - if (for_platform && ((asset_is_arm && !system_is_arm) || (!asset_is_arm && system_is_arm))) + } + if (for_platform && ((asset_is_arm && !system_is_arm) || (!asset_is_arm && system_is_arm))) { + qDebug() << "Rejecting" << asset.name << "because architecture does not match"; for_platform = false; - if (for_platform && platform.contains("windows") && !m_isPortable && asset_is_archive) + } + if (for_platform && platform.contains("windows") && !m_isPortable && asset_is_archive) { + qDebug() << "Rejecting" << asset.name << "because it is not an installer"; for_platform = false; + } + + auto qt_pattern = QRegularExpression("-qt(\\d+)"); + auto qt_match = qt_pattern.match(asset_name); + if (for_platform && qt_match.hasMatch()) { + if (platform_qt_ver.isEmpty() || platform_qt_ver.toInt() != qt_match.captured(1).toInt()) { + qDebug() << "Rejecting" << asset.name << "because it is not for the correct qt version" << platform_qt_ver.toInt() << "vs" + << qt_match.captured(1).toInt(); + for_platform = false; + } + } if (((m_isPortable && for_portable) || (!m_isPortable && !for_portable)) && for_platform) { - qDebug() << "Rejecting" << asset.name << "|" - << "For Platform:" << (for_platform ? "Yes" : "No") << "For Portable:" << (for_portable ? "Yes" : "No"); + qDebug() << "Accepting" << asset.name; valid.append(asset); } } @@ -866,8 +884,7 @@ QFileInfo PrismUpdaterApp::downloadAsset(const GitHubReleaseAsset& asset) auto progress_dialog = ProgressDialog(); progress_dialog.adjustSize(); - if (progress_dialog.execWithTask(download.get()) == QDialog::Rejected) - showFatalErrorMessage(tr("Download Aborted"), tr("Download of %1 aborted by user").arg(file_url.toString())); + progress_dialog.execWithTask(download.get()); qDebug() << "download complete"; From 1fd90e9dd03aa087fd7b8fa2178018812466d80e Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 30 Jun 2023 21:24:30 -0700 Subject: [PATCH 089/268] fix(updater): ensure updater knows binaries are in `./bin/` on linux and can find them Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/Application.cpp | 6 ++- launcher/updater/PrismExternalUpdater.cpp | 12 +++-- .../updater/prismupdater/PrismUpdater.cpp | 48 +++++++++++-------- 3 files changed, 40 insertions(+), 26 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 69cd34abc..7eec45bfd 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -1126,7 +1126,7 @@ bool Application::updaterEnabled() #if defined(Q_OS_MAC) return BuildConfig.UPDATER_ENABLED; #else - return BuildConfig.UPDATER_ENABLED && QFileInfo(FS::PathCombine(applicationDirPath(), updaterBinaryName())).isFile(); + return BuildConfig.UPDATER_ENABLED && QFileInfo(FS::PathCombine(m_rootPath, updaterBinaryName())).isFile(); #endif } @@ -1135,6 +1135,8 @@ QString Application::updaterBinaryName() auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); #if defined Q_OS_WIN32 exe_name.append(".exe"); +#else + exe_name.prepend("bin/"); #endif return exe_name; } @@ -1215,7 +1217,7 @@ void Application::performMainStartupAction() #ifdef Q_OS_MAC m_updater.reset(new MacSparkleUpdater()); #else - m_updater.reset(new PrismExternalUpdater(m_mainWindow, applicationDirPath(), m_dataPath)); + m_updater.reset(new PrismExternalUpdater(m_mainWindow, m_rootPath, m_dataPath)); #endif qDebug() << "<> Updater started."; } diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index 8de2a8119..3595042d6 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -40,7 +40,7 @@ class PrismExternalUpdater::Private { public: - QDir binDir; + QDir appDir; QDir dataDir; QTimer updateTimer; bool allowBeta; @@ -52,10 +52,10 @@ class PrismExternalUpdater::Private { QWidget* parent; }; -PrismExternalUpdater::PrismExternalUpdater(QWidget* parent, const QString& binDir, const QString& dataDir) +PrismExternalUpdater::PrismExternalUpdater(QWidget* parent, const QString& appDir, const QString& dataDir) { priv = new PrismExternalUpdater::Private(); - priv->binDir = QDir(binDir); + priv->appDir = QDir(appDir); priv->dataDir = QDir(dataDir); auto settings_file = priv->dataDir.absoluteFilePath("prismlauncher_update.cfg"); priv->settings = std::make_unique(settings_file, QSettings::Format::IniFormat); @@ -100,13 +100,15 @@ void PrismExternalUpdater::checkForUpdates() auto env = QProcessEnvironment::systemEnvironment(); env.insert("__COMPAT_LAYER", "RUNASINVOKER"); proc.setProcessEnvironment(env); +#else + exe_name = QString("bin/%1").arg(exe_name); #endif QStringList args = { "--check-only", "--dir", priv->dataDir.absolutePath(), "--debug" }; if (priv->allowBeta) args.append("--pre-release"); - proc.start(priv->binDir.absoluteFilePath(exe_name), args); + proc.start(priv->appDir.absoluteFilePath(exe_name), args); auto result_start = proc.waitForStarted(5000); if (!result_start) { auto err = proc.error(); @@ -342,7 +344,7 @@ void PrismExternalUpdater::performUpdate(const QString& version_tag) if (priv->allowBeta) args.append("--pre-release"); - auto result = proc.startDetached(priv->binDir.absoluteFilePath(exe_name), args); + auto result = proc.startDetached(priv->appDir.absoluteFilePath(exe_name), args); if (!result) { qDebug() << "Failed to start updater:" << proc.error() << proc.errorString(); } diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 3683286d8..8af65d3c6 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -258,9 +258,9 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar m_isFlatpak = DesktopServices::isFlatpak(); - QString prism_executable = QCoreApplication::applicationDirPath() + "/" + BuildConfig.LAUNCHER_APP_BINARY_NAME; + QString prism_executable = FS::PathCombine(applicationDirPath(), BuildConfig.LAUNCHER_APP_BINARY_NAME); #if defined Q_OS_WIN32 - prism_executable += ".exe"; + prism_executable.append(".exe"); #endif if (!QFileInfo(prism_executable).isFile()) { @@ -349,6 +349,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar m_updateLogPath = FS::PathCombine(m_dataPath, "logs", "prism_launcher_update.log"); { // setup logging + FS::ensureFolderPathExists(FS::PathCombine(m_dataPath, "logs")); static const QString baseLogFile = BuildConfig.LAUNCHER_NAME + "Updater" + (m_checkOnly ? "-CheckOnly" : "") + "-%0.log"; static const QString logBase = FS::PathCombine(m_dataPath, "logs", baseLogFile); auto moveFile = [](const QString& oldName, const QString& newName) { @@ -464,13 +465,13 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar m_network->setProxy(proxy); } - auto marker_file_path = QDir(applicationDirPath()).absoluteFilePath(".prism_launcher_updater_unpack.marker"); + auto marker_file_path = QDir(m_rootPath).absoluteFilePath(".prism_launcher_updater_unpack.marker"); auto marker_file = QFileInfo(marker_file_path); if (marker_file.exists()) { auto target_dir = QString(FS::read(marker_file_path)).trimmed(); if (target_dir.isEmpty()) { qWarning() << "Empty updater marker file contains no install target. making best guess of parent dir"; - target_dir = QDir(applicationDirPath()).absoluteFilePath(".."); + target_dir = QDir(m_rootPath).absoluteFilePath(".."); } QMetaObject::invokeMethod( @@ -629,10 +630,10 @@ void PrismUpdaterApp::moveAndFinishUpdate(QDir target) logUpdate("Waiting 2 seconds for resources to free"); this->thread()->sleep(2); - auto manifest_path = FS::PathCombine(applicationDirPath(), "manifest.txt"); + auto manifest_path = FS::PathCombine(m_rootPath, "manifest.txt"); QFileInfo manifest(manifest_path); - auto app_dir = QDir(applicationDirPath()); + auto app_dir = QDir(m_rootPath); QStringList file_list; if (manifest.isFile()) { @@ -649,7 +650,7 @@ void PrismUpdaterApp::moveAndFinishUpdate(QDir target) } if (file_list.isEmpty()) { - logUpdate(tr("Manifest empty, making best guess of the directory contents of %1").arg(applicationDirPath())); + logUpdate(tr("Manifest empty, making best guess of the directory contents of %1").arg(m_rootPath)); auto entries = target.entryInfoList(QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); for (auto entry : entries) { file_list.append(entry.fileName()); @@ -659,7 +660,7 @@ void PrismUpdaterApp::moveAndFinishUpdate(QDir target) bool error = false; - QProgressDialog progress(tr("Backing up install at %1").arg(applicationDirPath()), "", 0, file_list.length()); + QProgressDialog progress(tr("Backing up install at %1").arg(m_rootPath), "", 0, file_list.length()); progress.setCancelButton(nullptr); progress.setMinimumWidth(400); progress.adjustSize(); @@ -668,7 +669,7 @@ void PrismUpdaterApp::moveAndFinishUpdate(QDir target) int i = 0; for (auto glob : file_list) { - QDirIterator iter(applicationDirPath(), QStringList({ glob }), QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); + QDirIterator iter(m_rootPath, QStringList({ glob }), QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); progress.setValue(i); QCoreApplication::processEvents(); while (iter.hasNext()) { @@ -708,6 +709,8 @@ void PrismUpdaterApp::moveAndFinishUpdate(QDir target) auto env = QProcessEnvironment::systemEnvironment(); env.insert("__COMPAT_LAYER", "RUNASINVOKER"); proc.setProcessEnvironment(env); +#else + exe_name.prepend("bin/"); #endif auto app_exe_path = target.absoluteFilePath(app_exe_name); @@ -894,10 +897,15 @@ QFileInfo PrismUpdaterApp::downloadAsset(const GitHubReleaseAsset& asset) bool PrismUpdaterApp::callAppImageUpdate() { + auto appimage_path = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE")); QProcess proc = QProcess(); + qDebug() << "Calling: AppImageUpdate" << appimage_path; proc.setProgram("AppImageUpdate"); - proc.setArguments({ QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE")) }); - return proc.startDetached(); + proc.setArguments({ appimage_path }); + auto result = proc.startDetached(); + if (!result) + qDebug() << "Failed to start AppImageUpdate reason:" << proc.errorString(); + return result; } void PrismUpdaterApp::clearUpdateLog() @@ -1005,9 +1013,8 @@ void PrismUpdaterApp::performInstall(QFileInfo file) logUpdate(tr("Updating from %1 to %2").arg(m_prismVersion).arg(m_install_release.tag_name)); if (m_isPortable || file.suffix().toLower() == "zip") { - write_lock_file(update_lock_path, QDateTime::currentDateTime(), m_prismVersion, m_install_release.tag_name, applicationDirPath(), - m_dataPath); - logUpdate(tr("Updating portable install at %1").arg(applicationDirPath())); + write_lock_file(update_lock_path, QDateTime::currentDateTime(), m_prismVersion, m_install_release.tag_name, m_rootPath, m_dataPath); + logUpdate(tr("Updating portable install at %1").arg(m_rootPath)); unpackAndInstall(file); } else { logUpdate(tr("Running installer file at %1").arg(file.absoluteFilePath())); @@ -1016,6 +1023,8 @@ void PrismUpdaterApp::performInstall(QFileInfo file) auto env = QProcessEnvironment::systemEnvironment(); env.insert("__COMPAT_LAYER", "RUNASINVOKER"); proc.setProcessEnvironment(env); +#else + exe_name.prepend("bin/"); #endif proc.setProgram(file.absoluteFilePath()); bool result = proc.startDetached(); @@ -1031,7 +1040,7 @@ void PrismUpdaterApp::unpackAndInstall(QFileInfo archive) if (auto loc = unpackArchive(archive)) { auto marker_file_path = loc.value().absoluteFilePath(".prism_launcher_updater_unpack.marker"); - FS::write(marker_file_path, applicationDirPath().toUtf8()); + FS::write(marker_file_path, m_rootPath.toUtf8()); QProcess proc = QProcess(); @@ -1042,6 +1051,8 @@ void PrismUpdaterApp::unpackAndInstall(QFileInfo archive) auto env = QProcessEnvironment::systemEnvironment(); env.insert("__COMPAT_LAYER", "RUNASINVOKER"); proc.setProcessEnvironment(env); +#else + exe_name.prepend("bin/"); #endif auto new_updater_path = loc.value().absoluteFilePath(exe_name); @@ -1057,7 +1068,7 @@ void PrismUpdaterApp::unpackAndInstall(QFileInfo archive) void PrismUpdaterApp::backupAppDir() { - auto manifest_path = FS::PathCombine(applicationDirPath(), "manifest.txt"); + auto manifest_path = FS::PathCombine(m_rootPath, "manifest.txt"); QFileInfo manifest(manifest_path); QStringList file_list; @@ -1099,8 +1110,7 @@ void PrismUpdaterApp::backupAppDir() logUpdate("manifest.txt empty or missing. making best guess at files to back up."); } logUpdate(tr("Backing up:\n %1").arg(file_list.join(",\n "))); - - QDir app_dir = QCoreApplication::applicationDirPath(); + auto app_dir = QDir(m_rootPath); auto backup_dir = FS::PathCombine( app_dir.absolutePath(), QStringLiteral("backup_") + @@ -1110,7 +1120,7 @@ void PrismUpdaterApp::backupAppDir() auto backup_marker_path = FS::PathCombine(m_dataPath, ".prism_launcher_update_backup_path.txt"); FS::write(backup_marker_path, backup_dir.toUtf8()); - QProgressDialog progress(tr("Backing up install at %1").arg(applicationDirPath()), "", 0, file_list.length()); + QProgressDialog progress(tr("Backing up install at %1").arg(m_rootPath), "", 0, file_list.length()); progress.setCancelButton(nullptr); progress.setMinimumWidth(400); progress.adjustSize(); From cea285f5f5b678bed0786ff78586e2e921a77392 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 30 Jun 2023 21:33:22 -0700 Subject: [PATCH 090/268] fix(updater): fix bad exe_name on linux Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/updater/prismupdater/PrismUpdater.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 8af65d3c6..2604d2e0b 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -710,7 +710,7 @@ void PrismUpdaterApp::moveAndFinishUpdate(QDir target) env.insert("__COMPAT_LAYER", "RUNASINVOKER"); proc.setProcessEnvironment(env); #else - exe_name.prepend("bin/"); + app_exe_name.prepend("bin/"); #endif auto app_exe_path = target.absoluteFilePath(app_exe_name); @@ -1023,8 +1023,6 @@ void PrismUpdaterApp::performInstall(QFileInfo file) auto env = QProcessEnvironment::systemEnvironment(); env.insert("__COMPAT_LAYER", "RUNASINVOKER"); proc.setProcessEnvironment(env); -#else - exe_name.prepend("bin/"); #endif proc.setProgram(file.absoluteFilePath()); bool result = proc.startDetached(); From b67844e74c1125bde0111606b06aee5a4686c94b Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 30 Jun 2023 23:04:55 -0700 Subject: [PATCH 091/268] fix(windows installer): old installers didn't uninstall first. now they do so ensure shortcuts stay selected by default Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- program_info/win_install.nsi.in | 1 - 1 file changed, 1 deletion(-) diff --git a/program_info/win_install.nsi.in b/program_info/win_install.nsi.in index 1e4c29ad7..fa8615e94 100644 --- a/program_info/win_install.nsi.in +++ b/program_info/win_install.nsi.in @@ -470,7 +470,6 @@ Function .onInit ${GetParameters} $R0 ${GetOptions} $R0 "/NoShortcuts" $R1 ${IfNot} ${Errors} -${OrIf} ${FileExists} "$InstDir\@Launcher_APP_BINARY_NAME@.exe" !insertmacro UnselectSection ${SM_SHORTCUTS} !insertmacro UnselectSection ${DESKTOP_SHORTCUTS} ${EndIf} From 22327bbe71b271a126521db737101ae07ba26f8d Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Thu, 6 Jul 2023 18:04:44 +0100 Subject: [PATCH 092/268] Combine launch buttons in Instance window, persist profiler Signed-off-by: TheKodeToad --- launcher/Application.cpp | 21 ++-- launcher/Application.h | 13 +-- launcher/BaseInstance.cpp | 5 +- launcher/BaseInstance.h | 14 ++- launcher/LaunchController.cpp | 10 +- launcher/NullInstance.h | 6 +- launcher/minecraft/MinecraftInstance.cpp | 50 ++++++++- launcher/minecraft/MinecraftInstance.h | 4 +- launcher/ui/InstanceWindow.cpp | 115 ++++++-------------- launcher/ui/InstanceWindow.h | 16 ++- launcher/ui/MainWindow.cpp | 119 ++------------------- launcher/ui/MainWindow.h | 8 +- launcher/ui/MainWindow.ui | 22 +--- launcher/ui/pages/instance/ServersPage.cpp | 4 +- 14 files changed, 143 insertions(+), 264 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 1d97a5f2e..625cebd37 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -1005,7 +1005,7 @@ void Application::performMainStartupAction() qDebug() << " Launching with account" << m_profileToUse; } - launch(inst, true, false, nullptr, serverToJoin, accountToUse); + launch(inst, true, false, serverToJoin, accountToUse); return; } } @@ -1120,7 +1120,6 @@ void Application::messageReceived(const QByteArray& message) instance, true, false, - nullptr, serverObject, accountObject ); @@ -1187,14 +1186,12 @@ bool Application::openJsonEditor(const QString &filename) } } -bool Application::launch( - InstancePtr instance, - bool online, - bool demo, - BaseProfilerFactory *profiler, - MinecraftServerTargetPtr serverToJoin, - MinecraftAccountPtr accountToUse -) { +bool Application::launch(InstancePtr instance, + bool online, + bool demo, + MinecraftServerTargetPtr serverToJoin, + MinecraftAccountPtr accountToUse) +{ if(m_updateRunning) { qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed."; @@ -1202,7 +1199,7 @@ bool Application::launch( else if(instance->canLaunch()) { auto & extras = m_instanceExtras[instance->id()]; - auto & window = extras.window; + auto window = extras.window; if(window) { if(!window->saveAll()) @@ -1215,7 +1212,7 @@ bool Application::launch( controller->setInstance(instance); controller->setOnline(online); controller->setDemo(demo); - controller->setProfiler(profiler); + controller->setProfiler(profilers().value(instance->settings()->get("Profiler").toString(), nullptr).get()); controller->setServerToJoin(serverToJoin); controller->setAccountToUse(accountToUse); if(window) diff --git a/launcher/Application.h b/launcher/Application.h index ced0af17d..b911edc18 100644 --- a/launcher/Application.h +++ b/launcher/Application.h @@ -222,14 +222,11 @@ signals: #endif public slots: - bool launch( - InstancePtr instance, - bool online = true, - bool demo = false, - BaseProfilerFactory *profiler = nullptr, - MinecraftServerTargetPtr serverToJoin = nullptr, - MinecraftAccountPtr accountToUse = nullptr - ); + bool launch(InstancePtr instance, + bool online = true, + bool demo = false, + MinecraftServerTargetPtr serverToJoin = nullptr, + MinecraftAccountPtr accountToUse = nullptr); bool kill(InstancePtr instance); void closeCurrentWindow(); diff --git a/launcher/BaseInstance.cpp b/launcher/BaseInstance.cpp index a8fce879c..28b4cb5c3 100644 --- a/launcher/BaseInstance.cpp +++ b/launcher/BaseInstance.cpp @@ -1,8 +1,9 @@ // 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) 2023 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 @@ -101,6 +102,8 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s m_settings->registerSetting("ManagedPackName", ""); m_settings->registerSetting("ManagedPackVersionID", ""); m_settings->registerSetting("ManagedPackVersionName", ""); + + m_settings->registerSetting("Profiler", ""); } QString BaseInstance::getPreLaunchCommand() diff --git a/launcher/BaseInstance.h b/launcher/BaseInstance.h index 83a8064fa..7da4e20b2 100644 --- a/launcher/BaseInstance.h +++ b/launcher/BaseInstance.h @@ -1,8 +1,9 @@ // 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) 2023 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 @@ -37,11 +38,12 @@ #pragma once #include -#include -#include "QObjectPtr.h" #include -#include +#include +#include #include +#include +#include "QObjectPtr.h" #include "settings/SettingsObject.h" @@ -270,6 +272,8 @@ public: virtual bool canEdit() const = 0; virtual bool canExport() const = 0; + virtual void populateLaunchMenu(QMenu* menu) = 0; + bool reloadSettings(); /** @@ -306,6 +310,8 @@ signals: void runningStatusChanged(bool running); + void profilerChanged(); + void statusChanged(Status from, Status to); protected slots: diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp index 070ee283c..5a79b2530 100644 --- a/launcher/LaunchController.cpp +++ b/launcher/LaunchController.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) 2023 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 @@ -406,22 +407,21 @@ void LaunchController::readyForLaunch() if (!m_profiler->check(&error)) { m_launcher->abort(); - QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Couldn't start profiler: %1").arg(error)); emitFailed("Profiler startup failed!"); + QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Profiler check for %1 failed: %2").arg(m_profiler->name(), error)); return; } BaseProfiler *profilerInstance = m_profiler->createProfiler(m_launcher->instance(), this); connect(profilerInstance, &BaseProfiler::readyToLaunch, [this](const QString & message) { - QMessageBox msg; + QMessageBox msg(m_parentWidget); msg.setText(tr("The game launch is delayed until you press the " "button. This is the right time to setup the profiler, as the " "profiler server is running now.\n\n%1").arg(message)); msg.setWindowTitle(tr("Waiting.")); msg.setIcon(QMessageBox::Information); - msg.addButton(tr("Launch"), QMessageBox::AcceptRole); - msg.setModal(true); + msg.addButton(tr("&Launch"), QMessageBox::AcceptRole); msg.exec(); m_launcher->proceed(); }); diff --git a/launcher/NullInstance.h b/launcher/NullInstance.h index 53edfa0b8..b0ec89f42 100644 --- a/launcher/NullInstance.h +++ b/launcher/NullInstance.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) 2023 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 @@ -110,6 +111,9 @@ public: { return false; } + void populateLaunchMenu(QMenu* menu) override + { + } QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override { QStringList out; diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index aab930de0..d511451e5 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -3,7 +3,7 @@ * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 Jamie Mansfield - * Copyright (C) 2022 TheKodeToad + * Copyright (C) 2023 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 @@ -88,6 +88,8 @@ #include "minecraft/gameoptions/GameOptions.h" #include "minecraft/update/FoldersTask.h" +#include "tools/BaseProfiler.h" + #ifdef Q_OS_LINUX #include "MangoHud.h" #endif @@ -234,6 +236,52 @@ QSet MinecraftInstance::traits() const return profile->getTraits(); } +void MinecraftInstance::populateLaunchMenu(QMenu* menu) +{ + QAction* normalLaunch = menu->addAction(tr("&Launch")); + normalLaunch->setShortcut(QKeySequence::Open); + QAction* normalLaunchOffline = menu->addAction(tr("Launch &Offline")); + normalLaunchOffline->setShortcut(QKeySequence(tr("Ctrl+Shift+O"))); + QAction* normalLaunchDemo = menu->addAction(tr("Launch &Demo")); + normalLaunchDemo->setShortcut(QKeySequence(tr("Ctrl+Alt+O"))); + + normalLaunchDemo->setEnabled(supportsDemo()); + + connect(normalLaunch, &QAction::triggered, [this] { APPLICATION->launch(shared_from_this()); }); + connect(normalLaunchOffline, &QAction::triggered, [this] { APPLICATION->launch(shared_from_this(), false, false); }); + connect(normalLaunchDemo, &QAction::triggered, [this] { APPLICATION->launch(shared_from_this(), false, true); }); + + QString profilersTitle = tr("Profilers"); + menu->addSeparator()->setText(profilersTitle); + + auto profilers = new QActionGroup(menu); + profilers->setExclusive(true); + connect(profilers, &QActionGroup::triggered, [this](QAction* action) { + settings()->set("Profiler", action->data()); + emit profilerChanged(); + }); + + QAction* noProfilerAction = menu->addAction(tr("&No Profiler")); + noProfilerAction->setData(""); + noProfilerAction->setCheckable(true); + noProfilerAction->setChecked(true); + profilers->addAction(noProfilerAction); + + for (auto profiler = APPLICATION->profilers().begin(); profiler != APPLICATION->profilers().end(); profiler++) { + QAction* profilerAction = menu->addAction(profiler.value()->name()); + profilers->addAction(profilerAction); + profilerAction->setData(profiler.key()); + profilerAction->setCheckable(true); + profilerAction->setChecked(settings()->get("Profiler").toString() == profiler.key()); + + QString error; + if (profiler.value()->check(&error)) + profilerAction->setIcon(APPLICATION->getThemedIcon("status-good")); + else + profilerAction->setIcon(APPLICATION->getThemedIcon("status-bad")); + } +} + QString MinecraftInstance::gameRoot() const { QFileInfo mcDir(FS::PathCombine(instanceRoot(), "minecraft")); diff --git a/launcher/minecraft/MinecraftInstance.h b/launcher/minecraft/MinecraftInstance.h index 068b30082..c5ebbb7cb 100644 --- a/launcher/minecraft/MinecraftInstance.h +++ b/launcher/minecraft/MinecraftInstance.h @@ -2,7 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu - * Copyright (C) 2022 TheKodeToad + * Copyright (C) 2023 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 @@ -77,6 +77,8 @@ public: return true; } + void populateLaunchMenu(QMenu* menu) override; + ////// Directories and files ////// QString jarModsDir() const; QString resourcePacksDir() const; diff --git a/launcher/ui/InstanceWindow.cpp b/launcher/ui/InstanceWindow.cpp index c62b370fd..350b19079 100644 --- a/launcher/ui/InstanceWindow.cpp +++ b/launcher/ui/InstanceWindow.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) 2023 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 @@ -43,8 +44,6 @@ #include #include -#include "ui/dialogs/CustomMessageBox.h" -#include "ui/dialogs/ProgressDialog.h" #include "ui/widgets/PageContainer.h" #include "InstancePageProvider.h" @@ -83,33 +82,36 @@ InstanceWindow::InstanceWindow(InstancePtr instance, QWidget *parent) auto btnHelp = new QPushButton(); btnHelp->setText(tr("Help")); horizontalLayout->addWidget(btnHelp); - connect(btnHelp, SIGNAL(clicked(bool)), m_container, SLOT(help())); + connect(btnHelp, &QPushButton::clicked, m_container, &PageContainer::help); auto spacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); horizontalLayout->addSpacerItem(spacer); - m_killButton = new QPushButton(); + m_launchButton = new QToolButton(this); + m_launchButton->setText(tr("&Launch")); + m_launchButton->setToolTip(tr("Launch the instance")); + m_launchButton->setPopupMode(QToolButton::MenuButtonPopup); + m_launchButton->setMinimumWidth(80); // HACK!! + horizontalLayout->addWidget(m_launchButton); + connect(m_launchButton, &QPushButton::clicked, this, [this] { APPLICATION->launch(m_instance); }); + + m_killButton = new QPushButton(this); + m_killButton->setText(tr("&Kill")); + m_killButton->setToolTip(tr("Kill the running instance")); horizontalLayout->addWidget(m_killButton); - connect(m_killButton, SIGNAL(clicked(bool)), SLOT(on_btnKillMinecraft_clicked())); + connect(m_killButton, &QPushButton::clicked, this, [this] { APPLICATION->kill(m_instance); }); - m_launchOfflineButton = new QPushButton(); - horizontalLayout->addWidget(m_launchOfflineButton); - m_launchOfflineButton->setText(tr("Launch Offline")); - - m_launchDemoButton = new QPushButton(); - horizontalLayout->addWidget(m_launchDemoButton); - m_launchDemoButton->setText(tr("Launch Demo")); - - updateLaunchButtons(); - connect(m_launchOfflineButton, SIGNAL(clicked(bool)), SLOT(on_btnLaunchMinecraftOffline_clicked())); - connect(m_launchDemoButton, SIGNAL(clicked(bool)), SLOT(on_btnLaunchMinecraftDemo_clicked())); + updateButtons(); m_closeButton = new QPushButton(); m_closeButton->setText(tr("Close")); horizontalLayout->addWidget(m_closeButton); - connect(m_closeButton, SIGNAL(clicked(bool)), SLOT(on_closeButton_clicked())); + connect(m_closeButton, &QPushButton::clicked, this, &QMainWindow::close); m_container->addButtons(horizontalLayout); + + connect(m_instance.get(), &BaseInstance::profilerChanged, this, &InstanceWindow::updateButtons); + connect(APPLICATION, &Application::globalSettingsClosed, this, &InstanceWindow::updateButtons); } // restore window state @@ -150,52 +152,18 @@ void InstanceWindow::on_instanceStatusChanged(BaseInstance::Status, BaseInstance } } -void InstanceWindow::updateLaunchButtons() +void InstanceWindow::updateButtons() { - if(m_instance->isRunning()) - { - m_launchOfflineButton->setEnabled(false); - m_launchDemoButton->setEnabled(false); - m_killButton->setText(tr("Kill")); - m_killButton->setObjectName("killButton"); - m_killButton->setToolTip(tr("Kill the running instance")); - } - else if(!m_instance->canLaunch()) - { - m_launchOfflineButton->setEnabled(false); - m_launchDemoButton->setEnabled(false); - m_killButton->setText(tr("Launch")); - m_killButton->setObjectName("launchButton"); - m_killButton->setToolTip(tr("Launch the instance")); - m_killButton->setEnabled(false); - } + m_launchButton->setEnabled(m_instance->canLaunch()); + m_killButton->setEnabled(m_instance->isRunning()); + + QMenu *launchMenu = m_launchButton->menu(); + if (launchMenu) + launchMenu->clear(); else - { - m_launchOfflineButton->setEnabled(true); - - // Disable demo-mode if not available. - auto instance = dynamic_cast(m_instance.get()); - if (instance) { - m_launchDemoButton->setEnabled(instance->supportsDemo()); - } - - m_killButton->setText(tr("Launch")); - m_killButton->setObjectName("launchButton"); - m_killButton->setToolTip(tr("Launch the instance")); - } - // NOTE: this is a hack to force the button to recalculate its style - m_killButton->setStyleSheet("/* */"); - m_killButton->setStyleSheet(QString()); -} - -void InstanceWindow::on_btnLaunchMinecraftOffline_clicked() -{ - APPLICATION->launch(m_instance, false, false, nullptr); -} - -void InstanceWindow::on_btnLaunchMinecraftDemo_clicked() -{ - APPLICATION->launch(m_instance, false, true, nullptr); + launchMenu = new QMenu(this); + m_instance->populateLaunchMenu(launchMenu); + m_launchButton->setMenu(launchMenu); } void InstanceWindow::instanceLaunchTaskChanged(shared_qobject_ptr proc) @@ -205,18 +173,13 @@ void InstanceWindow::instanceLaunchTaskChanged(shared_qobject_ptr pr void InstanceWindow::runningStateChanged(bool running) { - updateLaunchButtons(); + updateButtons(); m_container->refreshContainer(); if(running) { selectPage("log"); } } -void InstanceWindow::on_closeButton_clicked() -{ - close(); -} - void InstanceWindow::closeEvent(QCloseEvent *event) { bool proceed = true; @@ -241,18 +204,6 @@ bool InstanceWindow::saveAll() return m_container->saveAll(); } -void InstanceWindow::on_btnKillMinecraft_clicked() -{ - if(m_instance->isRunning()) - { - APPLICATION->kill(m_instance); - } - else - { - APPLICATION->launch(m_instance, true, false, nullptr); - } -} - QString InstanceWindow::instanceId() { return m_instance->id(); @@ -268,10 +219,6 @@ void InstanceWindow::refreshContainer() m_container->refreshContainer(); } -InstanceWindow::~InstanceWindow() -{ -} - bool InstanceWindow::requestClose() { if(m_container->prepareToClose()) diff --git a/launcher/ui/InstanceWindow.h b/launcher/ui/InstanceWindow.h index 554c4c740..64d9a45ef 100644 --- a/launcher/ui/InstanceWindow.h +++ b/launcher/ui/InstanceWindow.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) 2023 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 @@ -37,6 +38,7 @@ #include #include +#include #include "LaunchController.h" #include "launch/LaunchTask.h" @@ -53,7 +55,7 @@ class InstanceWindow : public QMainWindow, public BasePageContainer public: explicit InstanceWindow(InstancePtr proc, QWidget *parent = 0); - virtual ~InstanceWindow(); + virtual ~InstanceWindow() = default; bool selectPage(QString pageId) override; void refreshContainer() override; @@ -71,11 +73,6 @@ signals: private slots: - void on_closeButton_clicked(); - void on_btnKillMinecraft_clicked(); - void on_btnLaunchMinecraftOffline_clicked(); - void on_btnLaunchMinecraftDemo_clicked(); - void instanceLaunchTaskChanged(shared_qobject_ptr proc); void runningStateChanged(bool running); void on_instanceStatusChanged(BaseInstance::Status, BaseInstance::Status newStatus); @@ -84,7 +81,7 @@ protected: void closeEvent(QCloseEvent *) override; private: - void updateLaunchButtons(); + void updateButtons(); private: shared_qobject_ptr m_proc; @@ -92,7 +89,6 @@ private: bool m_doNotSave = false; PageContainer *m_container = nullptr; QPushButton *m_closeButton = nullptr; + QToolButton *m_launchButton = nullptr; QPushButton *m_killButton = nullptr; - QPushButton *m_launchOfflineButton = nullptr; - QPushButton *m_launchDemoButton = nullptr; }; diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 515abf070..a2137dd3f 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -572,93 +572,14 @@ void MainWindow::updateMainToolBar() ui->mainToolBar->setVisible(ui->menuBar->isNativeMenuBar() || !APPLICATION->settings()->get("MenuBarInsteadOfToolBar").toBool()); } -void MainWindow::updateToolsMenu() +void MainWindow::updateLaunchButton() { - bool currentInstanceRunning = m_selectedInstance && m_selectedInstance->isRunning(); - - ui->actionLaunchInstance->setDisabled(!m_selectedInstance || currentInstanceRunning); - ui->actionLaunchInstanceOffline->setDisabled(!m_selectedInstance || currentInstanceRunning); - ui->actionLaunchInstanceDemo->setDisabled(!m_selectedInstance || currentInstanceRunning); - QMenu *launchMenu = ui->actionLaunchInstance->menu(); if (launchMenu) - { launchMenu->clear(); - } else - { launchMenu = new QMenu(this); - } - QAction *normalLaunch = launchMenu->addAction(tr("Launch")); - normalLaunch->setShortcut(QKeySequence::Open); - QAction *normalLaunchOffline = launchMenu->addAction(tr("Launch Offline")); - normalLaunchOffline->setShortcut(QKeySequence(tr("Ctrl+Shift+O"))); - QAction *normalLaunchDemo = launchMenu->addAction(tr("Launch Demo")); - normalLaunchDemo->setShortcut(QKeySequence(tr("Ctrl+Alt+O"))); - if (m_selectedInstance) - { - normalLaunch->setEnabled(m_selectedInstance->canLaunch()); - normalLaunchOffline->setEnabled(m_selectedInstance->canLaunch()); - normalLaunchDemo->setEnabled(m_selectedInstance->canLaunch()); - - connect(normalLaunch, &QAction::triggered, [this]() { - APPLICATION->launch(m_selectedInstance, true, false); - }); - connect(normalLaunchOffline, &QAction::triggered, [this]() { - APPLICATION->launch(m_selectedInstance, false, false); - }); - connect(normalLaunchDemo, &QAction::triggered, [this]() { - APPLICATION->launch(m_selectedInstance, false, true); - }); - } - else - { - normalLaunch->setDisabled(true); - normalLaunchOffline->setDisabled(true); - normalLaunchDemo->setDisabled(true); - } - - // Disable demo-mode if not available. - auto instance = dynamic_cast(m_selectedInstance.get()); - if (instance) { - normalLaunchDemo->setEnabled(instance->supportsDemo()); - } - - QString profilersTitle = tr("Profilers"); - launchMenu->addSeparator()->setText(profilersTitle); - for (auto profiler : APPLICATION->profilers().values()) - { - QAction *profilerAction = launchMenu->addAction(profiler->name()); - QAction *profilerOfflineAction = launchMenu->addAction(tr("%1 Offline").arg(profiler->name())); - QString error; - if (!profiler->check(&error)) - { - profilerAction->setDisabled(true); - profilerOfflineAction->setDisabled(true); - QString profilerToolTip = tr("Profiler not setup correctly. Go into settings, \"External Tools\"."); - profilerAction->setToolTip(profilerToolTip); - profilerOfflineAction->setToolTip(profilerToolTip); - } - else if (m_selectedInstance) - { - profilerAction->setEnabled(m_selectedInstance->canLaunch()); - profilerOfflineAction->setEnabled(m_selectedInstance->canLaunch()); - - connect(profilerAction, &QAction::triggered, [this, profiler]() - { - APPLICATION->launch(m_selectedInstance, true, false, profiler.get()); - }); - connect(profilerOfflineAction, &QAction::triggered, [this, profiler]() - { - APPLICATION->launch(m_selectedInstance, false, false, profiler.get()); - }); - } - else - { - profilerAction->setDisabled(true); - profilerOfflineAction->setDisabled(true); - } - } + m_selectedInstance->populateLaunchMenu(launchMenu); ui->actionLaunchInstance->setMenu(launchMenu); } @@ -1268,7 +1189,7 @@ void MainWindow::globalSettingsClosed() proxymodel->invalidate(); proxymodel->sort(0); updateMainToolBar(); - updateToolsMenu(); + updateLaunchButton(); updateThemeMenu(); updateStatusCenter(); // This needs to be done to prevent UI elements disappearing in the event the config is changed @@ -1471,22 +1392,6 @@ void MainWindow::activateInstance(InstancePtr instance) APPLICATION->launch(instance); } -void MainWindow::on_actionLaunchInstanceOffline_triggered() -{ - if (m_selectedInstance) - { - APPLICATION->launch(m_selectedInstance, false); - } -} - -void MainWindow::on_actionLaunchInstanceDemo_triggered() -{ - if (m_selectedInstance) - { - APPLICATION->launch(m_selectedInstance, false, true); - } -} - void MainWindow::on_actionKillInstance_triggered() { if(m_selectedInstance && m_selectedInstance->isRunning()) @@ -1633,6 +1538,7 @@ void MainWindow::instanceChanged(const QModelIndex ¤t, const QModelIndex & } if (m_selectedInstance) { disconnect(m_selectedInstance.get(), &BaseInstance::runningStatusChanged, this, &MainWindow::refreshCurrentInstance); + disconnect(m_selectedInstance.get(), &BaseInstance::profilerChanged, this, &MainWindow::refreshCurrentInstance); } QString id = current.data(InstanceList::InstanceIDRole).toString(); m_selectedInstance = APPLICATION->instances()->getInstanceById(id); @@ -1641,14 +1547,6 @@ void MainWindow::instanceChanged(const QModelIndex ¤t, const QModelIndex & ui->instanceToolBar->setEnabled(true); setInstanceActionsEnabled(true); ui->actionLaunchInstance->setEnabled(m_selectedInstance->canLaunch()); - ui->actionLaunchInstanceOffline->setEnabled(m_selectedInstance->canLaunch()); - ui->actionLaunchInstanceDemo->setEnabled(m_selectedInstance->canLaunch()); - - // Disable demo-mode if not available. - auto instance = dynamic_cast(m_selectedInstance.get()); - if (instance) { - ui->actionLaunchInstanceDemo->setEnabled(instance->supportsDemo()); - } ui->actionKillInstance->setEnabled(m_selectedInstance->isRunning()); ui->actionExportInstance->setEnabled(m_selectedInstance->canExport()); @@ -1657,19 +1555,18 @@ void MainWindow::instanceChanged(const QModelIndex ¤t, const QModelIndex & updateStatusCenter(); updateInstanceToolIcon(m_selectedInstance->iconKey()); - updateToolsMenu(); + updateLaunchButton(); APPLICATION->settings()->set("SelectedInstance", m_selectedInstance->id()); connect(m_selectedInstance.get(), &BaseInstance::runningStatusChanged, this, &MainWindow::refreshCurrentInstance); + connect(m_selectedInstance.get(), &BaseInstance::profilerChanged, this, &MainWindow::refreshCurrentInstance); } else { ui->instanceToolBar->setEnabled(false); setInstanceActionsEnabled(false); ui->actionLaunchInstance->setEnabled(false); - ui->actionLaunchInstanceOffline->setEnabled(false); - ui->actionLaunchInstanceDemo->setEnabled(false); ui->actionKillInstance->setEnabled(false); APPLICATION->settings()->set("SelectedInstance", QString()); selectionBad(); @@ -1700,7 +1597,7 @@ void MainWindow::selectionBad() statusBar()->clearMessage(); ui->instanceToolBar->setEnabled(false); setInstanceActionsEnabled(false); - updateToolsMenu(); + updateLaunchButton(); renameButton->setText(tr("Rename Instance")); updateInstanceToolIcon("grass"); @@ -1769,7 +1666,7 @@ void MainWindow::setInstanceActionsEnabled(bool enabled) ui->actionCreateInstanceShortcut->setEnabled(enabled); } -void MainWindow::refreshCurrentInstance(bool running) +void MainWindow::refreshCurrentInstance() { auto current = view->selectionModel()->currentIndex(); instanceChanged(current, current); diff --git a/launcher/ui/MainWindow.h b/launcher/ui/MainWindow.h index 3bb20c4a4..7814e5b1f 100644 --- a/launcher/ui/MainWindow.h +++ b/launcher/ui/MainWindow.h @@ -143,10 +143,6 @@ private slots: void on_actionLaunchInstance_triggered(); - void on_actionLaunchInstanceOffline_triggered(); - - void on_actionLaunchInstanceDemo_triggered(); - void on_actionKillInstance_triggered(); void on_actionDeleteInstance_triggered(); @@ -175,7 +171,7 @@ private slots: void updateMainToolBar(); - void updateToolsMenu(); + void updateLaunchButton(); void updateThemeMenu(); @@ -210,7 +206,7 @@ private slots: void keyReleaseEvent(QKeyEvent *event) override; #endif - void refreshCurrentInstance(bool running); + void refreshCurrentInstance(); private: void retranslateUi(); diff --git a/launcher/ui/MainWindow.ui b/launcher/ui/MainWindow.ui index 113dfc1e0..bc1edf394 100644 --- a/launcher/ui/MainWindow.ui +++ b/launcher/ui/MainWindow.ui @@ -435,22 +435,6 @@ Ctrl+D
- - - Launch &Offline - - - Launch the selected instance in offline mode. - - - - - Launch &Demo - - - Launch the selected instance in demo mode. - - @@ -465,7 +449,8 @@ - + + .. Prism Launcher (zip) @@ -473,7 +458,8 @@ - + + .. Modrinth (mrpack) diff --git a/launcher/ui/pages/instance/ServersPage.cpp b/launcher/ui/pages/instance/ServersPage.cpp index 4b1fa08a3..5e899a11f 100644 --- a/launcher/ui/pages/instance/ServersPage.cpp +++ b/launcher/ui/pages/instance/ServersPage.cpp @@ -3,7 +3,7 @@ * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * Copyright (C) 2022 Sefa Eyeoglu - * Copyright (C) 2022 TheKodeToad + * Copyright (C) 2023 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 @@ -835,7 +835,7 @@ void ServersPage::on_actionMove_Down_triggered() void ServersPage::on_actionJoin_triggered() { const auto &address = m_model->at(currentServer)->m_address; - APPLICATION->launch(m_inst, true, false, nullptr, std::make_shared(MinecraftServerTarget::parse(address))); + APPLICATION->launch(m_inst, true, false, std::make_shared(MinecraftServerTarget::parse(address))); } #include "ServersPage.moc" From 440dcdf02206e582814f7fdcc58c9b4755d8d3cc Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Thu, 6 Jul 2023 18:31:59 +0100 Subject: [PATCH 093/268] Import QActionGroup properly :P Signed-off-by: TheKodeToad --- launcher/minecraft/MinecraftInstance.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index d511451e5..30f41de37 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -90,6 +90,8 @@ #include "tools/BaseProfiler.h" +#include + #ifdef Q_OS_LINUX #include "MangoHud.h" #endif From 4c25e3ce75158b6a17fafd60849621f5e0338fc9 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Thu, 6 Jul 2023 21:04:21 +0100 Subject: [PATCH 094/268] Add keyboard shortcut to Kill action Signed-off-by: TheKodeToad --- launcher/ui/InstanceWindow.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/launcher/ui/InstanceWindow.cpp b/launcher/ui/InstanceWindow.cpp index 350b19079..78580bcb9 100644 --- a/launcher/ui/InstanceWindow.cpp +++ b/launcher/ui/InstanceWindow.cpp @@ -98,6 +98,7 @@ InstanceWindow::InstanceWindow(InstancePtr instance, QWidget *parent) m_killButton = new QPushButton(this); m_killButton->setText(tr("&Kill")); m_killButton->setToolTip(tr("Kill the running instance")); + m_killButton->setShortcut(QKeySequence(tr("Ctrl+K"))); horizontalLayout->addWidget(m_killButton); connect(m_killButton, &QPushButton::clicked, this, [this] { APPLICATION->kill(m_instance); }); From ec85266860b7503b24ff3840fb6bf74588acc7c8 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 7 Jul 2023 12:18:04 +0100 Subject: [PATCH 095/268] Memory leak fixes Signed-off-by: TheKodeToad --- launcher/ui/InstanceWindow.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/launcher/ui/InstanceWindow.cpp b/launcher/ui/InstanceWindow.cpp index 78580bcb9..ac8454c7e 100644 --- a/launcher/ui/InstanceWindow.cpp +++ b/launcher/ui/InstanceWindow.cpp @@ -75,11 +75,11 @@ InstanceWindow::InstanceWindow(InstancePtr instance, QWidget *parent) // Add custom buttons to the page container layout. { - auto horizontalLayout = new QHBoxLayout(); + auto horizontalLayout = new QHBoxLayout(this); horizontalLayout->setObjectName(QStringLiteral("horizontalLayout")); horizontalLayout->setContentsMargins(6, -1, 6, -1); - auto btnHelp = new QPushButton(); + auto btnHelp = new QPushButton(this); btnHelp->setText(tr("Help")); horizontalLayout->addWidget(btnHelp); connect(btnHelp, &QPushButton::clicked, m_container, &PageContainer::help); @@ -104,7 +104,7 @@ InstanceWindow::InstanceWindow(InstancePtr instance, QWidget *parent) updateButtons(); - m_closeButton = new QPushButton(); + m_closeButton = new QPushButton(this); m_closeButton->setText(tr("Close")); horizontalLayout->addWidget(m_closeButton); connect(m_closeButton, &QPushButton::clicked, this, &QMainWindow::close); From cebb4dd17ae9bfab35210250ab7a5484c644abb0 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 14 Jul 2023 20:39:53 +0300 Subject: [PATCH 096/268] made the number of concurrent tasks configurable Signed-off-by: Trial97 --- launcher/Application.cpp | 2 ++ launcher/modplatform/EnsureMetadataTask.cpp | 2 +- .../modplatform/flame/FlamePackExportTask.cpp | 2 +- .../modrinth/ModrinthCheckUpdate.cpp | 2 +- launcher/tasks/ConcurrentTask.h | 5 +++- launcher/ui/dialogs/BlockedModsDialog.cpp | 4 ++-- launcher/ui/pages/global/LauncherPage.cpp | 4 ++++ launcher/ui/pages/global/LauncherPage.ui | 23 +++++++++++++++++++ 8 files changed, 38 insertions(+), 6 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 5aa9efc4a..e427eebdc 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -529,6 +529,8 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_settings->registerSetting("MenuBarInsteadOfToolBar", false); + m_settings->registerSetting("NumberOfConcurrentTasks", 6); + QString defaultMonospace; int defaultSize = 11; #ifdef Q_OS_WIN32 diff --git a/launcher/modplatform/EnsureMetadataTask.cpp b/launcher/modplatform/EnsureMetadataTask.cpp index c3eadd06d..6977c1128 100644 --- a/launcher/modplatform/EnsureMetadataTask.cpp +++ b/launcher/modplatform/EnsureMetadataTask.cpp @@ -33,7 +33,7 @@ EnsureMetadataTask::EnsureMetadataTask(Mod* mod, QDir dir, ModPlatform::Resource EnsureMetadataTask::EnsureMetadataTask(QList& mods, QDir dir, ModPlatform::ResourceProvider prov) : Task(nullptr), m_index_dir(dir), m_provider(prov), m_current_task(nullptr) { - m_hashing_task.reset(new ConcurrentTask(this, "MakeHashesTask", 10)); + m_hashing_task.reset(new ConcurrentTask(this, "MakeHashesTask")); for (auto* mod : mods) { auto hash_task = createNewHash(mod); if (!hash_task) diff --git a/launcher/modplatform/flame/FlamePackExportTask.cpp b/launcher/modplatform/flame/FlamePackExportTask.cpp index ac0da2142..48ddddf70 100644 --- a/launcher/modplatform/flame/FlamePackExportTask.cpp +++ b/launcher/modplatform/flame/FlamePackExportTask.cpp @@ -108,7 +108,7 @@ void FlamePackExportTask::collectHashes() setStatus(tr("Finding file hashes...")); setProgress(1, 5); auto allMods = mcInstance->loaderModList()->allMods(); - ConcurrentTask::Ptr hashingTask(new ConcurrentTask(this, "MakeHashesTask", 10)); + ConcurrentTask::Ptr hashingTask(new ConcurrentTask(this, "MakeHashesTask")); task.reset(hashingTask); for (const QFileInfo& file : files) { const QString relative = gameRoot.relativeFilePath(file.absoluteFilePath()); diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp index a7c22832a..3b0528f0d 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp @@ -39,7 +39,7 @@ void ModrinthCheckUpdate::executeTask() QStringList hashes; auto best_hash_type = ProviderCaps.hashType(ModPlatform::ResourceProvider::MODRINTH).first(); - ConcurrentTask hashing_task(this, "MakeModrinthHashesTask", 10); + ConcurrentTask hashing_task(this, "MakeModrinthHashesTask"); for (auto* mod : m_mods) { if (!mod->enabled()) { emit checkFailed(mod, tr("Disabled mods won't be updated, to prevent mod duplication issues!")); diff --git a/launcher/tasks/ConcurrentTask.h b/launcher/tasks/ConcurrentTask.h index 6325fc9e7..7130ca3a9 100644 --- a/launcher/tasks/ConcurrentTask.h +++ b/launcher/tasks/ConcurrentTask.h @@ -41,6 +41,7 @@ #include #include +#include "Application.h" #include "tasks/Task.h" class ConcurrentTask : public Task { @@ -48,7 +49,9 @@ class ConcurrentTask : public Task { public: using Ptr = shared_qobject_ptr; - explicit ConcurrentTask(QObject* parent = nullptr, QString task_name = "", int max_concurrent = 6); + explicit ConcurrentTask(QObject* parent = nullptr, + QString task_name = "", + int max_concurrent = APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt()); ~ConcurrentTask() override; bool canAbort() const override { return true; } diff --git a/launcher/ui/dialogs/BlockedModsDialog.cpp b/launcher/ui/dialogs/BlockedModsDialog.cpp index fdfae5973..ebb136ede 100644 --- a/launcher/ui/dialogs/BlockedModsDialog.cpp +++ b/launcher/ui/dialogs/BlockedModsDialog.cpp @@ -44,7 +44,7 @@ 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)); + m_hashing_task = shared_qobject_ptr(new ConcurrentTask(this, "MakeHashesTask")); connect(m_hashing_task.get(), &Task::finished, this, &BlockedModsDialog::hashTaskFinished); ui->setupUi(this); @@ -313,7 +313,7 @@ bool BlockedModsDialog::checkValidPath(QString path) // efectivly compare two strings ignoring all separators and case auto laxCompare = [](QString fsfilename, QString metadataFilename) { // allowed character seperators - QList allowedSeperators = { '-', '+', '.' , '_'}; + QList allowedSeperators = { '-', '+', '.', '_' }; // copy in lowercase auto fsName = fsfilename.toLower(); diff --git a/launcher/ui/pages/global/LauncherPage.cpp b/launcher/ui/pages/global/LauncherPage.cpp index 816dde723..9d62258ac 100644 --- a/launcher/ui/pages/global/LauncherPage.cpp +++ b/launcher/ui/pages/global/LauncherPage.cpp @@ -201,6 +201,8 @@ void LauncherPage::applySettings() s->set("MenuBarInsteadOfToolBar", ui->preferMenuBarCheckBox->isChecked()); + s->set("NumberOfConcurrentTasks", ui->numberOfConcurrentTasksSpinBox->value()); + // Console settings s->set("ShowConsole", ui->showConsoleCheck->isChecked()); s->set("AutoCloseConsole", ui->autoCloseConsoleCheck->isChecked()); @@ -251,6 +253,8 @@ void LauncherPage::loadSettings() #endif ui->preferMenuBarCheckBox->setChecked(s->get("MenuBarInsteadOfToolBar").toBool()); + ui->numberOfConcurrentTasksSpinBox->setValue(s->get("NumberOfConcurrentTasks").toInt()); + // Console settings ui->showConsoleCheck->setChecked(s->get("ShowConsole").toBool()); ui->autoCloseConsoleCheck->setChecked(s->get("AutoCloseConsole").toBool()); diff --git a/launcher/ui/pages/global/LauncherPage.ui b/launcher/ui/pages/global/LauncherPage.ui index 26408f44f..33ad39442 100644 --- a/launcher/ui/pages/global/LauncherPage.ui +++ b/launcher/ui/pages/global/LauncherPage.ui @@ -189,6 +189,29 @@
+ + + + Miscellaneous + + + + + + Number of concurrent tasks + + + + + + + 1 + + + + + + From db9f5f44e08dae27578f0d74e5092f47e40a1216 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 14 Jul 2023 22:26:22 +0300 Subject: [PATCH 097/268] Split in two the options Signed-off-by: Trial97 --- launcher/Application.cpp | 3 ++- launcher/minecraft/mod/ResourceFolderModel.cpp | 1 + launcher/modplatform/EnsureMetadataTask.cpp | 3 ++- .../modplatform/flame/FlamePackExportTask.cpp | 4 +++- .../modrinth/ModrinthCheckUpdate.cpp | 2 +- launcher/net/NetJob.cpp | 5 +++++ launcher/net/NetJob.h | 2 +- launcher/tasks/ConcurrentTask.h | 8 ++++---- launcher/ui/dialogs/BlockedModsDialog.cpp | 3 ++- launcher/ui/dialogs/ModUpdateDialog.cpp | 17 ++++++++++------- launcher/ui/pages/global/LauncherPage.cpp | 2 ++ launcher/ui/pages/global/LauncherPage.ui | 14 ++++++++++++++ launcher/ui/pages/instance/ModFolderPage.cpp | 4 ++-- launcher/ui/pages/instance/ResourcePackPage.cpp | 3 ++- launcher/ui/pages/instance/ShaderPackPage.cpp | 2 +- launcher/ui/pages/instance/TexturePackPage.cpp | 3 ++- launcher/ui/pages/modplatform/ResourceModel.cpp | 1 + 17 files changed, 55 insertions(+), 22 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index e427eebdc..8f52c41cf 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -529,7 +529,8 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_settings->registerSetting("MenuBarInsteadOfToolBar", false); - m_settings->registerSetting("NumberOfConcurrentTasks", 6); + m_settings->registerSetting("NumberOfConcurrentTasks", 10); + m_settings->registerSetting("NumberOfConcurrentDownloads", 6); QString defaultMonospace; int defaultSize = 11; diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index 39a61067e..e2aa957be 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -33,6 +33,7 @@ ResourceFolderModel::ResourceFolderModel(QDir dir, BaseInstance* instance, QObje connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &ResourceFolderModel::directoryChanged); connect(&m_helper_thread_task, &ConcurrentTask::finished, this, [this] { m_helper_thread_task.clear(); }); + m_helper_thread_task.setMaxConcurrent(APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt()); } ResourceFolderModel::~ResourceFolderModel() diff --git a/launcher/modplatform/EnsureMetadataTask.cpp b/launcher/modplatform/EnsureMetadataTask.cpp index 6977c1128..a9ad22581 100644 --- a/launcher/modplatform/EnsureMetadataTask.cpp +++ b/launcher/modplatform/EnsureMetadataTask.cpp @@ -3,6 +3,7 @@ #include #include +#include "Application.h" #include "Json.h" #include "minecraft/mod/Mod.h" @@ -33,7 +34,7 @@ EnsureMetadataTask::EnsureMetadataTask(Mod* mod, QDir dir, ModPlatform::Resource EnsureMetadataTask::EnsureMetadataTask(QList& mods, QDir dir, ModPlatform::ResourceProvider prov) : Task(nullptr), m_index_dir(dir), m_provider(prov), m_current_task(nullptr) { - m_hashing_task.reset(new ConcurrentTask(this, "MakeHashesTask")); + m_hashing_task.reset(new ConcurrentTask(this, "MakeHashesTask", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt())); for (auto* mod : mods) { auto hash_task = createNewHash(mod); if (!hash_task) diff --git a/launcher/modplatform/flame/FlamePackExportTask.cpp b/launcher/modplatform/flame/FlamePackExportTask.cpp index 48ddddf70..b0e5638d9 100644 --- a/launcher/modplatform/flame/FlamePackExportTask.cpp +++ b/launcher/modplatform/flame/FlamePackExportTask.cpp @@ -27,6 +27,7 @@ #include #include #include +#include "Application.h" #include "Json.h" #include "MMCZip.h" #include "minecraft/PackProfile.h" @@ -108,7 +109,8 @@ void FlamePackExportTask::collectHashes() setStatus(tr("Finding file hashes...")); setProgress(1, 5); auto allMods = mcInstance->loaderModList()->allMods(); - ConcurrentTask::Ptr hashingTask(new ConcurrentTask(this, "MakeHashesTask")); + ConcurrentTask::Ptr hashingTask( + new ConcurrentTask(this, "MakeHashesTask", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt())); task.reset(hashingTask); for (const QFileInfo& file : files) { const QString relative = gameRoot.relativeFilePath(file.absoluteFilePath()); diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp index 3b0528f0d..c1f83bbc5 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp @@ -39,7 +39,7 @@ void ModrinthCheckUpdate::executeTask() QStringList hashes; auto best_hash_type = ProviderCaps.hashType(ModPlatform::ResourceProvider::MODRINTH).first(); - ConcurrentTask hashing_task(this, "MakeModrinthHashesTask"); + ConcurrentTask hashing_task(this, "MakeModrinthHashesTask", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt()); for (auto* mod : m_mods) { if (!mod->enabled()) { emit checkFailed(mod, tr("Disabled mods won't be updated, to prevent mod duplication issues!")); diff --git a/launcher/net/NetJob.cpp b/launcher/net/NetJob.cpp index 3869316e3..b99c5acb0 100644 --- a/launcher/net/NetJob.cpp +++ b/launcher/net/NetJob.cpp @@ -36,6 +36,11 @@ */ #include "NetJob.h" +#include "Application.h" + +NetJob::NetJob(QString job_name, shared_qobject_ptr network) + : ConcurrentTask(nullptr, job_name, APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()), m_network(network) +{} auto NetJob::addNetAction(NetAction::Ptr action) -> bool { diff --git a/launcher/net/NetJob.h b/launcher/net/NetJob.h index 764cec18b..1c4337ec6 100644 --- a/launcher/net/NetJob.h +++ b/launcher/net/NetJob.h @@ -52,7 +52,7 @@ class NetJob : public ConcurrentTask { public: using Ptr = shared_qobject_ptr; - explicit NetJob(QString job_name, shared_qobject_ptr network) : ConcurrentTask(nullptr, job_name), m_network(network) {} + explicit NetJob(QString job_name, shared_qobject_ptr network); ~NetJob() override = default; void startNext() override; diff --git a/launcher/tasks/ConcurrentTask.h b/launcher/tasks/ConcurrentTask.h index 7130ca3a9..1c333ce02 100644 --- a/launcher/tasks/ConcurrentTask.h +++ b/launcher/tasks/ConcurrentTask.h @@ -41,7 +41,6 @@ #include #include -#include "Application.h" #include "tasks/Task.h" class ConcurrentTask : public Task { @@ -49,11 +48,12 @@ class ConcurrentTask : public Task { public: using Ptr = shared_qobject_ptr; - explicit ConcurrentTask(QObject* parent = nullptr, - QString task_name = "", - int max_concurrent = APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt()); + explicit ConcurrentTask(QObject* parent = nullptr, QString task_name = "", int max_concurrent = 6); ~ConcurrentTask() override; + // safe to call before starting the task + void setMaxConcurrent(int max_concurrent) { m_total_max_size = max_concurrent; } + bool canAbort() const override { return true; } inline auto isMultiStep() const -> bool override { return totalSize() > 1; }; diff --git a/launcher/ui/dialogs/BlockedModsDialog.cpp b/launcher/ui/dialogs/BlockedModsDialog.cpp index ebb136ede..5a1a2f80e 100644 --- a/launcher/ui/dialogs/BlockedModsDialog.cpp +++ b/launcher/ui/dialogs/BlockedModsDialog.cpp @@ -44,7 +44,8 @@ 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")); + m_hashing_task = shared_qobject_ptr( + new ConcurrentTask(this, "MakeHashesTask", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt())); connect(m_hashing_task.get(), &Task::finished, this, &BlockedModsDialog::hashTaskFinished); ui->setupUi(this); diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index 8618b9240..d08c2898f 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -43,7 +43,8 @@ ModUpdateDialog::ModUpdateDialog(QWidget* parent, , m_parent(parent) , m_mod_model(mods) , m_candidates(search_for) - , m_second_try_metadata(new ConcurrentTask()) + , m_second_try_metadata( + new ConcurrentTask(nullptr, "Second Metadata Search", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt())) , m_instance(instance) { ReviewMessageBox::setGeometry(0, 0, 800, 600); @@ -89,15 +90,17 @@ void ModUpdateDialog::checkCandidates() if (!m_modrinth_to_update.empty()) { m_modrinth_check_task.reset(new ModrinthCheckUpdate(m_modrinth_to_update, versions, loaders, m_mod_model)); - connect(m_modrinth_check_task.get(), &CheckUpdateTask::checkFailed, this, - [this](Mod* mod, QString reason, QUrl recover_url) { m_failed_check_update.append({mod, reason, recover_url}); }); + connect(m_modrinth_check_task.get(), &CheckUpdateTask::checkFailed, this, [this](Mod* mod, QString reason, QUrl recover_url) { + m_failed_check_update.append({ mod, reason, recover_url }); + }); check_task.addTask(m_modrinth_check_task); } if (!m_flame_to_update.empty()) { m_flame_check_task.reset(new FlameCheckUpdate(m_flame_to_update, versions, loaders, m_mod_model)); - connect(m_flame_check_task.get(), &CheckUpdateTask::checkFailed, this, - [this](Mod* mod, QString reason, QUrl recover_url) { m_failed_check_update.append({mod, reason, recover_url}); }); + connect(m_flame_check_task.get(), &CheckUpdateTask::checkFailed, this, [this](Mod* mod, QString reason, QUrl recover_url) { + m_failed_check_update.append({ mod, reason, recover_url }); + }); check_task.addTask(m_flame_check_task); } @@ -162,7 +165,7 @@ void ModUpdateDialog::checkCandidates() if (!recover_url.isEmpty()) //: %1 is the link to download it manually text += tr("Possible solution: Getting the latest version manually:
%1
") - .arg(QString("%1").arg(recover_url.toString())); + .arg(QString("%1").arg(recover_url.toString())); text += "
"; } @@ -342,7 +345,7 @@ void ModUpdateDialog::onMetadataFailed(Mod* mod, bool try_others, ModPlatform::R } else { QString reason{ tr("Couldn't find a valid version on the selected mod provider(s)") }; - m_failed_metadata.append({mod, reason}); + m_failed_metadata.append({ mod, reason }); } } diff --git a/launcher/ui/pages/global/LauncherPage.cpp b/launcher/ui/pages/global/LauncherPage.cpp index 9d62258ac..86597a5e4 100644 --- a/launcher/ui/pages/global/LauncherPage.cpp +++ b/launcher/ui/pages/global/LauncherPage.cpp @@ -202,6 +202,7 @@ void LauncherPage::applySettings() s->set("MenuBarInsteadOfToolBar", ui->preferMenuBarCheckBox->isChecked()); s->set("NumberOfConcurrentTasks", ui->numberOfConcurrentTasksSpinBox->value()); + s->set("NumberOfConcurrentDownloads", ui->numberOfConcurrentDownloadsSpinBox->value()); // Console settings s->set("ShowConsole", ui->showConsoleCheck->isChecked()); @@ -254,6 +255,7 @@ void LauncherPage::loadSettings() ui->preferMenuBarCheckBox->setChecked(s->get("MenuBarInsteadOfToolBar").toBool()); ui->numberOfConcurrentTasksSpinBox->setValue(s->get("NumberOfConcurrentTasks").toInt()); + ui->numberOfConcurrentDownloadsSpinBox->setValue(s->get("NumberOfConcurrentDownloads").toInt()); // Console settings ui->showConsoleCheck->setChecked(s->get("ShowConsole").toBool()); diff --git a/launcher/ui/pages/global/LauncherPage.ui b/launcher/ui/pages/global/LauncherPage.ui index 33ad39442..11c59ab78 100644 --- a/launcher/ui/pages/global/LauncherPage.ui +++ b/launcher/ui/pages/global/LauncherPage.ui @@ -209,6 +209,20 @@
+ + + + Number of concurrent downloads + + + + + + + 1 + + + diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index cef292bd9..a8ce68c56 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -166,7 +166,7 @@ void ModFolderPage::installMods() ResourceDownload::ModDownloadDialog mdownload(this, m_model, m_instance); if (mdownload.exec()) { - ConcurrentTask* tasks = new ConcurrentTask(this); + auto tasks = new ConcurrentTask(this, "Download Mods", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); connect(tasks, &Task::failed, [this, tasks](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); tasks->deleteLater(); @@ -225,7 +225,7 @@ void ModFolderPage::updateMods() } if (update_dialog.exec()) { - ConcurrentTask* tasks = new ConcurrentTask(this); + auto tasks = new ConcurrentTask(this, "Download Mods", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); connect(tasks, &Task::failed, [this, tasks](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); tasks->deleteLater(); diff --git a/launcher/ui/pages/instance/ResourcePackPage.cpp b/launcher/ui/pages/instance/ResourcePackPage.cpp index 12b371df4..cba80142a 100644 --- a/launcher/ui/pages/instance/ResourcePackPage.cpp +++ b/launcher/ui/pages/instance/ResourcePackPage.cpp @@ -72,7 +72,8 @@ void ResourcePackPage::downloadRPs() ResourceDownload::ResourcePackDownloadDialog mdownload(this, std::static_pointer_cast(m_model), m_instance); if (mdownload.exec()) { - auto tasks = new ConcurrentTask(this); + auto tasks = + new ConcurrentTask(this, "Download Resource Pack", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); connect(tasks, &Task::failed, [this, tasks](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); tasks->deleteLater(); diff --git a/launcher/ui/pages/instance/ShaderPackPage.cpp b/launcher/ui/pages/instance/ShaderPackPage.cpp index dc8b0a05b..40366a1be 100644 --- a/launcher/ui/pages/instance/ShaderPackPage.cpp +++ b/launcher/ui/pages/instance/ShaderPackPage.cpp @@ -65,7 +65,7 @@ void ShaderPackPage::downloadShaders() ResourceDownload::ShaderPackDownloadDialog mdownload(this, std::static_pointer_cast(m_model), m_instance); if (mdownload.exec()) { - auto tasks = new ConcurrentTask(this); + auto tasks = new ConcurrentTask(this, "Download Shaders", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); connect(tasks, &Task::failed, [this, tasks](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); tasks->deleteLater(); diff --git a/launcher/ui/pages/instance/TexturePackPage.cpp b/launcher/ui/pages/instance/TexturePackPage.cpp index e477ceda3..bc07d495c 100644 --- a/launcher/ui/pages/instance/TexturePackPage.cpp +++ b/launcher/ui/pages/instance/TexturePackPage.cpp @@ -74,7 +74,8 @@ void TexturePackPage::downloadTPs() ResourceDownload::TexturePackDownloadDialog mdownload(this, std::static_pointer_cast(m_model), m_instance); if (mdownload.exec()) { - auto tasks = new ConcurrentTask(this); + auto tasks = + new ConcurrentTask(this, "Download Texture Packs", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); connect(tasks, &Task::failed, [this, tasks](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); tasks->deleteLater(); diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp index 49405a02b..e0b663522 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.cpp +++ b/launcher/ui/pages/modplatform/ResourceModel.cpp @@ -31,6 +31,7 @@ QHash ResourceModel::s_running_models; ResourceModel::ResourceModel(ResourceAPI* api) : QAbstractListModel(), m_api(api) { s_running_models.insert(this, true); + m_current_info_job.setMaxConcurrent(APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); } ResourceModel::~ResourceModel() From 149b6d59cf848a3b3cd50b3aee1c112e9c47e633 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 14 Jul 2023 23:22:11 +0300 Subject: [PATCH 098/268] fixed tests Signed-off-by: Trial97 --- launcher/CMakeLists.txt | 3 +++ launcher/minecraft/mod/ResourceFolderModel.cpp | 3 +++ launcher/ui/pages/modplatform/ResourceModel.cpp | 2 ++ 3 files changed, 8 insertions(+) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index af3bc28e3..ea8b1b841 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -1094,6 +1094,9 @@ endif() # Add executable add_library(Launcher_logic STATIC ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${LAUNCHER_UI} ${LAUNCHER_RESOURCES}) +if(BUILD_TESTING) +target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_TEST) +endif() target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(Launcher_logic systeminfo diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index e2aa957be..7c2dc4393 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -33,7 +33,10 @@ ResourceFolderModel::ResourceFolderModel(QDir dir, BaseInstance* instance, QObje connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &ResourceFolderModel::directoryChanged); connect(&m_helper_thread_task, &ConcurrentTask::finished, this, [this] { m_helper_thread_task.clear(); }); +#ifndef LAUNCHER_TEST + // in tests the application macro doesn't work m_helper_thread_task.setMaxConcurrent(APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt()); +#endif } ResourceFolderModel::~ResourceFolderModel() diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp index e0b663522..9f95d0467 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.cpp +++ b/launcher/ui/pages/modplatform/ResourceModel.cpp @@ -31,7 +31,9 @@ QHash ResourceModel::s_running_models; ResourceModel::ResourceModel(ResourceAPI* api) : QAbstractListModel(), m_api(api) { s_running_models.insert(this, true); +#ifndef LAUNCHER_TEST m_current_info_job.setMaxConcurrent(APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); +#endif } ResourceModel::~ResourceModel() From 20e2c70464059dec07f875b9cb703bacdcfeee57 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sun, 16 Jul 2023 23:23:33 +0100 Subject: [PATCH 099/268] Rename groups and janky reference counting map Signed-off-by: TheKodeToad --- launcher/InstanceList.cpp | 93 ++++++++++++++++------ launcher/InstanceList.h | 46 ++++++++--- launcher/ui/MainWindow.cpp | 73 +++++++++-------- launcher/ui/MainWindow.h | 3 +- launcher/ui/dialogs/CopyInstanceDialog.cpp | 25 +++--- launcher/ui/dialogs/NewInstanceDialog.cpp | 28 +++---- 6 files changed, 166 insertions(+), 102 deletions(-) diff --git a/launcher/InstanceList.cpp b/launcher/InstanceList.cpp index b4c520cd9..6bc906ebd 100644 --- a/launcher/InstanceList.cpp +++ b/launcher/InstanceList.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) 2023 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 @@ -237,8 +238,11 @@ GroupId InstanceList::getInstanceGroup(const InstanceId& id) const return GroupId(); } -void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name) +void InstanceList::setInstanceGroup(const InstanceId& id, GroupId name) { + if (name.isEmpty() && !name.isNull()) + name = QString(); + auto inst = getInstanceById(id); if (!inst) { qDebug() << "Attempt to set a null instance's group"; @@ -249,6 +253,9 @@ void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name) auto iter = m_instanceGroupIndex.find(inst->id()); if (iter != m_instanceGroupIndex.end()) { if (*iter != name) { + if (!iter->isEmpty() && --m_groupNameCache[*iter] == 0) + m_groupNameCache.remove(*iter); + *iter = name; changed = true; } @@ -258,7 +265,9 @@ void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name) } if (changed) { - m_groupNameCache.insert(name); + if (!name.isEmpty()) + m_groupNameCache[name]++; + auto idx = getInstIndex(inst.get()); emit dataChanged(index(idx), index(idx), { GroupRole }); saveGroupList(); @@ -267,29 +276,55 @@ void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name) QStringList InstanceList::getGroups() { - return m_groupNameCache.values(); + return m_groupNameCache.keys(); } -void InstanceList::deleteGroup(const QString& name) +void InstanceList::deleteGroup(const GroupId& name) { + m_groupNameCache.remove(name); + m_collapsedGroups.remove(name); + bool removed = false; qDebug() << "Delete group" << name; for (auto& instance : m_instances) { - const auto& instID = instance->id(); - auto instGroupName = getInstanceGroup(instID); + const QString& instID = instance->id(); + const QString instGroupName = getInstanceGroup(instID); if (instGroupName == name) { m_instanceGroupIndex.remove(instID); qDebug() << "Remove" << instID << "from group" << name; removed = true; auto idx = getInstIndex(instance.get()); - if (idx > 0) { + if (idx > 0) emit dataChanged(index(idx), index(idx), { GroupRole }); - } } } - if (removed) { + if (removed) saveGroupList(); +} + +void InstanceList::renameGroup(const QString& src, const QString& dst) +{ + m_groupNameCache.remove(src); + if (m_collapsedGroups.remove(src)) + m_collapsedGroups.insert(dst); + + bool modified = false; + qDebug() << "Rename group" << src << "to" << dst; + for (auto& instance : m_instances) { + const QString& instID = instance->id(); + const QString instGroupName = getInstanceGroup(instID); + if (instGroupName == src) { + m_instanceGroupIndex[instID] = dst; + m_groupNameCache[dst]++; + qDebug() << "Set" << instID << "group to" << dst; + modified = true; + auto idx = getInstIndex(instance.get()); + if (idx > 0) + emit dataChanged(index(idx), index(idx), { GroupRole }); + } } + if (modified) + saveGroupList(); } bool InstanceList::isGroupCollapsed(const QString& group) @@ -305,12 +340,15 @@ bool InstanceList::trashInstance(const InstanceId& id) return false; } - auto cachedGroupId = m_instanceGroupIndex[id]; + QString cachedGroupId = m_instanceGroupIndex[id]; qDebug() << "Will trash instance" << id; QString trashedLoc; if (m_instanceGroupIndex.remove(id)) { + if (!cachedGroupId.isEmpty() && --m_groupNameCache[cachedGroupId] == 0) + m_groupNameCache.remove(cachedGroupId); + saveGroupList(); } @@ -346,7 +384,8 @@ void InstanceList::undoTrashInstance() { QFile(top.trashPath).rename(top.polyPath); m_instanceGroupIndex[top.id] = top.groupName; - m_groupNameCache.insert(top.groupName); + if (!top.groupName.isEmpty()) + m_groupNameCache[top.groupName]++; saveGroupList(); emit instancesChanged(); @@ -360,7 +399,12 @@ void InstanceList::deleteInstance(const InstanceId& id) return; } + QString cachedGroupId = m_instanceGroupIndex[id]; + if (m_instanceGroupIndex.remove(id)) { + if (!cachedGroupId.isEmpty() && --m_groupNameCache[cachedGroupId] == 0) + m_groupNameCache.remove(cachedGroupId); + saveGroupList(); } @@ -621,7 +665,7 @@ void InstanceList::saveGroupList() QString groupFileName = m_instDir + "/instgroups.json"; QMap> reverseGroupMap; for (auto iter = m_instanceGroupIndex.begin(); iter != m_instanceGroupIndex.end(); iter++) { - QString id = iter.key(); + const QString& id = iter.key(); QString group = iter.value(); if (group.isEmpty()) continue; @@ -711,17 +755,22 @@ void InstanceList::loadGroupList() return; } - QSet groupSet; m_instanceGroupIndex.clear(); + m_groupNameCache.clear(); // Iterate through all the groups. QJsonObject groupMapping = rootObj.value("groups").toObject(); for (QJsonObject::iterator iter = groupMapping.begin(); iter != groupMapping.end(); iter++) { QString groupName = iter.key(); + if (iter.key().isEmpty()) { + qWarning() << "Redundant empty group found"; + continue; + } + // If not an object, complain and skip to the next one. if (!iter.value().isObject()) { - qWarning() << QString("Group '%1' in the group list should be an object.").arg(groupName).toUtf8(); + qWarning() << QString("Group '%1' in the group list should be an object").arg(groupName).toUtf8(); continue; } @@ -734,22 +783,19 @@ void InstanceList::loadGroupList() } // keep a list/set of groups for choosing - groupSet.insert(groupName); + m_groupNameCache[groupName]++; auto hidden = groupObj.value("hidden").toBool(false); - if (hidden) { + if (hidden) m_collapsedGroups.insert(groupName); - } // Iterate through the list of instances in the group. QJsonArray instancesArray = groupObj.value("instances").toArray(); - for (QJsonArray::iterator iter2 = instancesArray.begin(); iter2 != instancesArray.end(); iter2++) { - m_instanceGroupIndex[(*iter2).toString()] = groupName; - } + for (auto value : instancesArray) + m_instanceGroupIndex[value.toString()] = groupName; } m_groupsLoaded = true; - m_groupNameCache.unite(groupSet); qDebug() << "Group list loaded."; } @@ -924,7 +970,8 @@ bool InstanceList::commitStagedInstance(const QString& path, InstanceName const& } m_instanceGroupIndex[instID] = groupName; - m_groupNameCache.insert(groupName); + if (!groupName.isEmpty()) + m_groupNameCache[groupName]++; } instanceSet.insert(instID); diff --git a/launcher/InstanceList.h b/launcher/InstanceList.h index 48bede07d..61795d86c 100644 --- a/launcher/InstanceList.h +++ b/launcher/InstanceList.h @@ -1,16 +1,36 @@ -/* Copyright 2013-2021 MultiMC Contributors +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 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. + * 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. */ #pragma once @@ -110,9 +130,10 @@ public: bool isGroupCollapsed(const QString &groupName); GroupId getInstanceGroup(const InstanceId & id) const; - void setInstanceGroup(const InstanceId & id, const GroupId& name); + void setInstanceGroup(const InstanceId & id, GroupId name); void deleteGroup(const GroupId & name); + void renameGroup(const GroupId& src, const GroupId& dst); bool trashInstance(const InstanceId &id); bool trashedSomething(); void undoTrashInstance(); @@ -187,7 +208,8 @@ private: int totalPlayTime = 0; bool m_dirty = false; QList m_instances; - QSet m_groupNameCache; + // id -> refs + QMap m_groupNameCache; SettingsObjectPtr m_globalSettings; QString m_instDir; diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index da572fc34..70a76e218 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -509,10 +509,10 @@ void MainWindow::showInstanceContextMenu(const QPoint& pos) } else { auto group = view->groupNameAt(pos); - QAction* actionVoid = new QAction(BuildConfig.LAUNCHER_DISPLAYNAME, this); + QAction* actionVoid = new QAction(group.isNull() ? BuildConfig.LAUNCHER_DISPLAYNAME : group, this); actionVoid->setEnabled(false); - QAction* actionCreateInstance = new QAction(tr("Create instance"), this); + QAction* actionCreateInstance = new QAction(tr("&Create instance"), this); actionCreateInstance->setToolTip(ui->actionAddInstance->toolTip()); if (!group.isNull()) { QVariantMap data; @@ -526,12 +526,13 @@ void MainWindow::showInstanceContextMenu(const QPoint& pos) actions.prepend(actionVoid); actions.append(actionCreateInstance); if (!group.isNull()) { - QAction* actionDeleteGroup = new QAction(tr("Delete group '%1'").arg(group), this); - QVariantMap data; - data["group"] = group; - actionDeleteGroup->setData(data); - connect(actionDeleteGroup, SIGNAL(triggered(bool)), SLOT(deleteGroup())); + QAction* actionDeleteGroup = new QAction(tr("&Delete group"), this); + connect(actionDeleteGroup, &QAction::triggered, this, [this, group] { deleteGroup(group); }); actions.append(actionDeleteGroup); + + QAction* actionRenameGroup = new QAction(tr("&Rename group"), this); + connect(actionRenameGroup, &QAction::triggered, this, [this, group] { renameGroup(group); }); + actions.append(actionRenameGroup); } } QMenu myMenu; @@ -1092,40 +1093,50 @@ void MainWindow::on_actionChangeInstGroup_triggered() if (!m_selectedInstance) return; - bool ok = false; InstanceId instId = m_selectedInstance->id(); - QString name(APPLICATION->instances()->getInstanceGroup(instId)); - auto groups = APPLICATION->instances()->getGroups(); - groups.insert(0, ""); - groups.sort(Qt::CaseInsensitive); - int foo = groups.indexOf(name); + QString src(APPLICATION->instances()->getInstanceGroup(instId)); + + QStringList groups = APPLICATION->instances()->getGroups(); + if (!groups.isEmpty()) + groups.prepend(""); + + int index = groups.indexOf(src); + bool ok = false; + QString dst = QInputDialog::getItem(this, tr("Group name"), tr("Enter a new group name."), groups, index, true, &ok); + dst = dst.simplified(); - name = QInputDialog::getItem(this, tr("Group name"), tr("Enter a new group name."), groups, foo, true, &ok); - name = name.simplified(); if (ok) { - APPLICATION->instances()->setInstanceGroup(instId, name); + APPLICATION->instances()->setInstanceGroup(instId, dst); } } -void MainWindow::deleteGroup() +void MainWindow::deleteGroup(QString group) { - QObject* obj = sender(); - if (!obj) + Q_ASSERT(!group.isEmpty()); + + const int reply = QMessageBox::question(this, tr("Delete group"), tr("Are you sure you want to delete the group '%1'?").arg(group), + QMessageBox::Yes | QMessageBox::No); + if (reply == QMessageBox::Yes) + APPLICATION->instances()->deleteGroup(group); +} + +void MainWindow::renameGroup(QString group) { + Q_ASSERT(!group.isEmpty()); + + QString name = QInputDialog::getText(this, tr("Rename group"), tr("Enter a new group name."), QLineEdit::Normal, group); + name = name.simplified(); + if (name.isNull() || name == group) return; - QAction* action = qobject_cast(obj); - if (!action) + + const bool empty = name.isEmpty(); + const bool duplicate = APPLICATION->instances()->getGroups().contains(name, Qt::CaseInsensitive) && group.toLower() != name.toLower(); + + if (empty || duplicate) { + QMessageBox::warning(this, tr("Cannot rename group"), empty ? tr("Cannot set empty name.") : tr("Group already exists. :/")); return; - auto map = action->data().toMap(); - if (!map.contains("group")) - return; - QString groupName = map["group"].toString(); - if (!groupName.isEmpty()) { - auto reply = QMessageBox::question(this, tr("Delete group"), tr("Are you sure you want to delete the group %1?").arg(groupName), - QMessageBox::Yes | QMessageBox::No); - if (reply == QMessageBox::Yes) { - APPLICATION->instances()->deleteGroup(groupName); - } } + + APPLICATION->instances()->renameGroup(group, name); } void MainWindow::undoTrashInstance() diff --git a/launcher/ui/MainWindow.h b/launcher/ui/MainWindow.h index 27c2756f6..77ed72a45 100644 --- a/launcher/ui/MainWindow.h +++ b/launcher/ui/MainWindow.h @@ -151,7 +151,8 @@ private slots: void on_actionDeleteInstance_triggered(); - void deleteGroup(); + void deleteGroup(QString group); + void renameGroup(QString group); void undoTrashInstance(); inline void on_actionExportInstance_triggered() { on_actionExportInstanceZip_triggered(); } diff --git a/launcher/ui/dialogs/CopyInstanceDialog.cpp b/launcher/ui/dialogs/CopyInstanceDialog.cpp index d75bb5fe3..ce12f605b 100644 --- a/launcher/ui/dialogs/CopyInstanceDialog.cpp +++ b/launcher/ui/dialogs/CopyInstanceDialog.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) 2023 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 @@ -61,22 +62,14 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget* parent) ui->iconButton->setIcon(APPLICATION->icons()->getIcon(InstIconKey)); ui->instNameTextBox->setText(original->name()); ui->instNameTextBox->setFocus(); -#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) - auto groupList = APPLICATION->instances()->getGroups(); - QSet groups(groupList.begin(), groupList.end()); - groupList = QStringList(groups.values()); -#else - auto groups = APPLICATION->instances()->getGroups().toSet(); - auto groupList = QStringList(groups.toList()); -#endif - groupList.sort(Qt::CaseInsensitive); - groupList.removeOne(""); - groupList.push_front(""); - ui->groupBox->addItems(groupList); - int index = groupList.indexOf(APPLICATION->instances()->getInstanceGroup(m_original->id())); - if (index == -1) { + + QStringList groups = APPLICATION->instances()->getGroups(); + groups.prepend(""); + ui->groupBox->addItems(groups); + int index = groups.indexOf(APPLICATION->instances()->getInstanceGroup(m_original->id())); + if (index == -1) index = 0; - } + ui->groupBox->setCurrentIndex(index); ui->groupBox->lineEdit()->setPlaceholderText(tr("No group")); ui->copySavesCheckbox->setChecked(m_selectedOptions.isCopySavesEnabled()); diff --git a/launcher/ui/dialogs/NewInstanceDialog.cpp b/launcher/ui/dialogs/NewInstanceDialog.cpp index 7b9bb944c..30e41c09d 100644 --- a/launcher/ui/dialogs/NewInstanceDialog.cpp +++ b/launcher/ui/dialogs/NewInstanceDialog.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) 2023 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 @@ -74,28 +75,17 @@ NewInstanceDialog::NewInstanceDialog(const QString & initialGroup, const QString InstIconKey = "default"; ui->iconButton->setIcon(APPLICATION->icons()->getIcon(InstIconKey)); -#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) - auto groupList = APPLICATION->instances()->getGroups(); - auto groups = QSet(groupList.begin(), groupList.end()); - groupList = groups.values(); -#else - auto groups = APPLICATION->instances()->getGroups().toSet(); - auto groupList = QStringList(groups.toList()); -#endif - groupList.sort(Qt::CaseInsensitive); - groupList.removeOne(""); - groupList.push_front(initialGroup); - groupList.push_front(""); - ui->groupBox->addItems(groupList); - int index = groupList.indexOf(initialGroup); - if(index == -1) - { - index = 0; + QStringList groups = APPLICATION->instances()->getGroups(); + groups.prepend(""); + int index = groups.indexOf(initialGroup); + if (index == -1) { + index = 1; + groups.insert(index, initialGroup); } + ui->groupBox->addItems(groups); ui->groupBox->setCurrentIndex(index); ui->groupBox->lineEdit()->setPlaceholderText(tr("No group")); - // NOTE: m_buttons must be initialized before PageContainer, because it indirectly accesses m_buttons through setSuggestedPack! Do not move this below. m_buttons = new QDialogButtonBox(QDialogButtonBox::Help | QDialogButtonBox::Ok | QDialogButtonBox::Cancel); From fb5aefffb2f12562910a85f67cb1c8ca3f2ed369 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sun, 16 Jul 2023 23:36:01 +0100 Subject: [PATCH 100/268] Reduce bugginess Signed-off-by: TheKodeToad --- launcher/InstanceList.cpp | 51 +++++++++++++++++++++++---------------- launcher/InstanceList.h | 3 +++ 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/launcher/InstanceList.cpp b/launcher/InstanceList.cpp index 6bc906ebd..d6faaaf1a 100644 --- a/launcher/InstanceList.cpp +++ b/launcher/InstanceList.cpp @@ -253,9 +253,7 @@ void InstanceList::setInstanceGroup(const InstanceId& id, GroupId name) auto iter = m_instanceGroupIndex.find(inst->id()); if (iter != m_instanceGroupIndex.end()) { if (*iter != name) { - if (!iter->isEmpty() && --m_groupNameCache[*iter] == 0) - m_groupNameCache.remove(*iter); - + decreaseGroupCount(*iter); *iter = name; changed = true; } @@ -265,9 +263,7 @@ void InstanceList::setInstanceGroup(const InstanceId& id, GroupId name) } if (changed) { - if (!name.isEmpty()) - m_groupNameCache[name]++; - + increaseGroupCount(name); auto idx = getInstIndex(inst.get()); emit dataChanged(index(idx), index(idx), { GroupRole }); saveGroupList(); @@ -315,7 +311,7 @@ void InstanceList::renameGroup(const QString& src, const QString& dst) const QString instGroupName = getInstanceGroup(instID); if (instGroupName == src) { m_instanceGroupIndex[instID] = dst; - m_groupNameCache[dst]++; + increaseGroupCount(dst); qDebug() << "Set" << instID << "group to" << dst; modified = true; auto idx = getInstIndex(instance.get()); @@ -346,9 +342,7 @@ bool InstanceList::trashInstance(const InstanceId& id) QString trashedLoc; if (m_instanceGroupIndex.remove(id)) { - if (!cachedGroupId.isEmpty() && --m_groupNameCache[cachedGroupId] == 0) - m_groupNameCache.remove(cachedGroupId); - + decreaseGroupCount(cachedGroupId); saveGroupList(); } @@ -384,8 +378,7 @@ void InstanceList::undoTrashInstance() { QFile(top.trashPath).rename(top.polyPath); m_instanceGroupIndex[top.id] = top.groupName; - if (!top.groupName.isEmpty()) - m_groupNameCache[top.groupName]++; + increaseGroupCount(top.groupName); saveGroupList(); emit instancesChanged(); @@ -402,9 +395,7 @@ void InstanceList::deleteInstance(const InstanceId& id) QString cachedGroupId = m_instanceGroupIndex[id]; if (m_instanceGroupIndex.remove(id)) { - if (!cachedGroupId.isEmpty() && --m_groupNameCache[cachedGroupId] == 0) - m_groupNameCache.remove(cachedGroupId); - + decreaseGroupCount(cachedGroupId); saveGroupList(); } @@ -654,6 +645,26 @@ InstancePtr InstanceList::loadInstance(const InstanceId& id) return inst; } + +void InstanceList::increaseGroupCount(const QString& group) +{ + if (group.isEmpty()) + return; + + ++m_groupNameCache[group]; +} + +void InstanceList::decreaseGroupCount(const QString& group) +{ + if (group.isEmpty()) + return; + + if (--m_groupNameCache[group] < 1) { + m_groupNameCache.remove(group); + m_collapsedGroups.remove(group); + } +} + void InstanceList::saveGroupList() { qDebug() << "Will save group list now."; @@ -782,9 +793,6 @@ void InstanceList::loadGroupList() continue; } - // keep a list/set of groups for choosing - m_groupNameCache[groupName]++; - auto hidden = groupObj.value("hidden").toBool(false); if (hidden) m_collapsedGroups.insert(groupName); @@ -792,8 +800,10 @@ void InstanceList::loadGroupList() // Iterate through the list of instances in the group. QJsonArray instancesArray = groupObj.value("instances").toArray(); - for (auto value : instancesArray) + for (auto value : instancesArray) { m_instanceGroupIndex[value.toString()] = groupName; + increaseGroupCount(groupName); + } } m_groupsLoaded = true; qDebug() << "Group list loaded."; @@ -970,8 +980,7 @@ bool InstanceList::commitStagedInstance(const QString& path, InstanceName const& } m_instanceGroupIndex[instID] = groupName; - if (!groupName.isEmpty()) - m_groupNameCache[groupName]++; + increaseGroupCount(groupName); } instanceSet.insert(instID); diff --git a/launcher/InstanceList.h b/launcher/InstanceList.h index 61795d86c..aaee3bb44 100644 --- a/launcher/InstanceList.h +++ b/launcher/InstanceList.h @@ -203,6 +203,9 @@ private: QList discoverInstances(); InstancePtr loadInstance(const InstanceId& id); + void increaseGroupCount(const QString& group); + void decreaseGroupCount(const QString& group); + private: int m_watchLevel = 0; int totalPlayTime = 0; From 49cebc2da48b4228d0e729298b2301c7ff281bc6 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Mon, 17 Jul 2023 12:49:12 +0100 Subject: [PATCH 101/268] More consistent behaviour Signed-off-by: TheKodeToad --- 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 70a76e218..d1ea95d1f 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -1097,9 +1097,7 @@ void MainWindow::on_actionChangeInstGroup_triggered() QString src(APPLICATION->instances()->getInstanceGroup(instId)); QStringList groups = APPLICATION->instances()->getGroups(); - if (!groups.isEmpty()) - groups.prepend(""); - + groups.prepend(""); int index = groups.indexOf(src); bool ok = false; QString dst = QInputDialog::getItem(this, tr("Group name"), tr("Enter a new group name."), groups, index, true, &ok); From 5eec0f5901a0b11c9c01fd7b086968951b792fb6 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 18 Jul 2023 23:40:03 +0300 Subject: [PATCH 102/268] feat:managed packs can update from file Signed-off-by: Trial97 --- .../ui/pages/instance/ManagedPackPage.cpp | 45 +++++++++++++++++++ launcher/ui/pages/instance/ManagedPackPage.h | 3 ++ launcher/ui/pages/instance/ManagedPackPage.ui | 13 ++++++ 3 files changed, 61 insertions(+) diff --git a/launcher/ui/pages/instance/ManagedPackPage.cpp b/launcher/ui/pages/instance/ManagedPackPage.cpp index 0fc0c9867..1dbbae0f4 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.cpp +++ b/launcher/ui/pages/instance/ManagedPackPage.cpp @@ -5,6 +5,7 @@ #include "ManagedPackPage.h" #include "ui_ManagedPackPage.h" +#include #include #include #include @@ -205,6 +206,7 @@ ModrinthManagedPackPage::ModrinthManagedPackPage(BaseInstance* inst, InstanceWin Q_ASSERT(inst->isManagedPack()); connect(ui->versionsComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(suggestVersion())); connect(ui->updateButton, &QPushButton::clicked, this, &ModrinthManagedPackPage::update); + connect(ui->updateFromFileButton, &QPushButton::clicked, this, &ModrinthManagedPackPage::updateFromFile); } // MODRINTH @@ -332,6 +334,27 @@ void ModrinthManagedPackPage::update() m_instance_window->close(); } +void ModrinthManagedPackPage::updateFromFile() +{ + auto output = QFileDialog::getOpenFileUrl(this, tr("Choose update file"), QDir::homePath(), "Modrinth pack (*.mrpack *.zip)"); + QMap extra_info; + extra_info.insert("pack_id", m_inst->getManagedPackID()); + extra_info.insert("pack_version_id", QString()); + extra_info.insert("original_instance_id", m_inst->id()); + + auto extracted = new InstanceImportTask(output, this, std::move(extra_info)); + + extracted->setName(m_inst->name()); + extracted->setGroup(APPLICATION->instances()->getInstanceGroup(m_inst->id())); + extracted->setIcon(m_inst->iconKey()); + extracted->setConfirmUpdate(false); + + auto did_succeed = runUpdateTask(extracted); + + if (m_instance_window && did_succeed) + m_instance_window->close(); +} + // FLAME FlameManagedPackPage::FlameManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent) @@ -340,6 +363,7 @@ FlameManagedPackPage::FlameManagedPackPage(BaseInstance* inst, InstanceWindow* i Q_ASSERT(inst->isManagedPack()); connect(ui->versionsComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(suggestVersion())); connect(ui->updateButton, &QPushButton::clicked, this, &FlameManagedPackPage::update); + connect(ui->updateFromFileButton, &QPushButton::clicked, this, &FlameManagedPackPage::updateFromFile); } void FlameManagedPackPage::parseManagedPack() @@ -474,4 +498,25 @@ void FlameManagedPackPage::update() m_instance_window->close(); } +void FlameManagedPackPage::updateFromFile() +{ + auto output = QFileDialog::getOpenFileUrl(this, tr("Choose update file"), QDir::homePath(), "CurseForge pack (*.zip)"); + + QMap extra_info; + extra_info.insert("pack_id", m_inst->getManagedPackID()); + extra_info.insert("pack_version_id", QString()); + extra_info.insert("original_instance_id", m_inst->id()); + + auto extracted = new InstanceImportTask(output, this, std::move(extra_info)); + + extracted->setName(m_inst->name()); + extracted->setGroup(APPLICATION->instances()->getInstanceGroup(m_inst->id())); + extracted->setIcon(m_inst->iconKey()); + extracted->setConfirmUpdate(false); + + auto did_succeed = runUpdateTask(extracted); + + if (m_instance_window && did_succeed) + m_instance_window->close(); +} #include "ManagedPackPage.moc" diff --git a/launcher/ui/pages/instance/ManagedPackPage.h b/launcher/ui/pages/instance/ManagedPackPage.h index 1ac6fc038..d77cb97b8 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.h +++ b/launcher/ui/pages/instance/ManagedPackPage.h @@ -65,6 +65,7 @@ class ManagedPackPage : public QWidget, public BasePage { virtual void suggestVersion(); virtual void update(){}; + virtual void updateFromFile(){}; protected slots: /** Does the necessary UI changes for when something failed. @@ -123,6 +124,7 @@ class ModrinthManagedPackPage final : public ManagedPackPage { void suggestVersion() override; void update() override; + void updateFromFile() override; private: NetJob::Ptr m_fetch_job = nullptr; @@ -145,6 +147,7 @@ class FlameManagedPackPage final : public ManagedPackPage { void suggestVersion() override; void update() override; + void updateFromFile() override; private: NetJob::Ptr m_fetch_job = nullptr; diff --git a/launcher/ui/pages/instance/ManagedPackPage.ui b/launcher/ui/pages/instance/ManagedPackPage.ui index bbe44a940..96317be9f 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.ui +++ b/launcher/ui/pages/instance/ManagedPackPage.ui @@ -153,6 +153,19 @@ + + + + + 0 + 0 + + + + Update from file + + + From 5d68a4c992d07cdda847764931b4924572826ad5 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 30 Jul 2023 14:43:04 -0700 Subject: [PATCH 103/268] fix: religate export zip to launcher app only Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> fix(MMCZip): include QUrl Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> fix(FileLink): drop FreeConsole Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> fix(filelink exe): add console sources Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/CMakeLists.txt | 4 +++- launcher/MMCZip.cpp | 9 ++++++++- launcher/MMCZip.h | 3 +++ launcher/filelink/FileLink.cpp | 5 ++--- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 49deda07a..c639d10d2 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -577,6 +577,9 @@ set(ATLAUNCHER_SOURCES ) set(LINKEXE_SOURCES + WindowsConsole.cpp + WindowsConsole.h + filelink/FileLink.h filelink/FileLink.cpp FileSystem.h @@ -1206,7 +1209,6 @@ endif() # Add executable add_library(Launcher_logic STATIC ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${LAUNCHER_UI} ${LAUNCHER_RESOURCES}) -target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION) target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION) target_link_libraries(Launcher_logic diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp index ad47c1d03..40790a824 100644 --- a/launcher/MMCZip.cpp +++ b/launcher/MMCZip.cpp @@ -42,7 +42,11 @@ #include #include +#include + +#if defined(LAUNCHER_APPLICATION) #include +#endif namespace MMCZip { // ours @@ -424,6 +428,7 @@ bool collectFileListRecursively(const QString& rootDir, const QString& subDir, Q return true; } +#if defined(LAUNCHER_APPLICATION) void ExportToZipTask::executeTask() { setStatus("Adding files..."); @@ -502,5 +507,7 @@ bool ExportToZipTask::abort() } return false; } +#endif + +} // namespace MMCZip -} // namespace MMCZip \ No newline at end of file diff --git a/launcher/MMCZip.h b/launcher/MMCZip.h index 163f71671..8afb12410 100644 --- a/launcher/MMCZip.h +++ b/launcher/MMCZip.h @@ -151,6 +151,8 @@ bool extractFile(QString fileCompressed, QString file, QString dir); */ bool collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList* files, FilterFunction excludeFilter); + +#if defined(LAUNCHER_APPLICATION) class ExportToZipTask : public Task { public: ExportToZipTask(QString outputPath, QDir dir, QFileInfoList files, QString destinationPrefix = "", bool followSymlinks = false) @@ -193,4 +195,5 @@ class ExportToZipTask : public Task { QFuture m_build_zip_future; QFutureWatcher m_build_zip_watcher; }; +#endif } // namespace MMCZip diff --git a/launcher/filelink/FileLink.cpp b/launcher/filelink/FileLink.cpp index 6e869bad4..35d745dbd 100644 --- a/launcher/filelink/FileLink.cpp +++ b/launcher/filelink/FileLink.cpp @@ -269,7 +269,7 @@ void FileLinkApp::runLink() FS::LinkResult result = { src_path, dst_path, QString::fromStdString(os_err.message()), os_err.value() }; m_path_results.append(result); } else { - FS::LinkResult result = { src_path, dst_path }; + FS::LinkResult result = { src_path, dst_path, "", 0}; m_path_results.append(result); } } @@ -329,7 +329,7 @@ void FileLinkApp::readPathPairs() in >> numLinks; qDebug() << "numLinks" << numLinks; - for (int i = 0; i < numLinks; i++) { + for (unsigned int i = 0; i < numLinks; i++) { FS::LinkPair pair; in >> pair.src; in >> pair.dst; @@ -352,7 +352,6 @@ FileLinkApp::~FileLinkApp() fclose(stdout); fclose(stdin); fclose(stderr); - FreeConsole(); } #endif } From 1e947ca8931f47e78e3b4fbf64e1b03ce749332c Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 30 Jul 2023 15:42:15 -0700 Subject: [PATCH 104/268] fix(flame creation task): import ApiDownload Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .../modplatform/flame/FlameInstanceCreationTask.cpp | 10 ++++++---- launcher/modplatform/flame/FlameInstanceCreationTask.h | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp index c2c7080f0..61f8d4f51 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp @@ -62,6 +62,8 @@ #include "minecraft/World.h" #include "minecraft/mod/tasks/LocalResourceParse.h" +#include "net/ApiDownload.h" + static const FlameAPI api; bool FlameCreationTask::abort() @@ -542,7 +544,7 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop) m_mod_id_resolver.reset(); connect(m_files_job.get(), &NetJob::succeeded, this, [&]() { m_files_job.reset(); - validateZIPResouces(); + validateZIPResources(); }); connect(m_files_job.get(), &NetJob::failed, [&](QString reason) { m_files_job.reset(); @@ -591,7 +593,7 @@ void FlameCreationTask::copyBlockedMods(QList const& blocked_mods) setAbortable(true); } -void FlameCreationTask::validateZIPResouces() +void FlameCreationTask::validateZIPResources() { qDebug() << "Validating whether resources stored as .zip are in the right place"; for (auto [fileName, targetFolder] : m_ZIP_resources) { @@ -644,8 +646,8 @@ void FlameCreationTask::validateZIPResouces() validatePath(fileName, targetFolder, "datapacks"); break; case PackedResourceType::ShaderPack: - // in theroy flame API can't do this but who knows, that *may* change ? - // better to handle it if it *does* occure in the future + // in theory flame API can't do this but who knows, that *may* change ? + // better to handle it if it *does* occur in the future validatePath(fileName, targetFolder, "shaderpacks"); break; case PackedResourceType::WorldSave: diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.h b/launcher/modplatform/flame/FlameInstanceCreationTask.h index 603d3693e..02ad48f2e 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.h +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.h @@ -74,7 +74,7 @@ class FlameCreationTask final : public InstanceCreationTask { void idResolverSucceeded(QEventLoop&); void setupDownloadJob(QEventLoop&); void copyBlockedMods(QList const& blocked_mods); - void validateZIPResouces(); + void validateZIPResources(); QString getVersionForLoader(QString uid, QString loaderType, QString version, QString mcVersion); private: From 14e16db01ee8827c485e111b7d5ff852b0a476aa Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 30 Jul 2023 16:02:37 -0700 Subject: [PATCH 105/268] refactor(filelink): drop moved funciton Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/filelink/FileLink.cpp | 96 ---------------------------------- 1 file changed, 96 deletions(-) diff --git a/launcher/filelink/FileLink.cpp b/launcher/filelink/FileLink.cpp index 35d745dbd..c3f9bb852 100644 --- a/launcher/filelink/FileLink.cpp +++ b/launcher/filelink/FileLink.cpp @@ -59,102 +59,6 @@ namespace fs = std::filesystem; namespace fs = ghc::filesystem; #endif -#if defined Q_OS_WIN32 - -// taken from https://stackoverflow.com/a/25927081 -// getting a proper output to console with redirection support on windows is apparently hell -void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr) -{ - // Re-initialize the C runtime "FILE" handles with clean handles bound to "nul". We do this because it has been - // observed that the file number of our standard handle file objects can be assigned internally to a value of -2 - // when not bound to a valid target, which represents some kind of unknown internal invalid state. In this state our - // call to "_dup2" fails, as it specifically tests to ensure that the target file number isn't equal to this value - // before allowing the operation to continue. We can resolve this issue by first "re-opening" the target files to - // use the "nul" device, which will place them into a valid state, after which we can redirect them to our target - // using the "_dup2" function. - if (bindStdIn) { - FILE* dummyFile; - freopen_s(&dummyFile, "nul", "r", stdin); - } - if (bindStdOut) { - FILE* dummyFile; - freopen_s(&dummyFile, "nul", "w", stdout); - } - if (bindStdErr) { - FILE* dummyFile; - freopen_s(&dummyFile, "nul", "w", stderr); - } - - // Redirect unbuffered stdin from the current standard input handle - if (bindStdIn) { - HANDLE stdHandle = GetStdHandle(STD_INPUT_HANDLE); - if (stdHandle != INVALID_HANDLE_VALUE) { - int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); - if (fileDescriptor != -1) { - FILE* file = _fdopen(fileDescriptor, "r"); - if (file != NULL) { - int dup2Result = _dup2(_fileno(file), _fileno(stdin)); - if (dup2Result == 0) { - setvbuf(stdin, NULL, _IONBF, 0); - } - } - } - } - } - - // Redirect unbuffered stdout to the current standard output handle - if (bindStdOut) { - HANDLE stdHandle = GetStdHandle(STD_OUTPUT_HANDLE); - if (stdHandle != INVALID_HANDLE_VALUE) { - int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); - if (fileDescriptor != -1) { - FILE* file = _fdopen(fileDescriptor, "w"); - if (file != NULL) { - int dup2Result = _dup2(_fileno(file), _fileno(stdout)); - if (dup2Result == 0) { - setvbuf(stdout, NULL, _IONBF, 0); - } - } - } - } - } - - // Redirect unbuffered stderr to the current standard error handle - if (bindStdErr) { - HANDLE stdHandle = GetStdHandle(STD_ERROR_HANDLE); - if (stdHandle != INVALID_HANDLE_VALUE) { - int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); - if (fileDescriptor != -1) { - FILE* file = _fdopen(fileDescriptor, "w"); - if (file != NULL) { - int dup2Result = _dup2(_fileno(file), _fileno(stderr)); - if (dup2Result == 0) { - setvbuf(stderr, NULL, _IONBF, 0); - } - } - } - } - } - - // Clear the error state for each of the C++ standard stream objects. We need to do this, as attempts to access the - // standard streams before they refer to a valid target will cause the iostream objects to enter an error state. In - // versions of Visual Studio after 2005, this seems to always occur during startup regardless of whether anything - // has been read from or written to the targets or not. - if (bindStdIn) { - std::wcin.clear(); - std::cin.clear(); - } - if (bindStdOut) { - std::wcout.clear(); - std::cout.clear(); - } - if (bindStdErr) { - std::wcerr.clear(); - std::cerr.clear(); - } -} -#endif - FileLinkApp::FileLinkApp(int& argc, char** argv) : QCoreApplication(argc, argv), socket(new QLocalSocket(this)) { #if defined Q_OS_WIN32 From d48dd7eb6a43351ed181637dae4687163f92ce92 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 4 Aug 2023 16:00:02 +0100 Subject: [PATCH 106/268] Use override-based setting for online fixes Signed-off-by: TheKodeToad --- launcher/Application.cpp | 3 ++ launcher/minecraft/MinecraftInstance.cpp | 6 ++- launcher/ui/pages/global/MinecraftPage.cpp | 5 +++ launcher/ui/pages/global/MinecraftPage.ui | 21 ++++++++- .../pages/instance/InstanceSettingsPage.cpp | 11 +++-- .../ui/pages/instance/InstanceSettingsPage.ui | 43 +++++++++++++------ 6 files changed, 69 insertions(+), 20 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index d6c135de7..bbea34c76 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -614,6 +614,9 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) // Mod loader settings m_settings->registerSetting("DisableQuiltBeacon", false); + // Legacy settings + m_settings->registerSetting("OnlineFixes", true); + // Native library workarounds m_settings->registerSetting("UseNativeOpenAL", false); m_settings->registerSetting("UseNativeGLFW", false); diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index e0e4cdcf0..b4b0b4aa3 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -191,6 +191,10 @@ void MinecraftInstance::loadSpecificSettings() auto modLoaderSettings = m_settings->registerSetting("OverrideModLoaderSettings", false); m_settings->registerOverride(global_settings->getSetting("DisableQuiltBeacon"), modLoaderSettings); + // Legacy-related options + auto legacySettings = m_settings->registerSetting("OverrideLegacySettings", true); + m_settings->registerOverride(global_settings->getSetting("OnlineFixes"), legacySettings); + m_settings->set("InstanceType", "OneSix"); } @@ -202,8 +206,6 @@ void MinecraftInstance::loadSpecificSettings() m_settings->registerSetting("UseAccountForInstance", false); m_settings->registerSetting("InstanceAccountId", ""); - m_settings->registerSetting("OnlineFixes", true); - qDebug() << "Instance-type specific settings were loaded!"; setSpecificSettingsLoaded(true); diff --git a/launcher/ui/pages/global/MinecraftPage.cpp b/launcher/ui/pages/global/MinecraftPage.cpp index 954823564..cdab4feb6 100644 --- a/launcher/ui/pages/global/MinecraftPage.cpp +++ b/launcher/ui/pages/global/MinecraftPage.cpp @@ -103,6 +103,9 @@ void MinecraftPage::applySettings() // Mod loader settings s->set("DisableQuiltBeacon", ui->disableQuiltBeaconCheckBox->isChecked()); + + // Legacy settings + s->set("OnlineFixes", ui->onlineFixes->isChecked()); } void MinecraftPage::loadSettings() @@ -143,6 +146,8 @@ void MinecraftPage::loadSettings() ui->quitAfterGameStopCheck->setChecked(s->get("QuitAfterGameStop").toBool()); ui->disableQuiltBeaconCheckBox->setChecked(s->get("DisableQuiltBeacon").toBool()); + + ui->onlineFixes->setChecked(s->get("OnlineFixes").toBool()); } void MinecraftPage::retranslate() diff --git a/launcher/ui/pages/global/MinecraftPage.ui b/launcher/ui/pages/global/MinecraftPage.ui index a3188dccb..cd0f4fafd 100644 --- a/launcher/ui/pages/global/MinecraftPage.ui +++ b/launcher/ui/pages/global/MinecraftPage.ui @@ -198,11 +198,30 @@ + + Disable Quilt loader's beacon for counting monthly active users + Disable Quilt Loader Beacon + + + + + + + + + Legacy settings + + + + - Disable Quilt loader's beacon for counting monthly active users + <html><head/><body><p>Emulates usages of old online services which are no longer operating.</p><p>This currently allows modern skins to be used.</p></body></html> + + + Enable online fixes diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index 4d3cd16dd..7b019282b 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -290,8 +290,13 @@ void InstanceSettingsPage::applySettings() m_settings->reset("DisableQuiltBeacon"); } - bool onlineFixes = ui->onlineFixes->isChecked(); - m_settings->set("OnlineFixes", onlineFixes); + bool overrideLegacySettings = ui->legacySettingsGroupBox->isChecked(); + m_settings->set("OverrideLegacySettings", overrideLegacySettings); + if (overrideLegacySettings) { + m_settings->set("OnlineFixes", ui->onlineFixes->isChecked()); + } else { + m_settings->reset("OnlineFixes"); + } // FIXME: This should probably be called by a signal instead m_instance->updateRuntimeContext(); @@ -398,8 +403,8 @@ void InstanceSettingsPage::loadSettings() ui->modLoaderSettingsGroupBox->setChecked(m_settings->get("OverrideModLoaderSettings").toBool()); ui->disableQuiltBeaconCheckBox->setChecked(m_settings->get("DisableQuiltBeacon").toBool()); + ui->legacySettingsGroupBox->setChecked(m_settings->get("OverrideLegacySettings").toBool()); ui->onlineFixes->setChecked(m_settings->get("OnlineFixes").toBool()); - ui->onlineFixes->setVisible(m_instance->traits().contains("legacyServices")); } void InstanceSettingsPage::on_javaDetectBtn_clicked() diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index 9018659aa..4548b2e72 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -543,23 +543,48 @@ + + Mod loader settings + true false - - Mod loader settings - + + Disable Quilt loader's beacon for counting monthly active users + Disable Quilt Loader Beacon + + + + + + + + + Legacy settings + + + true + + + false + + + + - Disable Quilt loader's beacon for counting monthly active users + <html><head/><body><p>Emulates usages of old online services which are no longer operating.</p><p>This currently allows modern skins to be used.</p></body></html> + + + Enable online fixes @@ -668,16 +693,6 @@ - - - - <html><head/><body><p>Fixes usages of old online services which are no longer operating by emulating them or redirecting to their modern counterparts.</p><p>This currently only allows modern skins to be used.</p></body></html> - - - Enable online fixes - - - From eeb5fdbc9f7848717a167b251ddae521f19a056e Mon Sep 17 00:00:00 2001 From: seth Date: Fri, 4 Aug 2023 11:55:31 -0400 Subject: [PATCH 107/268] feat(nix): add darwin support Signed-off-by: seth --- nix/default.nix | 5 +-- nix/distribution.nix | 1 + nix/package.nix | 89 ++++++++++++++++++++++++-------------------- 3 files changed, 51 insertions(+), 44 deletions(-) diff --git a/nix/default.nix b/nix/default.nix index 47172927a..71c95c2cf 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -25,8 +25,7 @@ systems = [ "x86_64-linux" "aarch64-linux" - # Disabled due to our packages not supporting darwin yet. - # "x86_64-darwin" - # "aarch64-darwin" + "x86_64-darwin" + "aarch64-darwin" ]; } diff --git a/nix/distribution.nix b/nix/distribution.nix index 0f2e26f3e..d0904d41d 100644 --- a/nix/distribution.nix +++ b/nix/distribution.nix @@ -17,6 +17,7 @@ mkPrism = qt: qt.callPackage ./package.nix { inherit (inputs) libnbtplusplus; + inherit (prev.darwin.apple_sdk.frameworks) Cocoa; inherit self version; }; in { diff --git a/nix/package.nix b/nix/package.nix index edc266dc4..1dbadd405 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -2,6 +2,8 @@ lib, stdenv, cmake, + cmark, + Cocoa, ninja, jdk17, zlib, @@ -9,57 +11,62 @@ quazip, extra-cmake-modules, tomlplusplus, - cmark, ghc_filesystem, gamemode, msaClientID ? null, - gamemodeSupport ? true, + gamemodeSupport ? stdenv.isLinux, self, version, libnbtplusplus, }: -stdenv.mkDerivation rec { - pname = "prismlauncher-unwrapped"; - inherit version; +assert lib.assertMsg (stdenv.isLinux || !gamemodeSupport) "gamemodeSupport is only available on Linux"; + stdenv.mkDerivation rec { + pname = "prismlauncher-unwrapped"; + inherit version; - src = lib.cleanSource self; + src = lib.cleanSource self; - nativeBuildInputs = [extra-cmake-modules cmake jdk17 ninja]; - buildInputs = - [ - qtbase - zlib - quazip - ghc_filesystem - tomlplusplus - cmark - ] - ++ lib.optional gamemodeSupport gamemode; + nativeBuildInputs = [extra-cmake-modules cmake jdk17 ninja]; + buildInputs = + [ + qtbase + zlib + quazip + ghc_filesystem + tomlplusplus + cmark + ] + ++ lib.optional gamemodeSupport gamemode + ++ lib.optionals stdenv.isDarwin [Cocoa]; - hardeningEnable = ["pie"]; + hardeningEnable = lib.optionals stdenv.isLinux ["pie"]; - cmakeFlags = - lib.optionals (msaClientID != null) ["-DLauncher_MSA_CLIENT_ID=${msaClientID}"] - ++ lib.optionals (lib.versionOlder qtbase.version "6") ["-DLauncher_QT_VERSION_MAJOR=5"]; + cmakeFlags = + [ + "-DLauncher_BUILD_PLATFORM=nixpkgs" + ] + ++ lib.optionals (msaClientID != null) ["-DLauncher_MSA_CLIENT_ID=${msaClientID}"] + ++ lib.optionals (lib.versionOlder qtbase.version "6") ["-DLauncher_QT_VERSION_MAJOR=5"] + ++ lib.optionals stdenv.isDarwin ["-DINSTALL_BUNDLE=nodeps" "-DMACOSX_SPARKLE_UPDATE_FEED_URL=''"]; - postUnpack = '' - rm -rf source/libraries/libnbtplusplus - ln -s ${libnbtplusplus} source/libraries/libnbtplusplus - ''; - - dontWrapQtApps = true; - - meta = with lib; { - homepage = "https://prismlauncher.org/"; - 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. + postUnpack = '' + rm -rf source/libraries/libnbtplusplus + ln -s ${libnbtplusplus} source/libraries/libnbtplusplus ''; - platforms = platforms.linux; - changelog = "https://github.com/PrismLauncher/PrismLauncher/releases/tag/${version}"; - license = licenses.gpl3Only; - maintainers = with maintainers; [minion3665 Scrumplex]; - }; -} + + dontWrapQtApps = true; + + meta = with lib; { + homepage = "https://prismlauncher.org/"; + 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 = with platforms; linux ++ darwin; + changelog = "https://github.com/PrismLauncher/PrismLauncher/releases/tag/${version}"; + license = licenses.gpl3Only; + maintainers = with maintainers; [minion3665 Scrumplex getchoo]; + }; + } From 71890707c034a0503a92128335c13773e51dc43b Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 12 Aug 2023 12:54:10 +0300 Subject: [PATCH 108/268] format and apply the sugestion Signed-off-by: Trial97 --- launcher/modplatform/ModIndex.cpp | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/launcher/modplatform/ModIndex.cpp b/launcher/modplatform/ModIndex.cpp index 2e0d9e37a..04fd42a7a 100644 --- a/launcher/modplatform/ModIndex.cpp +++ b/launcher/modplatform/ModIndex.cpp @@ -24,14 +24,11 @@ namespace ModPlatform { -static const QMap s_indexed_version_type_names = { - {"release", IndexedVersionType::Enum::Release}, - {"beta", IndexedVersionType::Enum::Beta}, - {"alpha", IndexedVersionType::Enum::Alpha} -}; +static const QMap s_indexed_version_type_names = { { "release", IndexedVersionType::Enum::Release }, + { "beta", IndexedVersionType::Enum::Beta }, + { "alpha", IndexedVersionType::Enum::Alpha } }; -IndexedVersionType::IndexedVersionType(const QString& type): IndexedVersionType(enumFromString(type)) -{} +IndexedVersionType::IndexedVersionType(const QString& type) : IndexedVersionType(enumFromString(type)) {} IndexedVersionType::IndexedVersionType(int type) { @@ -60,13 +57,13 @@ IndexedVersionType::IndexedVersionType(const IndexedVersionType& other) m_type = other.m_type; } -IndexedVersionType& IndexedVersionType::operator=(const IndexedVersionType& other) +IndexedVersionType& IndexedVersionType::operator=(const IndexedVersionType& other) { m_type = other.m_type; return *this; } -const QString IndexedVersionType::toString (const IndexedVersionType::Enum& type) +const QString IndexedVersionType::toString(const IndexedVersionType::Enum& type) { switch (type) { case IndexedVersionType::Enum::Release: @@ -78,18 +75,12 @@ const QString IndexedVersionType::toString (const IndexedVersionType::Enum& type case IndexedVersionType::Enum::UNKNOWN: default: return "unknown"; - } } IndexedVersionType::Enum IndexedVersionType::enumFromString(const QString& type) { - auto found = s_indexed_version_type_names.constFind(type); - if (found != s_indexed_version_type_names.constEnd()) { - return *found; - } else { - return IndexedVersionType::Enum::UNKNOWN; - } + return s_indexed_version_type_names.value(type, IndexedVersionType::Enum::UNKNOWN); } auto ProviderCapabilities::name(ResourceProvider p) -> const char* From 5056a51c188ef366d5ea13b6794dd14b11f78075 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sat, 12 Aug 2023 21:18:22 +0100 Subject: [PATCH 109/268] Improvements and refinements to pack export - Persist fields - Toggle optional files Signed-off-by: TheKodeToad --- launcher/minecraft/MinecraftInstance.cpp | 6 ++ .../modplatform/flame/FlamePackExportTask.cpp | 4 +- .../modplatform/flame/FlamePackExportTask.h | 2 + .../modrinth/ModrinthPackExportTask.cpp | 24 +++--- .../modrinth/ModrinthPackExportTask.h | 2 + launcher/ui/dialogs/ExportPackDialog.cpp | 71 +++++++++------ launcher/ui/dialogs/ExportPackDialog.ui | 86 ++++++++++++------- 7 files changed, 129 insertions(+), 66 deletions(-) diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 305bff67b..c754640bb 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -194,6 +194,12 @@ void MinecraftInstance::loadSpecificSettings() m_settings->registerSetting("UseAccountForInstance", false); m_settings->registerSetting("InstanceAccountId", ""); + m_settings->registerSetting("ExportName", ""); + m_settings->registerSetting("ExportVersion", "1.0.0"); + m_settings->registerSetting("ExportSummary", ""); + m_settings->registerSetting("ExportAuthor", ""); + m_settings->registerSetting("ExportOptionalFiles", true); + qDebug() << "Instance-type specific settings were loaded!"; setSpecificSettingsLoaded(true); diff --git a/launcher/modplatform/flame/FlamePackExportTask.cpp b/launcher/modplatform/flame/FlamePackExportTask.cpp index f5f3af372..6cfc078ad 100644 --- a/launcher/modplatform/flame/FlamePackExportTask.cpp +++ b/launcher/modplatform/flame/FlamePackExportTask.cpp @@ -43,12 +43,14 @@ const QStringList FlamePackExportTask::FILE_EXTENSIONS({ "jar", "zip" }); FlamePackExportTask::FlamePackExportTask(const QString& name, const QString& version, const QString& author, + bool optionalFiles, InstancePtr instance, const QString& output, MMCZip::FilterFunction filter) : name(name) , version(version) , author(author) + , optionalFiles(optionalFiles) , instance(instance) , mcInstance(dynamic_cast(instance.get())) , gameRoot(instance->gameRoot()) @@ -407,7 +409,7 @@ QByteArray FlamePackExportTask::generateIndex() QJsonObject file; file["projectID"] = mod.addonId; file["fileID"] = mod.version; - file["required"] = mod.enabled; + file["required"] = mod.enabled || !optionalFiles; files << file; } obj["files"] = files; diff --git a/launcher/modplatform/flame/FlamePackExportTask.h b/launcher/modplatform/flame/FlamePackExportTask.h index d3dc6281e..78b46e91f 100644 --- a/launcher/modplatform/flame/FlamePackExportTask.h +++ b/launcher/modplatform/flame/FlamePackExportTask.h @@ -30,6 +30,7 @@ class FlamePackExportTask : public Task { FlamePackExportTask(const QString& name, const QString& version, const QString& author, + bool optionalFiles, InstancePtr instance, const QString& output, MMCZip::FilterFunction filter); @@ -44,6 +45,7 @@ class FlamePackExportTask : public Task { // inputs const QString name, version, author; + const bool optionalFiles; const InstancePtr instance; MinecraftInstance* mcInstance; const QDir gameRoot; diff --git a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp index 7bf296398..ffd215018 100644 --- a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp @@ -33,12 +33,14 @@ const QStringList ModrinthPackExportTask::FILE_EXTENSIONS({ "jar", "litemod", "z ModrinthPackExportTask::ModrinthPackExportTask(const QString& name, const QString& version, const QString& summary, + bool optionalFiles, InstancePtr instance, const QString& output, MMCZip::FilterFunction filter) : name(name) , version(version) , summary(summary) + , optionalFiles(optionalFiles) , instance(instance) , mcInstance(dynamic_cast(instance.get())) , gameRoot(instance->gameRoot()) @@ -267,16 +269,18 @@ QByteArray ModrinthPackExportTask::generateIndex() QString path = iterator.key(); const ResolvedFile& value = iterator.value(); - // detect disabled mod - const QFileInfo pathInfo(path); - if (pathInfo.suffix() == "disabled") { - // rename it - path = pathInfo.dir().filePath(pathInfo.completeBaseName()); - // ...and make it optional - QJsonObject env; - env["client"] = "optional"; - env["server"] = "optional"; - fileOut["env"] = env; + if (optionalFiles) { + // detect disabled mod + const QFileInfo pathInfo(path); + if (pathInfo.suffix() == "disabled") { + // rename it + path = pathInfo.dir().filePath(pathInfo.completeBaseName()); + // ...and make it optional + QJsonObject env; + env["client"] = "optional"; + env["server"] = "optional"; + fileOut["env"] = env; + } } fileOut["path"] = path; diff --git a/launcher/modplatform/modrinth/ModrinthPackExportTask.h b/launcher/modplatform/modrinth/ModrinthPackExportTask.h index 1f9e0eb77..83540dfac 100644 --- a/launcher/modplatform/modrinth/ModrinthPackExportTask.h +++ b/launcher/modplatform/modrinth/ModrinthPackExportTask.h @@ -31,6 +31,7 @@ class ModrinthPackExportTask : public Task { ModrinthPackExportTask(const QString& name, const QString& version, const QString& summary, + bool optionalFiles, InstancePtr instance, const QString& output, MMCZip::FilterFunction filter); @@ -50,6 +51,7 @@ class ModrinthPackExportTask : public Task { // inputs const QString name, version, summary; + const bool optionalFiles; const InstancePtr instance; MinecraftInstance* mcInstance; const QDir gameRoot; diff --git a/launcher/ui/dialogs/ExportPackDialog.cpp b/launcher/ui/dialogs/ExportPackDialog.cpp index ad8db5ffb..0a97ee13c 100644 --- a/launcher/ui/dialogs/ExportPackDialog.cpp +++ b/launcher/ui/dialogs/ExportPackDialog.cpp @@ -37,15 +37,21 @@ ExportPackDialog::ExportPackDialog(InstancePtr instance, QWidget* parent, ModPlatform::ResourceProvider provider) : QDialog(parent), instance(instance), ui(new Ui::ExportPackDialog), m_provider(provider) { + Q_ASSERT(m_provider == ModPlatform::ResourceProvider::MODRINTH || m_provider == ModPlatform::ResourceProvider::FLAME); + ui->setupUi(this); - ui->name->setText(instance->name()); + ui->name->setPlaceholderText(instance->name()); + ui->name->setText(instance->settings()->get("ExportName").toString()); + ui->version->setText(instance->settings()->get("ExportVersion").toString()); + ui->optionalFiles->setChecked(instance->settings()->get("ExportOptionalFiles").toBool()); + if (m_provider == ModPlatform::ResourceProvider::MODRINTH) { - ui->summary->setText(instance->notes().split(QRegularExpression("\\r?\\n"))[0]); - setWindowTitle("Export Modrinth Pack"); + setWindowTitle(tr("Export Modrinth Pack")); + ui->summary->setText(instance->settings()->get("ExportSummary").toString()); } else { - setWindowTitle("Export CurseForge Pack"); - ui->version->setText(""); - ui->summaryLabel->setText("Author"); + setWindowTitle(tr("Export CurseForge Pack")); + ui->summaryLabel->setText(tr("&Author")); + ui->summary->setText(instance->settings()->get("ExportAuthor").toString()); } // ensure a valid pack is generated @@ -81,14 +87,14 @@ ExportPackDialog::ExportPackDialog(InstancePtr instance, QWidget* parent, ModPla proxy->blockedPaths().insert(root.relativeFilePath(index.absolutePath())); } - ui->treeView->setModel(proxy); - ui->treeView->setRootIndex(proxy->mapFromSource(model->index(instance->gameRoot()))); - ui->treeView->sortByColumn(0, Qt::AscendingOrder); + ui->files->setModel(proxy); + ui->files->setRootIndex(proxy->mapFromSource(model->index(instance->gameRoot()))); + ui->files->sortByColumn(0, Qt::AscendingOrder); model->setFilter(filter); model->setRootPath(instance->gameRoot()); - QHeaderView* headerView = ui->treeView->header(); + QHeaderView* headerView = ui->files->header(); headerView->setSectionResizeMode(QHeaderView::ResizeToContents); headerView->setSectionResizeMode(0, QHeaderView::Stretch); } @@ -100,26 +106,40 @@ ExportPackDialog::~ExportPackDialog() void ExportPackDialog::done(int result) { + auto settings = instance->settings(); + settings->set("ExportName", ui->name->text()); + settings->set("ExportVersion", ui->version->text()); + settings->set(m_provider == ModPlatform::ResourceProvider::FLAME ? "ExportAuthor" : "ExportSummary", ui->summary->text()); + settings->set("ExportOptionalFiles", ui->optionalFiles->isChecked()); + if (result == Accepted) { - const QString filename = FS::RemoveInvalidFilenameChars(ui->name->text()); + const QString name = ui->name->text().isEmpty() ? instance->name() : ui->name->text(); + const QString filename = FS::RemoveInvalidFilenameChars(name); + QString output; - if (m_provider == ModPlatform::ResourceProvider::MODRINTH) - output = QFileDialog::getSaveFileName(this, tr("Export %1").arg(ui->name->text()), - FS::PathCombine(QDir::homePath(), filename + ".mrpack"), "Modrinth pack (*.mrpack *.zip)", - nullptr); - else - output = QFileDialog::getSaveFileName(this, tr("Export %1").arg(ui->name->text()), - FS::PathCombine(QDir::homePath(), filename + ".zip"), "CurseForge pack (*.zip)", nullptr); + if (m_provider == ModPlatform::ResourceProvider::MODRINTH) { + output = QFileDialog::getSaveFileName(this, tr("Export %1").arg(name), FS::PathCombine(QDir::homePath(), filename + ".mrpack"), + "Modrinth pack (*.mrpack *.zip)", nullptr); + if (!(output.endsWith(".zip") || output.endsWith(".mrpack"))) + output.append(".mrpack"); + } else { + output = QFileDialog::getSaveFileName(this, tr("Export %1").arg(name), FS::PathCombine(QDir::homePath(), filename + ".zip"), + "CurseForge pack (*.zip)", nullptr); + if (!output.endsWith(".zip")) + output.append(".zip"); + } if (output.isEmpty()) return; + Task* task; - if (m_provider == ModPlatform::ResourceProvider::MODRINTH) - task = new ModrinthPackExportTask(ui->name->text(), ui->version->text(), ui->summary->text(), instance, output, - std::bind(&FileIgnoreProxy::filterFile, proxy, std::placeholders::_1)); - else - task = new FlamePackExportTask(ui->name->text(), ui->version->text(), ui->summary->text(), instance, output, + if (m_provider == ModPlatform::ResourceProvider::MODRINTH) { + task = new ModrinthPackExportTask(name, ui->version->text(), ui->summary->text(), ui->optionalFiles->isChecked(), instance, + output, std::bind(&FileIgnoreProxy::filterFile, proxy, std::placeholders::_1)); + } else { + task = new FlamePackExportTask(name, ui->version->text(), ui->summary->text(), ui->optionalFiles->isChecked(), instance, output, std::bind(&FileIgnoreProxy::filterFile, proxy, std::placeholders::_1)); + } connect(task, &Task::failed, [this](const QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); }); @@ -140,7 +160,6 @@ void ExportPackDialog::done(int result) void ExportPackDialog::validate() { - const bool invalid = - ui->name->text().isEmpty() || ((m_provider == ModPlatform::ResourceProvider::MODRINTH) && ui->version->text().isEmpty()); - ui->buttonBox->button(QDialogButtonBox::Ok)->setDisabled(invalid); + ui->buttonBox->button(QDialogButtonBox::Ok) + ->setDisabled(m_provider == ModPlatform::ResourceProvider::MODRINTH && ui->version->text().isEmpty()); } diff --git a/launcher/ui/dialogs/ExportPackDialog.ui b/launcher/ui/dialogs/ExportPackDialog.ui index 3976e28f8..09dea72a8 100644 --- a/launcher/ui/dialogs/ExportPackDialog.ui +++ b/launcher/ui/dialogs/ExportPackDialog.ui @@ -7,12 +7,9 @@ 0 0 650 - 413 + 510 - - Export Pack - true @@ -20,13 +17,16 @@ - Information + &Description - Summary + &Summary + + + summary @@ -36,14 +36,20 @@ - Name + &Name + + + name - Version + &Version + + + version @@ -57,31 +63,52 @@ - - - - Files + + + &Options - - - - - - true - - - QAbstractItemView::ExtendedSelection - - - true - - - false - + + + + + &Files + + + files + + + + + + + true + + + QAbstractItemView::ExtendedSelection + + + true + + + false + + + + + + + &Mark disabled files as optional + + + true + + + + @@ -97,7 +124,8 @@ name version summary - treeView + files + optionalFiles From ff67fd10c33fa99423b5a43dcbd30494b4c40dc5 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Mon, 17 Jul 2023 10:57:41 +0200 Subject: [PATCH 110/268] feat: implement override for GLFW/OpenAL with split natives Fixes PrismLauncher/PrismLauncher#513 Signed-off-by: Sefa Eyeoglu --- launcher/MangoHud.cpp | 43 ++++++++++++++++++++ launcher/MangoHud.h | 4 +- launcher/minecraft/MinecraftInstance.cpp | 18 ++++++++ launcher/minecraft/launch/ExtractNatives.cpp | 10 ++--- 4 files changed, 68 insertions(+), 7 deletions(-) diff --git a/launcher/MangoHud.cpp b/launcher/MangoHud.cpp index 5758da3aa..ab79f418b 100644 --- a/launcher/MangoHud.cpp +++ b/launcher/MangoHud.cpp @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +#include #include #include #include @@ -26,6 +27,15 @@ #include "Json.h" #include "MangoHud.h" +#ifdef __GLIBC__ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#define UNDEF_GNU_SOURCE +#endif +#include +#include +#endif + namespace MangoHud { QString getLibraryString() @@ -106,4 +116,37 @@ QString getLibraryString() return QString(); } + +QString findLibrary(QString libName) +{ +#ifdef __GLIBC__ + const char* library = libName.toLocal8Bit().constData(); + + void* handle = dlopen(library, RTLD_NOW); + if (!handle) { + qCritical() << "dlopen() failed:" << dlerror(); + return {}; + } + + char path[PATH_MAX]; + if (dlinfo(handle, RTLD_DI_ORIGIN, path) == -1) { + qCritical() << "dlinfo() failed:" << dlerror(); + dlclose(handle); + return {}; + } + + auto fullPath = FS::PathCombine(QString(path), libName); + + dlclose(handle); + return fullPath; +#else + qWarning() << "MangoHud::findLibrary is not implemented on this platform"; + return {}; +#endif +} } // namespace MangoHud + +#ifdef UNDEF_GNU_SOURCE +#undef _GNU_SOURCE +#undef UNDEF_GNU_SOURCE +#endif diff --git a/launcher/MangoHud.h b/launcher/MangoHud.h index 7b7c2849c..5361999b4 100644 --- a/launcher/MangoHud.h +++ b/launcher/MangoHud.h @@ -24,4 +24,6 @@ namespace MangoHud { QString getLibraryString(); -} + +QString findLibrary(QString libName); +} // namespace MangoHud diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 305bff67b..40fde6cb3 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -389,6 +389,24 @@ QStringList MinecraftInstance::extraArguments() if (loaders.has_value() && loaders.value() & ResourceAPI::Quilt && settings()->get("DisableQuiltBeacon").toBool()) list.append("-Dloader.disable_beacon=true"); } + +#ifdef Q_OS_LINUX + { + QString openALPath; + QString glfwPath; + + if (settings()->get("UseNativeOpenAL").toBool()) + openALPath = MangoHud::findLibrary("libopenal.so"); + if (settings()->get("UseNativeGLFW").toBool()) + glfwPath = MangoHud::findLibrary("libglfw.so"); + + if (!openALPath.isEmpty()) + list.append("-Dorg.lwjgl.openal.libname=" + openALPath); + if (!glfwPath.isEmpty()) + list.append("-Dorg.lwjgl.glfw.libname=" + glfwPath); + } +#endif + return list; } diff --git a/launcher/minecraft/launch/ExtractNatives.cpp b/launcher/minecraft/launch/ExtractNatives.cpp index cebeaedd4..1aa4dccc4 100644 --- a/launcher/minecraft/launch/ExtractNatives.cpp +++ b/launcher/minecraft/launch/ExtractNatives.cpp @@ -39,7 +39,7 @@ static QString replaceSuffix(QString target, const QString& suffix, const QStrin return target + replacement; } -static bool unzipNatives(QString source, QString targetFolder, bool applyJnilibHack, bool nativeOpenAL, bool nativeGLFW) +static bool unzipNatives(QString source, QString targetFolder, bool applyJnilibHack, bool nativeOpenAL) { QuaZip zip(source); if (!zip.open(QuaZip::mdUnzip)) { @@ -52,9 +52,6 @@ static bool unzipNatives(QString source, QString targetFolder, bool applyJnilibH do { QString name = zip.getCurrentFileName(); auto lowercase = name.toLower(); - if (nativeGLFW && name.contains("glfw")) { - continue; - } if (nativeOpenAL && name.contains("openal")) { continue; } @@ -83,14 +80,15 @@ void ExtractNatives::executeTask() return; } auto settings = minecraftInstance->settings(); + + // We only need OpenAL here, as modern versions of LWJGL (3+) are handled by JVM args, while older versions (2) didn't have GLFW bool nativeOpenAL = settings->get("UseNativeOpenAL").toBool(); - bool nativeGLFW = settings->get("UseNativeGLFW").toBool(); auto outputPath = minecraftInstance->getNativePath(); auto javaVersion = minecraftInstance->getJavaVersion(); bool jniHackEnabled = javaVersion.major() >= 8; for (const auto& source : toExtract) { - if (!unzipNatives(source, outputPath, jniHackEnabled, nativeOpenAL, nativeGLFW)) { + if (!unzipNatives(source, outputPath, jniHackEnabled, nativeOpenAL)) { const char* reason = QT_TR_NOOP("Couldn't extract native jar '%1' to destination '%2'"); emit logLine(QString(reason).arg(source, outputPath), MessageLevel::Fatal); emitFailed(tr(reason).arg(source, outputPath)); From 83aa0062c7d831ca42ed4d82e5f6162ac8793be8 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Wed, 2 Aug 2023 13:45:08 +0200 Subject: [PATCH 111/268] feat: add custom native library path settings Signed-off-by: Sefa Eyeoglu --- CMakeLists.txt | 12 +++++ buildconfig/BuildConfig.cpp.in | 3 ++ buildconfig/BuildConfig.h | 3 ++ launcher/Application.cpp | 13 ++++++ launcher/Application.h | 4 ++ launcher/minecraft/MinecraftInstance.cpp | 20 ++++++--- launcher/ui/pages/global/MinecraftPage.cpp | 35 ++++++++++++++- launcher/ui/pages/global/MinecraftPage.h | 3 ++ launcher/ui/pages/global/MinecraftPage.ui | 40 +++++++++++++++-- .../pages/instance/InstanceSettingsPage.cpp | 34 +++++++++++++- .../ui/pages/instance/InstanceSettingsPage.h | 3 ++ .../ui/pages/instance/InstanceSettingsPage.ui | 44 ++++++++++++++++--- 12 files changed, 196 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a2853cb5a..a8dd7a73d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -216,6 +216,18 @@ set(Launcher_SUBREDDIT_URL "https://prismlauncher.org/reddit" CACHE STRING "URL set(Launcher_FORCE_BUNDLED_LIBS OFF CACHE BOOL "Prevent using system libraries, if they are available as submodules") set(Launcher_QT_VERSION_MAJOR "6" CACHE STRING "Major Qt version to build against") +# Native libraries +if(UNIX AND APPLE) + set(Launcher_GLFW_LIBRARY_NAME "libglfw.dylib" CACHE STRING "Name of native glfw library") + set(Launcher_OPENAL_LIBRARY_NAME "libopenal.dylib" CACHE STRING "Name of native glfw library") +elseif(UNIX) + set(Launcher_GLFW_LIBRARY_NAME "libglfw.so" CACHE STRING "Name of native glfw library") + set(Launcher_OPENAL_LIBRARY_NAME "libopenal.so" CACHE STRING "Name of native glfw library") +elseif(WIN32) + set(Launcher_GLFW_LIBRARY_NAME "glfw.dll" CACHE STRING "Name of native glfw library") + set(Launcher_OPENAL_LIBRARY_NAME "OpenAL.dll" CACHE STRING "Name of native glfw library") +endif() + # API Keys # NOTE: These API keys are here for convenience. If you rebrand this software or intend to break the terms of service # of these platforms, please change these API keys beforehand. diff --git a/buildconfig/BuildConfig.cpp.in b/buildconfig/BuildConfig.cpp.in index d7662a7a4..1eb0022b8 100644 --- a/buildconfig/BuildConfig.cpp.in +++ b/buildconfig/BuildConfig.cpp.in @@ -110,6 +110,9 @@ Config::Config() FLAME_API_KEY = "@Launcher_CURSEFORGE_API_KEY@"; META_URL = "@Launcher_META_URL@"; + GLFW_LIBRARY_NAME = "@Launcher_GLFW_LIBRARY_NAME@"; + OPENAL_LIBRARY_NAME = "@Launcher_OPENAL_LIBRARY_NAME@"; + BUG_TRACKER_URL = "@Launcher_BUG_TRACKER_URL@"; TRANSLATIONS_URL = "@Launcher_TRANSLATIONS_URL@"; MATRIX_URL = "@Launcher_MATRIX_URL@"; diff --git a/buildconfig/BuildConfig.h b/buildconfig/BuildConfig.h index 387f494f3..a5649b98f 100644 --- a/buildconfig/BuildConfig.h +++ b/buildconfig/BuildConfig.h @@ -134,6 +134,9 @@ class Config { */ QString META_URL; + QString GLFW_LIBRARY_NAME; + QString OPENAL_LIBRARY_NAME; + QString BUG_TRACKER_URL; QString TRANSLATIONS_URL; QString MATRIX_URL; diff --git a/launcher/Application.cpp b/launcher/Application.cpp index a13935101..6daa5a84b 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -582,7 +582,9 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) // Native library workarounds m_settings->registerSetting("UseNativeOpenAL", false); + m_settings->registerSetting("CustomOpenALPath", ""); m_settings->registerSetting("UseNativeGLFW", false); + m_settings->registerSetting("CustomGLFWPath", ""); // Peformance related options m_settings->registerSetting("EnableFeralGamemode", false); @@ -842,6 +844,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) updateCapabilities(); + detectLibraries(); + if (createSetupWizard()) { return; } @@ -1412,6 +1416,15 @@ void Application::updateCapabilities() #endif } +void Application::detectLibraries() +{ +#ifdef Q_OS_LINUX + m_detectedGLFWPath = MangoHud::findLibrary(BuildConfig.GLFW_LIBRARY_NAME); + m_detectedOpenALPath = MangoHud::findLibrary(BuildConfig.OPENAL_LIBRARY_NAME); + qDebug() << m_detectedGLFWPath << m_detectedOpenALPath; +#endif +} + QString Application::getJarPath(QString jarFile) { QStringList potentialPaths = { diff --git a/launcher/Application.h b/launcher/Application.h index 6bc332749..5807107cf 100644 --- a/launcher/Application.h +++ b/launcher/Application.h @@ -142,6 +142,8 @@ class Application : public QApplication { void updateCapabilities(); + void detectLibraries(); + /*! * Finds and returns the full path to a jar file. * Returns a null-string if it could not be found. @@ -275,6 +277,8 @@ class Application : public QApplication { SetupWizard* m_setupWizard = nullptr; public: + QString m_detectedGLFWPath; + QString m_detectedOpenALPath; QString m_instanceIdToLaunch; QString m_serverToJoin; QString m_profileToUse; diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 40fde6cb3..bfc4ad98a 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -166,7 +166,9 @@ void MinecraftInstance::loadSpecificSettings() // Native library workarounds auto nativeLibraryWorkaroundsOverride = m_settings->registerSetting("OverrideNativeWorkarounds", false); m_settings->registerOverride(global_settings->getSetting("UseNativeOpenAL"), nativeLibraryWorkaroundsOverride); + m_settings->registerOverride(global_settings->getSetting("CustomOpenALPath"), nativeLibraryWorkaroundsOverride); m_settings->registerOverride(global_settings->getSetting("UseNativeGLFW"), nativeLibraryWorkaroundsOverride); + m_settings->registerOverride(global_settings->getSetting("CustomGLFWPath"), nativeLibraryWorkaroundsOverride); // Peformance related options auto performanceOverride = m_settings->registerSetting("OverridePerformance", false); @@ -390,22 +392,28 @@ QStringList MinecraftInstance::extraArguments() list.append("-Dloader.disable_beacon=true"); } -#ifdef Q_OS_LINUX { QString openALPath; QString glfwPath; - if (settings()->get("UseNativeOpenAL").toBool()) - openALPath = MangoHud::findLibrary("libopenal.so"); - if (settings()->get("UseNativeGLFW").toBool()) - glfwPath = MangoHud::findLibrary("libglfw.so"); + if (settings()->get("UseNativeOpenAL").toBool()) { + auto customPath = settings()->get("CustomOpenALPath").toString(); + openALPath = APPLICATION->m_detectedOpenALPath; + if (!customPath.isEmpty()) + openALPath = customPath; + } + if (settings()->get("UseNativeGLFW").toBool()) { + auto customPath = settings()->get("CustomGLFWPath").toString(); + glfwPath = APPLICATION->m_detectedGLFWPath; + if (!customPath.isEmpty()) + glfwPath = customPath; + } if (!openALPath.isEmpty()) list.append("-Dorg.lwjgl.openal.libname=" + openALPath); if (!glfwPath.isEmpty()) list.append("-Dorg.lwjgl.glfw.libname=" + glfwPath); } -#endif return list; } diff --git a/launcher/ui/pages/global/MinecraftPage.cpp b/launcher/ui/pages/global/MinecraftPage.cpp index 866a4121c..1c7747210 100644 --- a/launcher/ui/pages/global/MinecraftPage.cpp +++ b/launcher/ui/pages/global/MinecraftPage.cpp @@ -35,6 +35,7 @@ */ #include "MinecraftPage.h" +#include "BuildConfig.h" #include "ui_MinecraftPage.h" #include @@ -44,9 +45,15 @@ #include "Application.h" #include "settings/SettingsObject.h" +#ifdef Q_OS_LINUX +#include "MangoHud.h" +#endif + MinecraftPage::MinecraftPage(QWidget* parent) : QWidget(parent), ui(new Ui::MinecraftPage) { ui->setupUi(this); + connect(ui->useNativeGLFWCheck, &QAbstractButton::toggled, this, &MinecraftPage::onUseNativeGLFWChanged); + connect(ui->useNativeOpenALCheck, &QAbstractButton::toggled, this, &MinecraftPage::onUseNativeOpenALChanged); loadSettings(); updateCheckboxStuff(); } @@ -74,6 +81,16 @@ void MinecraftPage::on_maximizedCheckBox_clicked(bool checked) updateCheckboxStuff(); } +void MinecraftPage::onUseNativeGLFWChanged(bool checked) +{ + ui->lineEditGLFWPath->setEnabled(checked); +} + +void MinecraftPage::onUseNativeOpenALChanged(bool checked) +{ + ui->lineEditOpenALPath->setEnabled(checked); +} + void MinecraftPage::applySettings() { auto s = APPLICATION->settings(); @@ -84,8 +101,10 @@ void MinecraftPage::applySettings() s->set("MinecraftWinHeight", ui->windowHeightSpinBox->value()); // Native library workarounds - s->set("UseNativeOpenAL", ui->useNativeOpenALCheck->isChecked()); s->set("UseNativeGLFW", ui->useNativeGLFWCheck->isChecked()); + s->set("CustomGLFWPath", ui->lineEditGLFWPath->text()); + s->set("UseNativeOpenAL", ui->useNativeOpenALCheck->isChecked()); + s->set("CustomOpenALPath", ui->lineEditOpenALPath->text()); // Peformance related options s->set("EnableFeralGamemode", ui->enableFeralGamemodeCheck->isChecked()); @@ -114,8 +133,20 @@ void MinecraftPage::loadSettings() ui->windowWidthSpinBox->setValue(s->get("MinecraftWinWidth").toInt()); ui->windowHeightSpinBox->setValue(s->get("MinecraftWinHeight").toInt()); - ui->useNativeOpenALCheck->setChecked(s->get("UseNativeOpenAL").toBool()); ui->useNativeGLFWCheck->setChecked(s->get("UseNativeGLFW").toBool()); + ui->lineEditGLFWPath->setText(s->get("CustomGLFWPath").toString()); + ui->lineEditGLFWPath->setPlaceholderText(tr("Path to %1 library file").arg(BuildConfig.GLFW_LIBRARY_NAME)); +#ifdef Q_OS_LINUX + if (!APPLICATION->m_detectedGLFWPath.isEmpty()) + ui->lineEditGLFWPath->setPlaceholderText(tr("Auto detected path: %1").arg(APPLICATION->m_detectedGLFWPath)); +#endif + ui->useNativeOpenALCheck->setChecked(s->get("UseNativeOpenAL").toBool()); + ui->lineEditOpenALPath->setText(s->get("CustomOpenALPath").toString()); + ui->lineEditOpenALPath->setPlaceholderText(tr("Path to %1 library file").arg(BuildConfig.OPENAL_LIBRARY_NAME)); +#ifdef Q_OS_LINUX + if (!APPLICATION->m_detectedOpenALPath.isEmpty()) + ui->lineEditOpenALPath->setPlaceholderText(tr("Auto detected path: %1").arg(APPLICATION->m_detectedOpenALPath)); +#endif ui->enableFeralGamemodeCheck->setChecked(s->get("EnableFeralGamemode").toBool()); ui->enableMangoHud->setChecked(s->get("EnableMangoHud").toBool()); diff --git a/launcher/ui/pages/global/MinecraftPage.h b/launcher/ui/pages/global/MinecraftPage.h index 28c31b5d8..5facfbb3f 100644 --- a/launcher/ui/pages/global/MinecraftPage.h +++ b/launcher/ui/pages/global/MinecraftPage.h @@ -70,6 +70,9 @@ class MinecraftPage : public QWidget, public BasePage { private slots: void on_maximizedCheckBox_clicked(bool checked); + void onUseNativeGLFWChanged(bool checked); + void onUseNativeOpenALChanged(bool checked); + private: Ui::MinecraftPage* ui; }; diff --git a/launcher/ui/pages/global/MinecraftPage.ui b/launcher/ui/pages/global/MinecraftPage.ui index 393b0f358..98e90e4ec 100644 --- a/launcher/ui/pages/global/MinecraftPage.ui +++ b/launcher/ui/pages/global/MinecraftPage.ui @@ -214,21 +214,55 @@ Native library workarounds - - + + Use system installation of &GLFW - + + + + &GLFW library path + + + lineEditGLFWPath + + + + Use system installation of &OpenAL + + + + &OpenAL library path + + + lineEditOpenALPath + + + + + + + false + + + + + + + false + + + diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index f7be91684..d603237b6 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -66,6 +66,10 @@ InstanceSettingsPage::InstanceSettingsPage(BaseInstance* inst, QWidget* parent) connect(APPLICATION, &Application::globalSettingsClosed, this, &InstanceSettingsPage::loadSettings); connect(ui->instanceAccountSelector, QOverload::of(&QComboBox::currentIndexChanged), this, &InstanceSettingsPage::changeInstanceAccount); + + connect(ui->useNativeGLFWCheck, &QAbstractButton::toggled, this, &InstanceSettingsPage::onUseNativeGLFWChanged); + connect(ui->useNativeOpenALCheck, &QAbstractButton::toggled, this, &InstanceSettingsPage::onUseNativeOpenALChanged); + loadSettings(); updateThresholds(); @@ -198,11 +202,15 @@ void InstanceSettingsPage::applySettings() bool workarounds = ui->nativeWorkaroundsGroupBox->isChecked(); m_settings->set("OverrideNativeWorkarounds", workarounds); if (workarounds) { - m_settings->set("UseNativeOpenAL", ui->useNativeOpenALCheck->isChecked()); m_settings->set("UseNativeGLFW", ui->useNativeGLFWCheck->isChecked()); + m_settings->set("CustomGLFWPath", ui->lineEditGLFWPath->text()); + m_settings->set("UseNativeOpenAL", ui->useNativeOpenALCheck->isChecked()); + m_settings->set("CustomOpenALPath", ui->lineEditOpenALPath->text()); } else { - m_settings->reset("UseNativeOpenAL"); m_settings->reset("UseNativeGLFW"); + m_settings->reset("CustomGLFWPath"); + m_settings->reset("UseNativeOpenAL"); + m_settings->reset("CustomOpenALPath"); } // Performance @@ -312,7 +320,19 @@ void InstanceSettingsPage::loadSettings() // Workarounds ui->nativeWorkaroundsGroupBox->setChecked(m_settings->get("OverrideNativeWorkarounds").toBool()); ui->useNativeGLFWCheck->setChecked(m_settings->get("UseNativeGLFW").toBool()); + ui->lineEditGLFWPath->setText(m_settings->get("CustomGLFWPath").toString()); +#ifdef Q_OS_LINUX + ui->lineEditGLFWPath->setPlaceholderText(APPLICATION->m_detectedGLFWPath); +#else + ui->lineEditGLFWPath->setPlaceholderText(tr("Path to %1 library file").arg(BuildConfig.GLFW_LIBRARY_NAME)); +#endif ui->useNativeOpenALCheck->setChecked(m_settings->get("UseNativeOpenAL").toBool()); + ui->lineEditOpenALPath->setText(m_settings->get("CustomOpenALPath").toString()); +#ifdef Q_OS_LINUX + ui->lineEditOpenALPath->setPlaceholderText(APPLICATION->m_detectedOpenALPath); +#else + ui->lineEditGLFWPath->setPlaceholderText(tr("Path to %1 library file").arg(BuildConfig.OPENAL_LIBRARY_NAME)); +#endif // Performance ui->perfomanceGroupBox->setChecked(m_settings->get("OverridePerformance").toBool()); @@ -408,6 +428,16 @@ void InstanceSettingsPage::on_javaTestBtn_clicked() checker->run(); } +void InstanceSettingsPage::onUseNativeGLFWChanged(bool checked) +{ + ui->lineEditGLFWPath->setEnabled(checked); +} + +void InstanceSettingsPage::onUseNativeOpenALChanged(bool checked) +{ + ui->lineEditOpenALPath->setEnabled(checked); +} + void InstanceSettingsPage::updateAccountsMenu() { ui->instanceAccountSelector->clear(); diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.h b/launcher/ui/pages/instance/InstanceSettingsPage.h index 21ecbaf8e..8b78dcb7f 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.h +++ b/launcher/ui/pages/instance/InstanceSettingsPage.h @@ -71,6 +71,9 @@ class InstanceSettingsPage : public QWidget, public BasePage { void on_javaBrowseBtn_clicked(); void on_maxMemSpinBox_valueChanged(int i); + void onUseNativeGLFWChanged(bool checked); + void onUseNativeOpenALChanged(bool checked); + void applySettings(); void loadSettings(); diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index 380d8c88c..fcc2a5d0a 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -443,18 +443,52 @@ false - - + + + + + Use system installation of OpenAL + + + + + + + &GLFW library path + + + lineEditGLFWPath + + + + Use system installation of GLFW - - + + + + false + + + + + - Use system installation of OpenAL + &OpenAL library path + + + lineEditOpenALPath + + + + + + + false From 4c446ccd500f7a0bc136b66b554fd7880c6f5168 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Wed, 2 Aug 2023 13:47:50 +0200 Subject: [PATCH 112/268] fix: remove meta OpenAL workaround LWJGL 2 doesn't have a separate zip for OpenAL. So there is no reason for this code. Signed-off-by: Sefa Eyeoglu --- launcher/minecraft/launch/ExtractNatives.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/launcher/minecraft/launch/ExtractNatives.cpp b/launcher/minecraft/launch/ExtractNatives.cpp index 1aa4dccc4..8f3cac4d1 100644 --- a/launcher/minecraft/launch/ExtractNatives.cpp +++ b/launcher/minecraft/launch/ExtractNatives.cpp @@ -39,7 +39,7 @@ static QString replaceSuffix(QString target, const QString& suffix, const QStrin return target + replacement; } -static bool unzipNatives(QString source, QString targetFolder, bool applyJnilibHack, bool nativeOpenAL) +static bool unzipNatives(QString source, QString targetFolder, bool applyJnilibHack) { QuaZip zip(source); if (!zip.open(QuaZip::mdUnzip)) { @@ -52,9 +52,6 @@ static bool unzipNatives(QString source, QString targetFolder, bool applyJnilibH do { QString name = zip.getCurrentFileName(); auto lowercase = name.toLower(); - if (nativeOpenAL && name.contains("openal")) { - continue; - } if (applyJnilibHack) { name = replaceSuffix(name, ".jnilib", ".dylib"); } @@ -81,14 +78,11 @@ void ExtractNatives::executeTask() } auto settings = minecraftInstance->settings(); - // We only need OpenAL here, as modern versions of LWJGL (3+) are handled by JVM args, while older versions (2) didn't have GLFW - bool nativeOpenAL = settings->get("UseNativeOpenAL").toBool(); - auto outputPath = minecraftInstance->getNativePath(); auto javaVersion = minecraftInstance->getJavaVersion(); bool jniHackEnabled = javaVersion.major() >= 8; for (const auto& source : toExtract) { - if (!unzipNatives(source, outputPath, jniHackEnabled, nativeOpenAL)) { + if (!unzipNatives(source, outputPath, jniHackEnabled)) { const char* reason = QT_TR_NOOP("Couldn't extract native jar '%1' to destination '%2'"); emit logLine(QString(reason).arg(source, outputPath), MessageLevel::Fatal); emitFailed(tr(reason).arg(source, outputPath)); From f1c3da6583d004273e5812a0b40977200ce330d6 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Wed, 2 Aug 2023 13:49:59 +0200 Subject: [PATCH 113/268] fix: fix typo Signed-off-by: Sefa Eyeoglu --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a8dd7a73d..638fba051 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -219,13 +219,13 @@ set(Launcher_QT_VERSION_MAJOR "6" CACHE STRING "Major Qt version to build agains # Native libraries if(UNIX AND APPLE) set(Launcher_GLFW_LIBRARY_NAME "libglfw.dylib" CACHE STRING "Name of native glfw library") - set(Launcher_OPENAL_LIBRARY_NAME "libopenal.dylib" CACHE STRING "Name of native glfw library") + set(Launcher_OPENAL_LIBRARY_NAME "libopenal.dylib" CACHE STRING "Name of native openal library") elseif(UNIX) set(Launcher_GLFW_LIBRARY_NAME "libglfw.so" CACHE STRING "Name of native glfw library") - set(Launcher_OPENAL_LIBRARY_NAME "libopenal.so" CACHE STRING "Name of native glfw library") + set(Launcher_OPENAL_LIBRARY_NAME "libopenal.so" CACHE STRING "Name of native openal library") elseif(WIN32) set(Launcher_GLFW_LIBRARY_NAME "glfw.dll" CACHE STRING "Name of native glfw library") - set(Launcher_OPENAL_LIBRARY_NAME "OpenAL.dll" CACHE STRING "Name of native glfw library") + set(Launcher_OPENAL_LIBRARY_NAME "OpenAL.dll" CACHE STRING "Name of native openal library") endif() # API Keys From b927e58126440d735803251caa3e03d29e4a0547 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Wed, 2 Aug 2023 13:51:13 +0200 Subject: [PATCH 114/268] fix: improve debug message Signed-off-by: Sefa Eyeoglu --- launcher/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 6daa5a84b..0f25589ed 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -1421,7 +1421,7 @@ void Application::detectLibraries() #ifdef Q_OS_LINUX m_detectedGLFWPath = MangoHud::findLibrary(BuildConfig.GLFW_LIBRARY_NAME); m_detectedOpenALPath = MangoHud::findLibrary(BuildConfig.OPENAL_LIBRARY_NAME); - qDebug() << m_detectedGLFWPath << m_detectedOpenALPath; + qDebug() << "Detected native libraries:" << m_detectedGLFWPath << m_detectedOpenALPath; #endif } From c2d6a137aba6d41df709c73096f4521df3779304 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Wed, 2 Aug 2023 13:56:23 +0200 Subject: [PATCH 115/268] fix: only add native library overrides if files exist Signed-off-by: Sefa Eyeoglu --- launcher/minecraft/MinecraftInstance.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index bfc4ad98a..62d22019f 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -397,22 +397,25 @@ QStringList MinecraftInstance::extraArguments() QString glfwPath; if (settings()->get("UseNativeOpenAL").toBool()) { - auto customPath = settings()->get("CustomOpenALPath").toString(); openALPath = APPLICATION->m_detectedOpenALPath; + auto customPath = settings()->get("CustomOpenALPath").toString(); if (!customPath.isEmpty()) openALPath = customPath; } if (settings()->get("UseNativeGLFW").toBool()) { - auto customPath = settings()->get("CustomGLFWPath").toString(); glfwPath = APPLICATION->m_detectedGLFWPath; + auto customPath = settings()->get("CustomGLFWPath").toString(); if (!customPath.isEmpty()) glfwPath = customPath; } - if (!openALPath.isEmpty()) - list.append("-Dorg.lwjgl.openal.libname=" + openALPath); - if (!glfwPath.isEmpty()) - list.append("-Dorg.lwjgl.glfw.libname=" + glfwPath); + QFileInfo openALInfo(openALPath); + QFileInfo glfwInfo(glfwPath); + + if (!openALPath.isEmpty() && openALInfo.exists()) + list.append("-Dorg.lwjgl.openal.libname=" + openALInfo.absoluteFilePath()); + if (!glfwPath.isEmpty() && glfwInfo.exists()) + list.append("-Dorg.lwjgl.glfw.libname=" + glfwInfo.absoluteFilePath()); } return list; From 06aba530d7154f0cff76b2d5dc650a428ff1a54e Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Wed, 2 Aug 2023 14:22:55 +0200 Subject: [PATCH 116/268] fix: add missing header Signed-off-by: Sefa Eyeoglu --- launcher/ui/pages/instance/InstanceSettingsPage.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index d603237b6..108f2c274 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -48,6 +48,7 @@ #include "ui/widgets/CustomCommands.h" #include "Application.h" +#include "BuildConfig.h" #include "JavaCommon.h" #include "minecraft/auth/AccountList.h" From 3a0aa353cc6c0c501a1a4cf1a3fa7600c798235a Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 6 Aug 2023 21:05:51 +0200 Subject: [PATCH 117/268] feat: add NeoForge to ModLoaderType Signed-off-by: Sefa Eyeoglu --- launcher/minecraft/PackProfile.cpp | 3 ++- launcher/modplatform/ResourceAPI.h | 4 +++- launcher/modplatform/flame/FlameAPI.h | 4 +++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp index cf8270cd2..92988808a 100644 --- a/launcher/minecraft/PackProfile.cpp +++ b/launcher/minecraft/PackProfile.cpp @@ -62,7 +62,8 @@ #include "Application.h" #include "modplatform/ResourceAPI.h" -static const QMap modloaderMapping{ { "net.minecraftforge", ResourceAPI::Forge }, +static const QMap modloaderMapping{ { "net.neoforged", ResourceAPI::NeoForge }, + { "net.minecraftforge", ResourceAPI::Forge }, { "net.fabricmc.fabric-loader", ResourceAPI::Fabric }, { "org.quiltmc.quilt-loader", ResourceAPI::Quilt }, { "com.mumfrey.liteloader", ResourceAPI::LiteLoader } }; diff --git a/launcher/modplatform/ResourceAPI.h b/launcher/modplatform/ResourceAPI.h index a92217a06..f6ccb426d 100644 --- a/launcher/modplatform/ResourceAPI.h +++ b/launcher/modplatform/ResourceAPI.h @@ -54,7 +54,7 @@ class ResourceAPI { public: virtual ~ResourceAPI() = default; - enum ModLoaderType { Forge = 1 << 0, Cauldron = 1 << 1, LiteLoader = 1 << 2, Fabric = 1 << 3, Quilt = 1 << 4 }; + enum ModLoaderType { NeoForge = 1 << 0, Forge = 1 << 1, Cauldron = 1 << 2, LiteLoader = 1 << 3, Fabric = 1 << 4, Quilt = 1 << 5 }; Q_DECLARE_FLAGS(ModLoaderTypes, ModLoaderType) struct SortingMethod { @@ -164,6 +164,8 @@ class ResourceAPI { static auto getModLoaderString(ModLoaderType type) -> const QString { switch (type) { + case NeoForge: + return "neoforge"; case Forge: return "forge"; case Cauldron: diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h index 49bc316f2..33b1bed7c 100644 --- a/launcher/modplatform/flame/FlameAPI.h +++ b/launcher/modplatform/flame/FlameAPI.h @@ -46,7 +46,9 @@ class FlameAPI : public NetworkResourceAPI { return 4; // TODO: remove this once Quilt drops official Fabric support if (loaders & Quilt) // NOTE: Most if not all Fabric mods should work *currently* - return 4; // Quilt would probably be 5 + return 4; // FIXME: implement multiple loaders filter + if (loaders & NeoForge) + return 6; return 0; } From 52e5ee711108c95815ac6c711bd7296f1a25884d Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 6 Aug 2023 21:06:52 +0200 Subject: [PATCH 118/268] feat: add NeoForge to UIs Signed-off-by: Sefa Eyeoglu --- launcher/resources/multimc/multimc.qrc | 1 + .../resources/multimc/scalable/instances/neoforged.svg | 3 +++ launcher/ui/dialogs/InstallLoaderDialog.cpp | 4 +++- launcher/ui/pages/modplatform/CustomPage.cpp | 3 +++ launcher/ui/pages/modplatform/CustomPage.ui | 10 ++++++++++ 5 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 launcher/resources/multimc/scalable/instances/neoforged.svg diff --git a/launcher/resources/multimc/multimc.qrc b/launcher/resources/multimc/multimc.qrc index 8f079bb37..80981559b 100644 --- a/launcher/resources/multimc/multimc.qrc +++ b/launcher/resources/multimc/multimc.qrc @@ -350,6 +350,7 @@ scalable/instances/quiltmc.svg scalable/instances/fabricmc.svg + scalable/instances/neoforged.svg 128x128/instances/forge.png 128x128/instances/liteloader.png diff --git a/launcher/resources/multimc/scalable/instances/neoforged.svg b/launcher/resources/multimc/scalable/instances/neoforged.svg new file mode 100644 index 000000000..706d53a0e --- /dev/null +++ b/launcher/resources/multimc/scalable/instances/neoforged.svg @@ -0,0 +1,3 @@ + + +Sefa Eyeoglu <contact@scrumplex.net> diff --git a/launcher/ui/dialogs/InstallLoaderDialog.cpp b/launcher/ui/dialogs/InstallLoaderDialog.cpp index 840a328f3..541119d10 100644 --- a/launcher/ui/dialogs/InstallLoaderDialog.cpp +++ b/launcher/ui/dialogs/InstallLoaderDialog.cpp @@ -129,7 +129,9 @@ InstallLoaderDialog::InstallLoaderDialog(std::shared_ptr profile, c QList InstallLoaderDialog::getPages() { - return { // Forge + return { // NeoForge + new InstallLoaderPage("net.neoforged", "neoforged", tr("NeoForge"), {}, profile), + // Forge new InstallLoaderPage("net.minecraftforge", "forge", tr("Forge"), {}, profile), // Fabric new InstallLoaderPage("net.fabricmc.fabric-loader", "fabricmc", tr("Fabric"), Version("1.14"), profile), diff --git a/launcher/ui/pages/modplatform/CustomPage.cpp b/launcher/ui/pages/modplatform/CustomPage.cpp index 4ac21b012..068fb3a36 100644 --- a/launcher/ui/pages/modplatform/CustomPage.cpp +++ b/launcher/ui/pages/modplatform/CustomPage.cpp @@ -127,6 +127,9 @@ void CustomPage::loaderFilterChanged() ui->loaderVersionList->setEmptyString(tr("No mod loader is selected.")); ui->loaderVersionList->setEmptyMode(VersionListView::String); return; + } else if (ui->neoForgeFilter->isChecked()) { + ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, minecraftVersion); + m_selectedLoader = "net.neoforged"; } else if (ui->forgeFilter->isChecked()) { ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, minecraftVersion); m_selectedLoader = "net.minecraftforge"; diff --git a/launcher/ui/pages/modplatform/CustomPage.ui b/launcher/ui/pages/modplatform/CustomPage.ui index 0d89b5956..23351ccd4 100644 --- a/launcher/ui/pages/modplatform/CustomPage.ui +++ b/launcher/ui/pages/modplatform/CustomPage.ui @@ -194,6 +194,16 @@ + + + + NeoForge + + + loaderBtnGroup + + + From 01c37508353b805a4c19330e076232ede593e383 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 6 Aug 2023 21:07:24 +0200 Subject: [PATCH 119/268] feat: support NeoForge mrpack modpacks Signed-off-by: Sefa Eyeoglu --- .../modplatform/modrinth/ModrinthInstanceCreationTask.cpp | 4 ++++ launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h | 2 +- launcher/modplatform/modrinth/ModrinthPackExportTask.cpp | 3 +++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp index cdbbd42d0..9ff6b374d 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp @@ -211,6 +211,8 @@ bool ModrinthCreationTask::createInstance() components->setComponentVersion("org.quiltmc.quilt-loader", m_quilt_version); if (!m_forge_version.isEmpty()) components->setComponentVersion("net.minecraftforge", m_forge_version); + if (!m_neoForge_version.isEmpty()) + components->setComponentVersion("net.neoforged", m_neoForge_version); if (m_instIcon != "default") { instance.setIconKey(m_instIcon); @@ -398,6 +400,8 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path, m_quilt_version = Json::requireString(*it, "Quilt Loader version"); } else if (name == "forge") { m_forge_version = Json::requireString(*it, "Forge version"); + } else if (name == "neoforge") { + m_neoForge_version = Json::requireString(*it, "NeoForge version"); } else { throw JSONValidationError("Unknown dependency type: " + name); } diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h index 07e417be5..1bd5b7de9 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h @@ -39,7 +39,7 @@ class ModrinthCreationTask final : public InstanceCreationTask { private: QWidget* m_parent = nullptr; - QString m_minecraft_version, m_fabric_version, m_quilt_version, m_forge_version; + QString m_minecraft_version, m_fabric_version, m_quilt_version, m_forge_version, m_neoForge_version; QString m_managed_id, m_managed_version_id, m_managed_name; std::vector m_files; diff --git a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp index 64c06d1ba..ad8fefac1 100644 --- a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp @@ -245,6 +245,7 @@ QByteArray ModrinthPackExportTask::generateIndex() const ComponentPtr quilt = profile->getComponent("org.quiltmc.quilt-loader"); const ComponentPtr fabric = profile->getComponent("net.fabricmc.fabric-loader"); const ComponentPtr forge = profile->getComponent("net.minecraftforge"); + const ComponentPtr neoForge = profile->getComponent("net.neoforged"); // convert all available components to mrpack dependencies QJsonObject dependencies; @@ -256,6 +257,8 @@ QByteArray ModrinthPackExportTask::generateIndex() dependencies["fabric-loader"] = fabric->m_version; if (forge != nullptr) dependencies["forge"] = forge->m_version; + if (neoForge != nullptr) + dependencies["neoforge"] = neoForge->m_version; out["dependencies"] = dependencies; } From 62c14cea2aea28cc943e795bb0f8ea10495c926a Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 6 Aug 2023 22:14:18 +0200 Subject: [PATCH 120/268] fix: allow NeoForge in resource APIs Signed-off-by: Sefa Eyeoglu --- launcher/modplatform/flame/FlameAPI.h | 2 +- launcher/modplatform/modrinth/ModrinthAPI.h | 4 ++-- launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h index 33b1bed7c..af8f39482 100644 --- a/launcher/modplatform/flame/FlameAPI.h +++ b/launcher/modplatform/flame/FlameAPI.h @@ -23,7 +23,7 @@ class FlameAPI : public NetworkResourceAPI { [[nodiscard]] auto getSortingMethods() const -> QList override; - static inline auto validateModLoaders(ModLoaderTypes loaders) -> bool { return loaders & (Forge | Fabric | Quilt); } + static inline auto validateModLoaders(ModLoaderTypes loaders) -> bool { return loaders & (NeoForge | Forge | Fabric | Quilt); } private: static int getClassId(ModPlatform::ResourceType type) diff --git a/launcher/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h index 58af14cc7..c3f6bab85 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.h +++ b/launcher/modplatform/modrinth/ModrinthAPI.h @@ -38,7 +38,7 @@ class ModrinthAPI : public NetworkResourceAPI { static auto getModLoaderStrings(const ModLoaderTypes types) -> const QStringList { QStringList l; - for (auto loader : { Forge, Fabric, Quilt, LiteLoader }) { + for (auto loader : { NeoForge, Forge, Fabric, Quilt, LiteLoader }) { if (types & loader) { l << getModLoaderString(loader); } @@ -141,7 +141,7 @@ class ModrinthAPI : public NetworkResourceAPI { return s.isEmpty() ? QString() : s; } - static inline auto validateModLoaders(ModLoaderTypes loaders) -> bool { return loaders & (Forge | Fabric | Quilt | LiteLoader); } + static inline auto validateModLoaders(ModLoaderTypes loaders) -> bool { return loaders & (NeoForge | Forge | Fabric | Quilt | LiteLoader); } [[nodiscard]] std::optional getDependencyURL(DependencySearchArgs const& args) const override { diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp index a7c22832a..bff8fa2fe 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp @@ -111,8 +111,8 @@ void ModrinthCheckUpdate::executeTask() // so we may want to filter it QString loader_filter; if (m_loaders.has_value()) { - static auto flags = { ResourceAPI::ModLoaderType::Forge, ResourceAPI::ModLoaderType::Fabric, - ResourceAPI::ModLoaderType::Quilt }; + static auto flags = { ResourceAPI::ModLoaderType::NeoForge, ResourceAPI::ModLoaderType::Forge, + ResourceAPI::ModLoaderType::Fabric, ResourceAPI::ModLoaderType::Quilt }; for (auto flag : flags) { if (m_loaders.value().testFlag(flag)) { loader_filter = api.getModLoaderString(flag); From 589d8b6923531a53cbccabb8b79ed9861d0fe27c Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 16 Aug 2023 19:53:39 +0300 Subject: [PATCH 121/268] feat:Added remove metadata button Signed-off-by: Trial97 --- launcher/minecraft/mod/Mod.cpp | 18 ++++++---- launcher/minecraft/mod/Mod.h | 2 ++ launcher/minecraft/mod/ModFolderModel.cpp | 19 +++++++++++ launcher/minecraft/mod/ModFolderModel.h | 1 + launcher/modplatform/ModIndex.h | 1 - .../pages/instance/ExternalResourcesPage.ui | 11 +++++++ launcher/ui/pages/instance/ModFolderPage.cpp | 33 +++++++++++++++++++ launcher/ui/pages/instance/ModFolderPage.h | 1 + 8 files changed, 79 insertions(+), 7 deletions(-) diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index ae3dea8d8..c9952998c 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -132,17 +132,23 @@ auto Mod::destroy(QDir& index_dir, bool preserve_metadata, bool attempt_trash) - if (!preserve_metadata) { qDebug() << QString("Destroying metadata for '%1' on purpose").arg(name()); - if (metadata()) { - Metadata::remove(index_dir, metadata()->slug); - } else { - auto n = name(); - Metadata::remove(index_dir, n); - } + destroyMetadata(index_dir); } return Resource::destroy(attempt_trash); } +void Mod::destroyMetadata(QDir& index_dir) +{ + if (metadata()) { + Metadata::remove(index_dir, metadata()->slug); + } else { + auto n = name(); + Metadata::remove(index_dir, n); + } + m_local_details.metadata = nullptr; +} + auto Mod::details() const -> const ModDetails& { return m_local_details; diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h index 6dafecfc5..e97ee9d3b 100644 --- a/launcher/minecraft/mod/Mod.h +++ b/launcher/minecraft/mod/Mod.h @@ -93,6 +93,8 @@ class Mod : public Resource { // Delete all the files of this mod auto destroy(QDir& index_dir, bool preserve_metadata = false, bool attempt_trash = true) -> bool; + // Delete the metadata only + void destroyMetadata(QDir& index_dir); void finishResolvingWithDetails(ModDetails&& details); diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index 280e70d7b..d6ce98ed9 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -228,6 +228,25 @@ bool ModFolderModel::deleteMods(const QModelIndexList& indexes) return true; } +bool ModFolderModel::deleteModsMeatadata(const QModelIndexList& indexes) +{ + if (indexes.isEmpty()) + return true; + + for (auto i : indexes) { + if (i.column() != 0) { + continue; + } + auto m = at(i.row()); + auto index_dir = indexDir(); + m->destroyMetadata(index_dir); + } + + update(); + + return true; +} + bool ModFolderModel::isValid() { return m_dir.exists() && m_dir.isReadable(); diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h index 06fd78149..c512c58f0 100644 --- a/launcher/minecraft/mod/ModFolderModel.h +++ b/launcher/minecraft/mod/ModFolderModel.h @@ -79,6 +79,7 @@ class ModFolderModel : public ResourceFolderModel { /// Deletes all the selected mods bool deleteMods(const QModelIndexList& indexes); + bool deleteModsMeatadata(const QModelIndexList& indexes); bool isValid(); diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index cad217034..0e18ccd17 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -128,7 +128,6 @@ struct IndexedPack { return std::any_of(versions.constBegin(), versions.constEnd(), [](auto const& v) { return v.is_currently_selected; }); } }; -QString getMetaURL(ResourceProvider provider, QVariant projectID); struct OverrideDep { QString quilt; diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.ui b/launcher/ui/pages/instance/ExternalResourcesPage.ui index 3c8366917..56adce476 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.ui +++ b/launcher/ui/pages/instance/ExternalResourcesPage.ui @@ -168,6 +168,17 @@ Go to mods home page + + + false + + + Remove metadata + + + Remove selected mod metadata + + diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index 0f5e29cb6..146e1d973 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -92,6 +92,10 @@ ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr ui->actionsToolbar->addAction(ui->actionVisitItemPage); connect(ui->actionVisitItemPage, &QAction::triggered, this, &ModFolderPage::visitModPages); + ui->actionRemoveItemMetadata->setToolTip(tr("Remove mod's metadata")); + ui->actionsToolbar->insertActionAfter(ui->actionRemoveItem, ui->actionRemoveItemMetadata); + connect(ui->actionRemoveItemMetadata, &QAction::triggered, this, &ModFolderPage::deleteModMetadata); + auto check_allow_update = [this] { return ui->treeView->selectionModel()->hasSelection() || !m_model->empty(); }; connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [this, check_allow_update] { @@ -104,11 +108,16 @@ ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr if (selected <= 1) { ui->actionVisitItemPage->setText(tr("Visit mod's page")); ui->actionVisitItemPage->setToolTip(tr("Go to mod's home page")); + + ui->actionRemoveItemMetadata->setToolTip(tr("Remove mod's metadata")); } else { ui->actionVisitItemPage->setText(tr("Visit mods' pages")); ui->actionVisitItemPage->setToolTip(tr("Go to the pages of the selected mods")); + + ui->actionRemoveItemMetadata->setToolTip(tr("Remove mods' metadata")); } ui->actionVisitItemPage->setEnabled(selected != 0); + ui->actionRemoveItemMetadata->setEnabled(selected != 0); }); connect(mods.get(), &ModFolderModel::rowsInserted, this, @@ -297,3 +306,27 @@ void ModFolderPage::visitModPages() DesktopServices::openUrl(url); } } + +void ModFolderPage::deleteModMetadata() +{ + auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); + QString text; + auto selectionCount = m_model->selectedMods(selection).length(); + if (selectionCount == 0) + return; + else if (selectionCount > 1) + text = tr("You are about to remove the metadata for %1 mods.\n" + "Are you sure?") + .arg(selectionCount); + else + text = tr("You are about to remove the metadata for %1.\n" + "Are you sure?") + .arg(m_model->at(selection.at(0).row())->name()); + + auto response = CustomMessageBox::selectable(this, tr("Confirm Removal"), text, QMessageBox::Warning, + QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + + if (response == QMessageBox::Yes) + m_model->deleteModsMeatadata(selection); +} diff --git a/launcher/ui/pages/instance/ModFolderPage.h b/launcher/ui/pages/instance/ModFolderPage.h index a23dcae18..0c654d0d1 100644 --- a/launcher/ui/pages/instance/ModFolderPage.h +++ b/launcher/ui/pages/instance/ModFolderPage.h @@ -61,6 +61,7 @@ class ModFolderPage : public ExternalResourcesPage { private slots: void removeItems(const QItemSelection& selection) override; + void deleteModMetadata(); void installMods(); void updateMods(); From f919d363b706e4c00317b56849702d8f95446bdb Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 16 Aug 2023 20:02:32 +0300 Subject: [PATCH 122/268] made safe for vegetarians Signed-off-by: Trial97 --- launcher/minecraft/mod/ModFolderModel.cpp | 2 +- launcher/minecraft/mod/ModFolderModel.h | 2 +- launcher/ui/pages/instance/ModFolderPage.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index d6ce98ed9..42ee94991 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -228,7 +228,7 @@ bool ModFolderModel::deleteMods(const QModelIndexList& indexes) return true; } -bool ModFolderModel::deleteModsMeatadata(const QModelIndexList& indexes) +bool ModFolderModel::deleteModsMetadata(const QModelIndexList& indexes) { if (indexes.isEmpty()) return true; diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h index c512c58f0..4d1bafaed 100644 --- a/launcher/minecraft/mod/ModFolderModel.h +++ b/launcher/minecraft/mod/ModFolderModel.h @@ -79,7 +79,7 @@ class ModFolderModel : public ResourceFolderModel { /// Deletes all the selected mods bool deleteMods(const QModelIndexList& indexes); - bool deleteModsMeatadata(const QModelIndexList& indexes); + bool deleteModsMetadata(const QModelIndexList& indexes); bool isValid(); diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index 146e1d973..d4e7a25c9 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -328,5 +328,5 @@ void ModFolderPage::deleteModMetadata() ->exec(); if (response == QMessageBox::Yes) - m_model->deleteModsMeatadata(selection); + m_model->deleteModsMetadata(selection); } From 6c2c724bd99d442f7856da1a2ef4ebdd674064d1 Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Wed, 16 Aug 2023 21:03:09 +0300 Subject: [PATCH 123/268] Update launcher/ui/pages/instance/ExternalResourcesPage.ui Co-authored-by: TheKodeToad Signed-off-by: Alexandru Ionut Tripon --- launcher/ui/pages/instance/ExternalResourcesPage.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.ui b/launcher/ui/pages/instance/ExternalResourcesPage.ui index 56adce476..ba703f77d 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.ui +++ b/launcher/ui/pages/instance/ExternalResourcesPage.ui @@ -176,7 +176,7 @@ Remove metadata - Remove selected mod metadata + Remove mod's metadata From b0e197de38858c3fe6bc5fbb62570efd3359c802 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 16 Aug 2023 21:26:43 +0300 Subject: [PATCH 124/268] removed warning for one mod selected Signed-off-by: Trial97 --- launcher/ui/pages/instance/ModFolderPage.cpp | 25 +++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index d4e7a25c9..b42bbf466 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -310,23 +310,20 @@ void ModFolderPage::visitModPages() void ModFolderPage::deleteModMetadata() { auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); - QString text; auto selectionCount = m_model->selectedMods(selection).length(); if (selectionCount == 0) return; - else if (selectionCount > 1) - text = tr("You are about to remove the metadata for %1 mods.\n" - "Are you sure?") - .arg(selectionCount); - else - text = tr("You are about to remove the metadata for %1.\n" - "Are you sure?") - .arg(m_model->at(selection.at(0).row())->name()); + if (selectionCount > 1) { + auto response = CustomMessageBox::selectable(this, tr("Confirm Removal"), + tr("You are about to remove the metadata for %1 mods.\n" + "Are you sure?") + .arg(selectionCount), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); - auto response = CustomMessageBox::selectable(this, tr("Confirm Removal"), text, QMessageBox::Warning, - QMessageBox::Yes | QMessageBox::No, QMessageBox::No) - ->exec(); + if (response != QMessageBox::Yes) + return; + } - if (response == QMessageBox::Yes) - m_model->deleteModsMetadata(selection); + m_model->deleteModsMetadata(selection); } From f0da16a758e4d3c2d41813f723b21390281e4a4e Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 17 Aug 2023 00:13:12 +0300 Subject: [PATCH 125/268] removed line Signed-off-by: Trial97 --- launcher/ui/pages/modplatform/ImportPage.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/launcher/ui/pages/modplatform/ImportPage.cpp b/launcher/ui/pages/modplatform/ImportPage.cpp index ee8757f85..3e3c36b7b 100644 --- a/launcher/ui/pages/modplatform/ImportPage.cpp +++ b/launcher/ui/pages/modplatform/ImportPage.cpp @@ -114,7 +114,6 @@ void ImportPage::updateState() bool isMRPack = fi.suffix() == "mrpack"; if (fi.exists() && (isZip || isMRPack)) { - QFileInfo fi(url.fileName()); auto extra_info = QMap(m_extra_info); qDebug() << "Pack Extra Info" << extra_info << m_extra_info; dialog->setSuggestedPack(fi.completeBaseName(), new InstanceImportTask(url, this, std::move(extra_info))); From bad44ea264eab5ce896f51a981cf1659f2755716 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 17 Aug 2023 00:14:49 +0300 Subject: [PATCH 126/268] feat:added flame install mod metadata Signed-off-by: Trial97 --- launcher/minecraft/mod/ModFolderModel.cpp | 49 +++++++++++++++++++++++ launcher/minecraft/mod/ModFolderModel.h | 2 + launcher/ui/MainWindow.cpp | 31 +++++++------- 3 files changed, 67 insertions(+), 15 deletions(-) diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index 280e70d7b..eed35615c 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -51,8 +51,13 @@ #include "Application.h" +#include "Json.h" #include "minecraft/mod/tasks/LocalModParseTask.h" +#include "minecraft/mod/tasks/LocalModUpdateTask.h" #include "minecraft/mod/tasks/ModFolderLoadTask.h" +#include "modplatform/ModIndex.h" +#include "modplatform/flame/FlameAPI.h" +#include "modplatform/flame/FlameModIndex.h" ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed, bool create_dir) : ResourceFolderModel(QDir(dir), instance, nullptr, create_dir), m_is_indexed(is_indexed) @@ -309,3 +314,47 @@ void ModFolderModel::onParseSucceeded(int ticket, QString mod_id) emit dataChanged(index(row), index(row, columnCount(QModelIndex()) - 1)); } + +static const FlameAPI flameAPI; +bool ModFolderModel::installMod(QString file_path, ModPlatform::IndexedVersion& vers) +{ + if (vers.addonId.isValid()) { + ModPlatform::IndexedPack pack{ + vers.addonId, + ModPlatform::ResourceProvider::FLAME, + }; + + QEventLoop loop; + + auto response = std::make_shared(); + auto job = flameAPI.getProject(vers.addonId.toString(), response); + + QObject::connect(job.get(), &Task::failed, [&loop] { loop.quit(); }); + QObject::connect(job.get(), &Task::aborted, &loop, &QEventLoop::quit); + QObject::connect(job.get(), &Task::succeeded, [response, this, &vers, &loop, &pack] { + QJsonParseError parse_error{}; + QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); + if (parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response for mod info at " << parse_error.offset + << " reason: " << parse_error.errorString(); + qDebug() << *response; + return; + } + try { + auto obj = Json::requireObject(Json::requireObject(doc), "data"); + FlameMod::loadIndexedPack(pack, obj); + } catch (const JSONValidationError& e) { + qDebug() << doc; + qWarning() << "Error while reading mod info: " << e.cause(); + } + LocalModUpdateTask update_metadata(indexDir(), pack, vers); + QObject::connect(&update_metadata, &Task::finished, &loop, &QEventLoop::quit); + update_metadata.start(); + }); + + job->start(); + + loop.exec(); + } + return ResourceFolderModel::installResource(file_path); +} diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h index 06fd78149..f1890e87e 100644 --- a/launcher/minecraft/mod/ModFolderModel.h +++ b/launcher/minecraft/mod/ModFolderModel.h @@ -48,6 +48,7 @@ #include "minecraft/mod/tasks/LocalModParseTask.h" #include "minecraft/mod/tasks/ModFolderLoadTask.h" +#include "modplatform/ModIndex.h" class LegacyInstance; class BaseInstance; @@ -75,6 +76,7 @@ class ModFolderModel : public ResourceFolderModel { [[nodiscard]] Task* createParseTask(Resource&) override; bool installMod(QString file_path) { return ResourceFolderModel::installResource(file_path); } + bool installMod(QString file_path, ModPlatform::IndexedVersion& vers); bool uninstallMod(const QString& filename, bool preserve_metadata = false); /// Deletes all the selected mods diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 7d4148775..6736e912c 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -43,6 +43,8 @@ #include "FileSystem.h" #include "MainWindow.h" +#include "modplatform/ModIndex.h" +#include "modplatform/flame/FlameModIndex.h" #include "ui/dialogs/ExportToModListDialog.h" #include "ui_MainWindow.h" @@ -980,11 +982,12 @@ void MainWindow::processURLs(QList urls) if (url.scheme().isEmpty()) url.setScheme("file"); + ModPlatform::IndexedVersion version; QMap extra_info; QUrl local_url; if (!url.isLocalFile()) { // download the remote resource and identify QUrl dl_url; - if(url.scheme() == "curseforge") { + if (url.scheme() == "curseforge") { // need to find the download link for the modpack / resource // format of url curseforge://install?addonId=IDHERE&fileId=IDHERE QUrlQuery query(url); @@ -1000,20 +1003,19 @@ void MainWindow::processURLs(QList urls) auto api = FlameAPI(); auto job = api.getFile(addonId, fileId, array); - QString resource_name; - connect(job.get(), &Task::failed, this, [this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); }); - connect(job.get(), &Task::succeeded, this, [this, array, addonId, fileId, &dl_url, &resource_name] { + connect(job.get(), &Task::succeeded, this, [this, array, addonId, fileId, &dl_url, &version] { qDebug() << "Returned CFURL Json:\n" << array->toStdString().c_str(); auto doc = Json::requireDocument(*array); auto data = Json::ensureObject(Json::ensureObject(doc.object()), "data"); // No way to find out if it's a mod or a modpack before here // And also we need to check if it ends with .zip, instead of any better way - auto fileName = Json::ensureString(data, "fileName"); + version = FlameMod::loadIndexedPackVersion(data); + auto fileName = version.fileName; // Have to use ensureString then use QUrl to get proper url encoding - dl_url = QUrl(Json::ensureString(data, "downloadUrl", "", "downloadUrl")); + dl_url = QUrl(version.downloadUrl); if (!dl_url.isValid()) { CustomMessageBox::selectable( this, tr("Error"), @@ -1024,22 +1026,20 @@ void MainWindow::processURLs(QList urls) } QFileInfo dl_file(dl_url.fileName()); - resource_name = Json::ensureString(data, "displayName", dl_file.completeBaseName(), "displayName"); }); - { // drop stack + { // drop stack ProgressDialog dlUrlDialod(this); dlUrlDialod.setSkipButton(true, tr("Abort")); dlUrlDialod.execWithTask(job.get()); } - } else { dl_url = url; } if (!dl_url.isValid()) { - continue; // no valid url to download this resource + continue; // no valid url to download this resource } const QString path = dl_url.host() + '/' + dl_url.path(); @@ -1050,17 +1050,18 @@ void MainWindow::processURLs(QList urls) auto archivePath = entry->getFullPath(); bool dl_success = false; - connect(dl_job.get(), &Task::failed, this, [this](QString reason){CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); }); - connect(dl_job.get(), &Task::succeeded, this, [&dl_success]{dl_success = true;}); + connect(dl_job.get(), &Task::failed, this, + [this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); }); + connect(dl_job.get(), &Task::succeeded, this, [&dl_success] { dl_success = true; }); - { // drop stack + { // drop stack ProgressDialog dlUrlDialod(this); dlUrlDialod.setSkipButton(true, tr("Abort")); dlUrlDialod.execWithTask(dl_job.get()); } if (!dl_success) { - continue; // no local file to identify + continue; // no local file to identify } local_url = QUrl::fromLocalFile(archivePath); @@ -1099,7 +1100,7 @@ void MainWindow::processURLs(QList urls) qWarning() << "Importing of Data Packs not supported at this time. Ignoring" << localFileName; break; case PackedResourceType::Mod: - minecraftInst->loaderModList()->installMod(localFileName); + minecraftInst->loaderModList()->installMod(localFileName, version); break; case PackedResourceType::ShaderPack: minecraftInst->shaderPackList()->installResource(localFileName); From 6da2e7d3f6f06916e9d92578546b5d80474aaa5f Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Thu, 17 Aug 2023 10:12:38 +0100 Subject: [PATCH 127/268] I was forced /j Co-authored-by: Sefa Eyeoglu Signed-off-by: TheKodeToad --- launcher/minecraft/MinecraftInstance.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index babf92989..d930d6811 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -272,10 +272,7 @@ void MinecraftInstance::populateLaunchMenu(QMenu* menu) profilerAction->setChecked(settings()->get("Profiler").toString() == profiler.key()); QString error; - if (profiler.value()->check(&error)) - profilerAction->setIcon(APPLICATION->getThemedIcon("status-good")); - else - profilerAction->setIcon(APPLICATION->getThemedIcon("status-bad")); + profilerAction->setEnabled(profiler.value()->check(&error)); } } From ffd8ed550f05e1336afc6ddc424b6733256ecf02 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Thu, 17 Aug 2023 10:16:52 +0100 Subject: [PATCH 128/268] Reformat Signed-off-by: TheKodeToad --- launcher/Application.cpp | 15 +++++---------- launcher/Application.h | 2 +- launcher/ui/InstanceWindow.h | 2 +- launcher/ui/MainWindow.cpp | 2 +- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index c1bf7e5ef..4da7629ce 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -1104,18 +1104,13 @@ bool Application::launch(InstancePtr instance, MinecraftServerTargetPtr serverToJoin, MinecraftAccountPtr accountToUse) { - if(m_updateRunning) - { + if (m_updateRunning) { qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed."; - } - else if(instance->canLaunch()) - { - auto & extras = m_instanceExtras[instance->id()]; + } else if (instance->canLaunch()) { + auto& extras = m_instanceExtras[instance->id()]; auto window = extras.window; - if(window) - { - if(!window->saveAll()) - { + if (window) { + if (!window->saveAll()) { return false; } } diff --git a/launcher/Application.h b/launcher/Application.h index a09f46b57..8a85fd952 100644 --- a/launcher/Application.h +++ b/launcher/Application.h @@ -187,7 +187,7 @@ class Application : public QApplication { void clickedOnDock(); #endif -public slots: + public slots: bool launch(InstancePtr instance, bool online = true, bool demo = false, diff --git a/launcher/ui/InstanceWindow.h b/launcher/ui/InstanceWindow.h index a8a7559fd..e5bc24d44 100644 --- a/launcher/ui/InstanceWindow.h +++ b/launcher/ui/InstanceWindow.h @@ -52,7 +52,7 @@ class PageContainer; class InstanceWindow : public QMainWindow, public BasePageContainer { Q_OBJECT -public: + public: explicit InstanceWindow(InstancePtr proc, QWidget* parent = 0); virtual ~InstanceWindow() = default; diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 48feea099..36a6e379b 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -551,7 +551,7 @@ void MainWindow::updateMainToolBar() void MainWindow::updateLaunchButton() { - QMenu *launchMenu = ui->actionLaunchInstance->menu(); + QMenu* launchMenu = ui->actionLaunchInstance->menu(); if (launchMenu) launchMenu->clear(); else From aac734d174cc61d0a72c677e82c6ca2539c09633 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Thu, 17 Aug 2023 22:20:43 +0200 Subject: [PATCH 129/268] fix: add theoretical support for NeoForge in FTB modpacks Signed-off-by: Sefa Eyeoglu --- launcher/modplatform/import_ftb/PackHelpers.cpp | 6 +++++- launcher/modplatform/import_ftb/PackInstallTask.cpp | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/launcher/modplatform/import_ftb/PackHelpers.cpp b/launcher/modplatform/import_ftb/PackHelpers.cpp index 4a1bbef96..118bdd157 100644 --- a/launcher/modplatform/import_ftb/PackHelpers.cpp +++ b/launcher/modplatform/import_ftb/PackHelpers.cpp @@ -59,7 +59,11 @@ Modpack parseDirectory(QString path) auto obj = Json::requireObject(target, "target"); auto name = Json::requireString(obj, "name", "name"); auto version = Json::requireString(obj, "version", "version"); - if (name == "forge") { + if (name == "neoforge") { + modpack.loaderType = ResourceAPI::NeoForge; + modpack.version = version; + break; + } else if (name == "forge") { modpack.loaderType = ResourceAPI::Forge; modpack.version = version; break; diff --git a/launcher/modplatform/import_ftb/PackInstallTask.cpp b/launcher/modplatform/import_ftb/PackInstallTask.cpp index b5e424d12..9e4decb0c 100644 --- a/launcher/modplatform/import_ftb/PackInstallTask.cpp +++ b/launcher/modplatform/import_ftb/PackInstallTask.cpp @@ -68,6 +68,10 @@ void PackInstallTask::copySettings() auto modloader = m_pack.loaderType; if (modloader.has_value()) switch (modloader.value()) { + case ResourceAPI::NeoForge: { + components->setComponentVersion("net.neoforged", m_pack.version, true); + break; + } case ResourceAPI::Forge: { components->setComponentVersion("net.minecraftforge", m_pack.version, true); break; From 7ab391904aca58c09bead083c41b7bf4108361d1 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 15 Aug 2023 14:13:29 +0300 Subject: [PATCH 130/268] Flame support for neoforge Signed-off-by: Trial97 --- launcher/modplatform/flame/FlameInstanceCreationTask.cpp | 8 ++++++-- launcher/modplatform/flame/FlamePackExportTask.cpp | 3 +++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp index 9fe8d4865..45b4e2125 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp @@ -284,7 +284,7 @@ QString FlameCreationTask::getVersionForLoader(QString uid, QString loaderType, // filter by minecraft version, if the loader depends on a certain version. // not all mod loaders depend on a given Minecraft version, so we won't do this // filtering for those loaders. - if (loaderType == "forge") { + if (loaderType == "forge" || loaderType == "neoforge") { auto iter = std::find_if(reqs.begin(), reqs.end(), [mcVersion](const Meta::Require& req) { return req.uid == "net.minecraft" && req.equalsVersion == mcVersion; }); @@ -350,7 +350,11 @@ bool FlameCreationTask::createInstance() for (auto& loader : m_pack.minecraft.modLoaders) { auto id = loader.id; - if (id.startsWith("forge-")) { + if (id.startsWith("neoforge-")) { + id.remove("neoforge-"); + loaderType = "neoforge"; + loaderUid = "net.neoforged"; + } else if (id.startsWith("forge-")) { id.remove("forge-"); loaderType = "forge"; loaderUid = "net.minecraftforge"; diff --git a/launcher/modplatform/flame/FlamePackExportTask.cpp b/launcher/modplatform/flame/FlamePackExportTask.cpp index f5f3af372..0863f0b2b 100644 --- a/launcher/modplatform/flame/FlamePackExportTask.cpp +++ b/launcher/modplatform/flame/FlamePackExportTask.cpp @@ -381,6 +381,7 @@ QByteArray FlamePackExportTask::generateIndex() const ComponentPtr quilt = profile->getComponent("org.quiltmc.quilt-loader"); const ComponentPtr fabric = profile->getComponent("net.fabricmc.fabric-loader"); const ComponentPtr forge = profile->getComponent("net.minecraftforge"); + const ComponentPtr neoforge = profile->getComponent("net.neoforged"); // convert all available components to mrpack dependencies if (minecraft != nullptr) @@ -392,6 +393,8 @@ QByteArray FlamePackExportTask::generateIndex() id = "fabric-" + fabric->getVersion(); else if (forge != nullptr) id = "forge-" + forge->getVersion(); + else if (neoforge != nullptr) + id = "neoforge-" + neoforge->getVersion(); version["modLoaders"] = QJsonArray(); if (!id.isEmpty()) { QJsonObject loader; From be2888d6fbf28e515206d8677f8f6c8fce8b6e89 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Thu, 17 Aug 2023 22:23:50 +0200 Subject: [PATCH 131/268] chore: reformat Signed-off-by: Sefa Eyeoglu --- launcher/modplatform/modrinth/ModrinthAPI.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/launcher/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h index c3f6bab85..0f150e97a 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.h +++ b/launcher/modplatform/modrinth/ModrinthAPI.h @@ -141,7 +141,10 @@ class ModrinthAPI : public NetworkResourceAPI { return s.isEmpty() ? QString() : s; } - static inline auto validateModLoaders(ModLoaderTypes loaders) -> bool { return loaders & (NeoForge | Forge | Fabric | Quilt | LiteLoader); } + static inline auto validateModLoaders(ModLoaderTypes loaders) -> bool + { + return loaders & (NeoForge | Forge | Fabric | Quilt | LiteLoader); + } [[nodiscard]] std::optional getDependencyURL(DependencySearchArgs const& args) const override { From f4ebeedc21c97df41168901c618a3b57357c7b7a Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Fri, 18 Aug 2023 09:01:57 +0200 Subject: [PATCH 132/268] fix: fix browse button size in skin upload dialog Signed-off-by: Sefa Eyeoglu --- launcher/ui/dialogs/SkinUploadDialog.ui | 6 ------ 1 file changed, 6 deletions(-) diff --git a/launcher/ui/dialogs/SkinUploadDialog.ui b/launcher/ui/dialogs/SkinUploadDialog.ui index 2c81a7fed..c6df92df3 100644 --- a/launcher/ui/dialogs/SkinUploadDialog.ui +++ b/launcher/ui/dialogs/SkinUploadDialog.ui @@ -35,12 +35,6 @@ 0 - - - 28 - 16777215 - - Browse From 6d1c67663dd5388b2209d162d517d6d9baa37d83 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 18 Aug 2023 12:24:28 +0300 Subject: [PATCH 133/268] feat:added option to show playtime in hours Signed-off-by: Trial97 --- launcher/Application.cpp | 1 + launcher/MMCTime.cpp | 7 ++++--- launcher/MMCTime.h | 2 +- launcher/minecraft/MinecraftInstance.cpp | 11 +++++++---- launcher/ui/MainWindow.cpp | 4 +++- launcher/ui/pages/global/MinecraftPage.cpp | 2 ++ launcher/ui/pages/global/MinecraftPage.ui | 7 +++++++ 7 files changed, 25 insertions(+), 9 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 78de87470..442970158 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -598,6 +598,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) m_settings->registerSetting("ShowGameTime", true); m_settings->registerSetting("ShowGlobalGameTime", true); m_settings->registerSetting("RecordGameTime", true); + m_settings->registerSetting("ShowGameTimeWithoutDays", false); // Minecraft mods m_settings->registerSetting("ModMetadataDisabled", false); diff --git a/launcher/MMCTime.cpp b/launcher/MMCTime.cpp index 3972dbd53..1765fd844 100644 --- a/launcher/MMCTime.cpp +++ b/launcher/MMCTime.cpp @@ -16,19 +16,20 @@ */ #include +#include #include #include #include -QString Time::prettifyDuration(int64_t duration) +QString Time::prettifyDuration(int64_t duration, bool noDays) { int seconds = (int)(duration % 60); duration /= 60; int minutes = (int)(duration % 60); duration /= 60; - int hours = (int)(duration % 24); - int days = (int)(duration / 24); + int hours = (int)(noDays ? duration : (duration % 24)); + int days = (int)(noDays ? 0 : (duration / 24)); if ((hours == 0) && (days == 0)) { return QObject::tr("%1min %2s").arg(minutes).arg(seconds); } diff --git a/launcher/MMCTime.h b/launcher/MMCTime.h index b7d34b5d8..ea6d37e7e 100644 --- a/launcher/MMCTime.h +++ b/launcher/MMCTime.h @@ -20,7 +20,7 @@ namespace Time { -QString prettifyDuration(int64_t duration); +QString prettifyDuration(int64_t duration, bool noDays = false); /** * @brief Returns a string with short form time duration ie. `2days 1h3m4s56.0ms`. diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 62d22019f..c90d43623 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -897,13 +897,16 @@ QString MinecraftInstance::getStatusbarDescription() if (m_settings->get("ShowGameTime").toBool()) { if (lastTimePlayed() > 0) { QDateTime lastLaunchTime = QDateTime::fromMSecsSinceEpoch(lastLaunch()); - description.append(tr(", last played on %1 for %2") - .arg(QLocale().toString(lastLaunchTime, QLocale::ShortFormat)) - .arg(Time::prettifyDuration(lastTimePlayed()))); + description.append( + tr(", last played on %1 for %2") + .arg(QLocale().toString(lastLaunchTime, QLocale::ShortFormat)) + .arg(Time::prettifyDuration(lastTimePlayed(), APPLICATION->settings()->get("ShowGameTimeWithoutDays").toBool()))); } if (totalTimePlayed() > 0) { - description.append(tr(", total played for %1").arg(Time::prettifyDuration(totalTimePlayed()))); + description.append( + tr(", total played for %1") + .arg(Time::prettifyDuration(totalTimePlayed(), APPLICATION->settings()->get("ShowGameTimeWithoutDays").toBool()))); } } if (hasCrashed()) { diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 067108f2d..de2b96345 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -1810,7 +1810,9 @@ void MainWindow::updateStatusCenter() int timePlayed = APPLICATION->instances()->getTotalPlayTime(); if (timePlayed > 0) { - m_statusCenter->setText(tr("Total playtime: %1").arg(Time::prettifyDuration(timePlayed))); + m_statusCenter->setText( + tr("Total playtime: %1") + .arg(Time::prettifyDuration(timePlayed, APPLICATION->settings()->get("ShowGameTimeWithoutDays").toBool()))); } } // "Instance actions" are actions that require an instance to be selected (i.e. "new instance" is not here) diff --git a/launcher/ui/pages/global/MinecraftPage.cpp b/launcher/ui/pages/global/MinecraftPage.cpp index 1c7747210..9beaa433e 100644 --- a/launcher/ui/pages/global/MinecraftPage.cpp +++ b/launcher/ui/pages/global/MinecraftPage.cpp @@ -115,6 +115,7 @@ void MinecraftPage::applySettings() s->set("ShowGameTime", ui->showGameTime->isChecked()); s->set("ShowGlobalGameTime", ui->showGlobalGameTime->isChecked()); s->set("RecordGameTime", ui->recordGameTime->isChecked()); + s->set("ShowGameTimeWithoutDays", ui->showGameTimeWithoutDays->isChecked()); // Miscellaneous s->set("CloseAfterLaunch", ui->closeAfterLaunchCheck->isChecked()); @@ -169,6 +170,7 @@ void MinecraftPage::loadSettings() ui->showGameTime->setChecked(s->get("ShowGameTime").toBool()); ui->showGlobalGameTime->setChecked(s->get("ShowGlobalGameTime").toBool()); ui->recordGameTime->setChecked(s->get("RecordGameTime").toBool()); + ui->showGameTimeWithoutDays->setChecked(s->get("ShowGameTimeWithoutDays").toBool()); ui->closeAfterLaunchCheck->setChecked(s->get("CloseAfterLaunch").toBool()); ui->quitAfterGameStopCheck->setChecked(s->get("QuitAfterGameStop").toBool()); diff --git a/launcher/ui/pages/global/MinecraftPage.ui b/launcher/ui/pages/global/MinecraftPage.ui index 98e90e4ec..89bef9491 100644 --- a/launcher/ui/pages/global/MinecraftPage.ui +++ b/launcher/ui/pages/global/MinecraftPage.ui @@ -138,6 +138,13 @@ + + + + Show time spent playing without days + + + From 28ffa8bf44f220e541d00204b52a45573fba645a Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Fri, 18 Aug 2023 12:57:08 +0300 Subject: [PATCH 134/268] Update launcher/ui/pages/global/MinecraftPage.ui Co-authored-by: Sefa Eyeoglu Signed-off-by: Alexandru Ionut Tripon --- launcher/ui/pages/global/MinecraftPage.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/pages/global/MinecraftPage.ui b/launcher/ui/pages/global/MinecraftPage.ui index 89bef9491..6fb21da42 100644 --- a/launcher/ui/pages/global/MinecraftPage.ui +++ b/launcher/ui/pages/global/MinecraftPage.ui @@ -141,7 +141,7 @@ - Show time spent playing without days + Show time spent playing in hours From d3acac16e664b5b36d5f7eb0c857bb400fdbdac8 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 18 Aug 2023 13:10:13 +0300 Subject: [PATCH 135/268] added -Wno-gnu-zero-variadic-macro-arguments and fixed more warnings Signed-off-by: Trial97 --- cmake/CompilerWarnings.cmake | 4 ++++ launcher/java/JavaInstall.h | 6 +++--- launcher/minecraft/mod/ResourceFolderModel.h | 3 ++- launcher/ui/widgets/VersionSelectWidget.h | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/cmake/CompilerWarnings.cmake b/cmake/CompilerWarnings.cmake index 635e54289..0ded8fd60 100644 --- a/cmake/CompilerWarnings.cmake +++ b/cmake/CompilerWarnings.cmake @@ -90,6 +90,10 @@ function( -Wdouble-promotion # warn if float is implicit promoted to double -Wformat=2 # warn on security issues around functions that format output (ie printf) -Wimplicit-fallthrough # warn on statements that fallthrough without an explicit annotation + # -Wgnu-zero-variadic-macro-arguments (part of -pedantic) is triggered by every qCDebug() call and therefore results + # in a lot of noise. This warning is only notifying us that clang is emulating the GCC behaviour + # instead of the exact standard wording so we can safely ignore it + -Wno-gnu-zero-variadic-macro-arguments ) endif() diff --git a/launcher/java/JavaInstall.h b/launcher/java/JavaInstall.h index 30815b5a8..49e9ed06e 100644 --- a/launcher/java/JavaInstall.h +++ b/launcher/java/JavaInstall.h @@ -24,11 +24,11 @@ struct JavaInstall : public BaseVersion { JavaInstall() {} JavaInstall(QString id, QString arch, QString path) : id(id), arch(arch), path(path) {} - virtual QString descriptor() { return id.toString(); } + virtual QString descriptor() override { return id.toString(); } - virtual QString name() { return id.toString(); } + virtual QString name() override { return id.toString(); } - virtual QString typeString() const { return arch; } + virtual QString typeString() const override { return arch; } virtual bool operator<(BaseVersion& a) override; virtual bool operator>(BaseVersion& a) override; diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h index 80c31e456..60b8879c0 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.h +++ b/launcher/minecraft/mod/ResourceFolderModel.h @@ -330,7 +330,8 @@ void ResourceFolderModel::applyUpdates(QSet& current_set, QSet // When you have a Qt build with assertions turned on, proceeding here will abort the application if (added_set.size() > 0) { - beginInsertRows(QModelIndex(), m_resources.size(), m_resources.size() + added_set.size() - 1); + beginInsertRows(QModelIndex(), static_cast(m_resources.size()), + static_cast(m_resources.size() + added_set.size() - 1)); for (auto& added : added_set) { auto res = new_resources[added]; diff --git a/launcher/ui/widgets/VersionSelectWidget.h b/launcher/ui/widgets/VersionSelectWidget.h index 99729fbdf..d5ef1cc9f 100644 --- a/launcher/ui/widgets/VersionSelectWidget.h +++ b/launcher/ui/widgets/VersionSelectWidget.h @@ -82,7 +82,7 @@ class VersionSelectWidget : public QWidget { void selectedVersionChanged(BaseVersion::Ptr version); protected: - virtual void closeEvent(QCloseEvent*); + virtual void closeEvent(QCloseEvent*) override; private slots: void onTaskSucceeded(); From 44ff247f5f71ebeb95423ca37bf82d9913073522 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 18 Aug 2023 20:03:02 +0300 Subject: [PATCH 136/268] feat:refactored modpack ux Signed-off-by: Trial97 --- launcher/modplatform/ResourceAPI.h | 2 + .../helpers/NetworkResourceAPI.cpp | 3 +- .../ui/pages/modplatform/ResourceModel.cpp | 57 ++++++++++ launcher/ui/pages/modplatform/ResourceModel.h | 1 + .../ui/pages/modplatform/ResourcePage.cpp | 3 - .../modplatform/atlauncher/AtlFilterModel.cpp | 3 +- .../modplatform/atlauncher/AtlListModel.cpp | 56 +++++++--- .../pages/modplatform/atlauncher/AtlPage.cpp | 4 +- .../pages/modplatform/atlauncher/AtlPage.ui | 93 +++++++++------- .../ui/pages/modplatform/flame/FlameModel.cpp | 55 +++++++-- .../ui/pages/modplatform/flame/FlameModel.h | 5 +- .../ui/pages/modplatform/flame/FlamePage.cpp | 10 ++ .../ui/pages/modplatform/flame/FlamePage.h | 5 +- .../modplatform/import_ftb/ImportFTBPage.cpp | 29 ++++- .../modplatform/import_ftb/ImportFTBPage.h | 3 + .../modplatform/import_ftb/ImportFTBPage.ui | 52 ++++++++- .../modplatform/import_ftb/ListModel.cpp | 105 ++++++++++++++++-- .../pages/modplatform/import_ftb/ListModel.h | 22 ++++ .../modplatform/legacy_ftb/ListModel.cpp | 90 ++++++++++----- .../pages/modplatform/legacy_ftb/ListModel.h | 2 + .../ui/pages/modplatform/legacy_ftb/Page.cpp | 13 +++ .../ui/pages/modplatform/legacy_ftb/Page.h | 5 +- .../ui/pages/modplatform/legacy_ftb/Page.ui | 49 +++++--- .../modplatform/modrinth/ModrinthModel.cpp | 62 +++++++++-- .../modplatform/modrinth/ModrinthModel.h | 5 +- .../modplatform/modrinth/ModrinthPage.cpp | 10 ++ .../pages/modplatform/modrinth/ModrinthPage.h | 4 + .../modplatform/technic/TechnicModel.cpp | 72 ++++++++---- .../pages/modplatform/technic/TechnicModel.h | 2 + .../pages/modplatform/technic/TechnicPage.cpp | 13 +++ .../pages/modplatform/technic/TechnicPage.h | 5 +- launcher/ui/widgets/ProjectItem.cpp | 2 +- 32 files changed, 679 insertions(+), 163 deletions(-) diff --git a/launcher/modplatform/ResourceAPI.h b/launcher/modplatform/ResourceAPI.h index a92217a06..bd0d2824f 100644 --- a/launcher/modplatform/ResourceAPI.h +++ b/launcher/modplatform/ResourceAPI.h @@ -109,6 +109,8 @@ class ResourceAPI { }; struct ProjectInfoCallbacks { std::function on_succeed; + std::function on_fail; + std::function on_abort; }; struct DependencySearchArgs { diff --git a/launcher/modplatform/helpers/NetworkResourceAPI.cpp b/launcher/modplatform/helpers/NetworkResourceAPI.cpp index 46b966620..506eb187e 100644 --- a/launcher/modplatform/helpers/NetworkResourceAPI.cpp +++ b/launcher/modplatform/helpers/NetworkResourceAPI.cpp @@ -72,7 +72,8 @@ Task::Ptr NetworkResourceAPI::getProjectInfo(ProjectInfoArgs&& args, ProjectInfo callbacks.on_succeed(doc, args.pack); }); - + QObject::connect(job.get(), &NetJob::failed, [callbacks](QString reason) { callbacks.on_fail(reason); }); + QObject::connect(job.get(), &NetJob::aborted, [callbacks] { callbacks.on_abort(); }); return job; } diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp index 0a7edb7b7..96803531b 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.cpp +++ b/launcher/ui/pages/modplatform/ResourceModel.cpp @@ -132,6 +132,36 @@ void ResourceModel::search() if (hasActiveSearchJob()) return; + if (m_search_term.startsWith("#")) { + auto projectId = m_search_term.removeFirst(); + if (!projectId.isEmpty()) { + ResourceAPI::ProjectInfoCallbacks callbacks; + + // Use defaults if no callbacks are set + if (!callbacks.on_fail) + callbacks.on_fail = [this](QString reason) { + if (!s_running_models.constFind(this).value()) + return; + searchRequestFailed(reason, -1); + }; + if (!callbacks.on_abort) + callbacks.on_abort = [this] { + if (!s_running_models.constFind(this).value()) + return; + searchRequestAborted(); + }; + + if (!callbacks.on_succeed) + callbacks.on_succeed = [this](auto& doc, auto pack) { + if (!s_running_models.constFind(this).value()) + return; + searchRequestForOneSucceeded(doc); + }; + if (auto job = m_api->getProjectInfo({ projectId }, std::move(callbacks)); job) + runSearchJob(job); + return; + } + } auto args{ createSearchArguments() }; auto callbacks{ createSearchCallbacks() }; @@ -194,6 +224,12 @@ void ResourceModel::loadEntry(QModelIndex& entry) return; infoRequestSucceeded(doc, pack, entry); }; + if (!callbacks.on_fail) + callbacks.on_fail = [this](QString reason) { + if (!s_running_models.constFind(this).value()) + return; + QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load project info:%1").arg(reason)); + }; if (auto job = m_api->getProjectInfo(std::move(args), std::move(callbacks)); job) runInfoJob(job); @@ -372,6 +408,27 @@ void ResourceModel::searchRequestSucceeded(QJsonDocument& doc) endInsertRows(); } +void ResourceModel::searchRequestForOneSucceeded(QJsonDocument& doc) +{ + ModPlatform::IndexedPack::Ptr pack = std::make_shared(); + + try { + auto obj = Json::requireObject(doc); + if (obj.contains("data")) + obj = Json::requireObject(obj, "data"); + loadIndexedPack(*pack, obj); + } catch (const JSONValidationError& e) { + qDebug() << doc; + qWarning() << "Error while reading " << debugName() << " resource info: " << e.cause(); + } + + m_search_state = SearchState::Finished; + + beginInsertRows(QModelIndex(), m_packs.size(), m_packs.size() + 1); + m_packs.append(pack); + endInsertRows(); +} + void ResourceModel::searchRequestFailed([[maybe_unused]] QString reason, int network_error_code) { switch (network_error_code) { diff --git a/launcher/ui/pages/modplatform/ResourceModel.h b/launcher/ui/pages/modplatform/ResourceModel.h index cc813d6e6..ecf4f8f79 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.h +++ b/launcher/ui/pages/modplatform/ResourceModel.h @@ -149,6 +149,7 @@ class ResourceModel : public QAbstractListModel { private: /* Default search request callbacks */ void searchRequestSucceeded(QJsonDocument&); + void searchRequestForOneSucceeded(QJsonDocument&); void searchRequestFailed(QString reason, int network_error_code); void searchRequestAborted(); diff --git a/launcher/ui/pages/modplatform/ResourcePage.cpp b/launcher/ui/pages/modplatform/ResourcePage.cpp index c087e2be3..fc7d64a48 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.cpp +++ b/launcher/ui/pages/modplatform/ResourcePage.cpp @@ -44,9 +44,6 @@ #include #include "Markdown.h" -#include "ResourceDownloadTask.h" - -#include "minecraft/MinecraftInstance.h" #include "ui/dialogs/ResourceDownloadDialog.h" #include "ui/pages/modplatform/ResourceModel.h" diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlFilterModel.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlFilterModel.cpp index 9cd5eed53..dee3784e5 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlFilterModel.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlFilterModel.cpp @@ -67,9 +67,10 @@ bool FilterModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParen if (searchTerm.isEmpty()) { return true; } - QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); ATLauncher::IndexedPack pack = sourceModel()->data(index, Qt::UserRole).value(); + if (searchTerm.startsWith("#")) + return QString::number(pack.id) == searchTerm.mid(1); return pack.name.contains(searchTerm, Qt::CaseInsensitive); } diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp index 39f4f346a..b6fb71535 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp @@ -21,6 +21,7 @@ #include #include "net/ApiDownload.h" +#include "ui/widgets/ProjectItem.h" namespace Atl { @@ -46,27 +47,50 @@ QVariant ListModel::data(const QModelIndex& index, int role) const } ATLauncher::IndexedPack pack = modpacks.at(pos); - if (role == Qt::DisplayRole) { - return pack.name; - } else if (role == Qt::ToolTipRole) { - return pack.name; - } else if (role == Qt::DecorationRole) { - if (m_logoMap.contains(pack.safeName)) { - return (m_logoMap.value(pack.safeName)); + switch (role) { + case Qt::ToolTipRole: { + if (pack.description.length() > 100) { + // some magic to prevent to long tooltips and replace html linebreaks + QString edit = pack.description.left(97); + edit = edit.left(edit.lastIndexOf("
")).left(edit.lastIndexOf(" ")).append("..."); + return edit; + } + return pack.description; } - auto icon = APPLICATION->getThemedIcon("atlauncher-placeholder"); + case Qt::DecorationRole: { + if (m_logoMap.contains(pack.safeName)) { + return (m_logoMap.value(pack.safeName)); + } + auto icon = APPLICATION->getThemedIcon("atlauncher-placeholder"); - auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/images/%1.png").arg(pack.safeName.toLower()); - ((ListModel*)this)->requestLogo(pack.safeName, url); + auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/images/%1.png").arg(pack.safeName.toLower()); + ((ListModel*)this)->requestLogo(pack.safeName, url); - return icon; - } else if (role == Qt::UserRole) { - QVariant v; - v.setValue(pack); - return v; + return icon; + } + case Qt::UserRole: { + QVariant v; + v.setValue(pack); + return v; + } + case Qt::DisplayRole: + return pack.name; + case Qt::SizeHintRole: + return QSize(0, 58); + // Custom data + case UserDataTypes::TITLE: + return pack.name; + case UserDataTypes::DESCRIPTION: + return pack.description; + case UserDataTypes::SELECTED: + return false; + case UserDataTypes::INSTALLED: + return false; + default: + break; } - return QVariant(); + return {}; } void ListModel::request() diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp index 5e3b9ecf1..c7e800278 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp @@ -35,11 +35,11 @@ */ #include "AtlPage.h" +#include "ui/widgets/ProjectItem.h" #include "ui_AtlPage.h" #include "BuildConfig.h" -#include "AtlOptionalModDialog.h" #include "AtlUserInteractionSupportImpl.h" #include "modplatform/atlauncher/ATLPackInstallTask.h" #include "ui/dialogs/NewInstanceDialog.h" @@ -71,6 +71,8 @@ AtlPage::AtlPage(NewInstanceDialog* dialog, QWidget* parent) : QWidget(parent), connect(ui->sortByBox, &QComboBox::currentTextChanged, this, &AtlPage::onSortingSelectionChanged); connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &AtlPage::onSelectionChanged); connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &AtlPage::onVersionSelectionChanged); + + ui->packView->setItemDelegate(new ProjectItemDelegate(this)); } AtlPage::~AtlPage() diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlPage.ui b/launcher/ui/pages/modplatform/atlauncher/AtlPage.ui index 746aa6d1c..8b6747331 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlPage.ui +++ b/launcher/ui/pages/modplatform/atlauncher/AtlPage.ui @@ -11,44 +11,7 @@ - - - - - - true - - - - 96 - 48 - - - - - - - - true - - - true - - - - - - - Warning: This is still a work in progress. If you run into issues with the imported modpack, it may be a bug. - - - true - - - - - - + @@ -68,7 +31,34 @@ - + + + + + + true + + + true + + + + + + + true + + + + 96 + 48 + + + + + + + Search and filter... @@ -78,6 +68,31 @@ + + + + Search + + + + + + + + true + + + + Warning: This is still a work in progress. If you run into issues with the imported modpack, it may be a bug. + + + Qt::AlignCenter + + + true + + + diff --git a/launcher/ui/pages/modplatform/flame/FlameModel.cpp b/launcher/ui/pages/modplatform/flame/FlameModel.cpp index ff21d0109..e488f0787 100644 --- a/launcher/ui/pages/modplatform/flame/FlameModel.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameModel.cpp @@ -1,6 +1,8 @@ #include "FlameModel.h" #include #include "Application.h" +#include "modplatform/ResourceAPI.h" +#include "modplatform/flame/FlameAPI.h" #include "ui/widgets/ProjectItem.h" #include "net/ApiDownload.h" @@ -161,6 +163,25 @@ void ListModel::fetchMore(const QModelIndex& parent) void ListModel::performPaginatedSearch() { + if (currentSearchTerm.startsWith("#")) { + auto projectId = currentSearchTerm.removeFirst(); + if (!projectId.isEmpty()) { + ResourceAPI::ProjectInfoCallbacks callbacks; + + // Use defaults if no callbacks are set + if (!callbacks.on_fail) + callbacks.on_fail = [this](QString reason) { searchRequestFailed(reason); }; + + if (!callbacks.on_succeed) + callbacks.on_succeed = [this](auto& doc, auto pack) { searchRequestForOneSucceeded(doc); }; + static const FlameAPI api; + if (auto job = api.getProjectInfo({ projectId }, std::move(callbacks)); job) { + jobPtr = job; + jobPtr->start(); + } + return; + } + } auto netJob = makeShared("Flame::Search", APPLICATION->network()); auto searchUrl = QString( "https://api.curseforge.com/v1/mods/search?" @@ -189,23 +210,24 @@ void ListModel::searchWithTerm(const QString& term, int sort) } currentSearchTerm = term; currentSort = sort; - if (jobPtr) { + if (hasActiveSearchJob()) { jobPtr->abort(); searchState = ResetRequested; return; - } else { - beginResetModel(); - modpacks.clear(); - endResetModel(); - searchState = None; } + beginResetModel(); + modpacks.clear(); + endResetModel(); + searchState = None; + nextSearchOffset = 0; performPaginatedSearch(); } void Flame::ListModel::searchRequestFinished() { - jobPtr.reset(); + if (hasActiveSearchJob()) + return; QJsonParseError parse_error; QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); @@ -246,6 +268,25 @@ void Flame::ListModel::searchRequestFinished() endInsertRows(); } +void Flame::ListModel::searchRequestForOneSucceeded(QJsonDocument& doc) +{ + jobPtr.reset(); + + auto packObj = Json::ensureObject(doc.object(), "data"); + + Flame::IndexedPack pack; + try { + Flame::loadIndexedPack(pack, packObj); + } catch (const JSONValidationError& e) { + qWarning() << "Error while loading pack from CurseForge: " << e.cause(); + return; + } + + beginInsertRows(QModelIndex(), modpacks.size(), modpacks.size() + 1); + modpacks.append({ pack }); + endInsertRows(); +} + void Flame::ListModel::searchRequestFailed(QString reason) { jobPtr.reset(); diff --git a/launcher/ui/pages/modplatform/flame/FlameModel.h b/launcher/ui/pages/modplatform/flame/FlameModel.h index b3bc96b8c..cd73fce30 100644 --- a/launcher/ui/pages/modplatform/flame/FlameModel.h +++ b/launcher/ui/pages/modplatform/flame/FlameModel.h @@ -40,6 +40,8 @@ class ListModel : public QAbstractListModel { void getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback); void searchWithTerm(const QString& term, const int sort); + [[nodiscard]] bool hasActiveSearchJob() const { return jobPtr && jobPtr->isRunning(); } + private slots: void performPaginatedSearch(); @@ -48,6 +50,7 @@ class ListModel : public QAbstractListModel { void searchRequestFinished(); void searchRequestFailed(QString reason); + void searchRequestForOneSucceeded(QJsonDocument&); private: void requestLogo(QString file, QString url); @@ -63,7 +66,7 @@ class ListModel : public QAbstractListModel { int currentSort = 0; int nextSearchOffset = 0; enum SearchState { None, CanPossiblyFetchMore, ResetRequested, Finished } searchState = None; - NetJob::Ptr jobPtr; + Task::Ptr jobPtr; std::shared_ptr response = std::make_shared(); }; diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.cpp b/launcher/ui/pages/modplatform/flame/FlamePage.cpp index 183e16f90..79fcc8211 100644 --- a/launcher/ui/pages/modplatform/flame/FlamePage.cpp +++ b/launcher/ui/pages/modplatform/flame/FlamePage.cpp @@ -61,6 +61,11 @@ FlamePage::FlamePage(NewInstanceDialog* dialog, QWidget* parent) : QWidget(paren ui->versionSelectionBox->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); ui->versionSelectionBox->view()->parentWidget()->setMaximumHeight(300); + m_search_timer.setTimerType(Qt::TimerType::CoarseTimer); + m_search_timer.setSingleShot(true); + + connect(&m_search_timer, &QTimer::timeout, this, &FlamePage::triggerSearch); + // index is used to set the sorting with the curseforge api ui->sortByBox->addItem(tr("Sort by Featured")); ui->sortByBox->addItem(tr("Sort by Popularity")); @@ -90,6 +95,11 @@ bool FlamePage::eventFilter(QObject* watched, QEvent* event) triggerSearch(); keyEvent->accept(); return true; + } else { + if (m_search_timer.isActive()) + m_search_timer.stop(); + + m_search_timer.start(350); } } return QWidget::eventFilter(watched, event); diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.h b/launcher/ui/pages/modplatform/flame/FlamePage.h index ff5c79750..a45c9e404 100644 --- a/launcher/ui/pages/modplatform/flame/FlamePage.h +++ b/launcher/ui/pages/modplatform/flame/FlamePage.h @@ -39,7 +39,7 @@ #include #include -#include "tasks/Task.h" +#include #include "ui/pages/BasePage.h" namespace Ui { @@ -86,4 +86,7 @@ class FlamePage : public QWidget, public BasePage { Flame::IndexedPack current; int m_selected_version_index = -1; + + // Used to do instant searching with a delay to cache quick changes + QTimer m_search_timer; }; diff --git a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp index 5c9ff63b2..d3ead0837 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp +++ b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp @@ -17,6 +17,7 @@ */ #include "ImportFTBPage.h" +#include "ui/widgets/ProjectItem.h" #include "ui_ImportFTBPage.h" #include @@ -32,17 +33,30 @@ ImportFTBPage::ImportFTBPage(NewInstanceDialog* dialog, QWidget* parent) : QWidg ui->setupUi(this); { + currentModel = new FilterModel(this); listModel = new ListModel(this); + currentModel->setSourceModel(listModel); - ui->modpackList->setModel(listModel); + ui->modpackList->setModel(currentModel); ui->modpackList->setSortingEnabled(true); ui->modpackList->header()->hide(); ui->modpackList->setIndentation(0); ui->modpackList->setIconSize(QSize(42, 42)); + + for (int i = 0; i < currentModel->getAvailableSortings().size(); i++) { + ui->sortByBox->addItem(currentModel->getAvailableSortings().keys().at(i)); + } + + ui->sortByBox->setCurrentText(currentModel->translateCurrentSorting()); } connect(ui->modpackList->selectionModel(), &QItemSelectionModel::currentChanged, this, &ImportFTBPage::onPublicPackSelectionChanged); + connect(ui->sortByBox, &QComboBox::currentTextChanged, this, &ImportFTBPage::onSortingSelectionChanged); + + connect(ui->searchEdit, &QLineEdit::textChanged, this, &ImportFTBPage::triggerSearch); + + ui->modpackList->setItemDelegate(new ProjectItemDelegate(this)); ui->modpackList->selectionModel()->reset(); } @@ -86,7 +100,7 @@ void ImportFTBPage::onPublicPackSelectionChanged(QModelIndex now, QModelIndex pr onPackSelectionChanged(); return; } - Modpack selectedPack = listModel->data(now, Qt::UserRole).value(); + Modpack selectedPack = currentModel->data(now, Qt::UserRole).value(); onPackSelectionChanged(&selectedPack); } @@ -101,4 +115,15 @@ void ImportFTBPage::onPackSelectionChanged(Modpack* pack) dialog->setSuggestedPack(); } +void ImportFTBPage::onSortingSelectionChanged(QString sort) +{ + FilterModel::Sorting toSet = currentModel->getAvailableSortings().value(sort); + currentModel->setSorting(toSet); +} + +void ImportFTBPage::triggerSearch() +{ + currentModel->setSearchTerm(ui->searchEdit->text()); +} + } // namespace FTBImportAPP diff --git a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.h b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.h index 54c49f7b7..8e9661272 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.h +++ b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.h @@ -53,12 +53,15 @@ class ImportFTBPage : public QWidget, public BasePage { void suggestCurrent(); void onPackSelectionChanged(Modpack* pack = nullptr); private slots: + void onSortingSelectionChanged(QString data); void onPublicPackSelectionChanged(QModelIndex first, QModelIndex second); + void triggerSearch(); private: bool initialized = false; Modpack selected; ListModel* listModel = nullptr; + FilterModel* currentModel = nullptr; NewInstanceDialog* dialog = nullptr; Ui::ImportFTBPage* ui = nullptr; diff --git a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.ui b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.ui index 32d548b0d..5e09fb6d1 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.ui +++ b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.ui @@ -10,8 +10,8 @@ 1011 - - + + @@ -21,6 +21,54 @@ + + + + + + Search and filter... + + + true + + + + + + + Search + + + + + + + + + + + + 265 + 0 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + diff --git a/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp b/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp index dc78f451c..134bdc0c3 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp +++ b/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp @@ -23,7 +23,9 @@ #include #include #include "FileSystem.h" +#include "StringUtils.h" #include "modplatform/import_ftb/PackHelpers.h" +#include "ui/widgets/ProjectItem.h" namespace FTBImportAPP { @@ -71,18 +73,99 @@ QVariant ListModel::data(const QModelIndex& index, int role) const } auto pack = modpacks.at(pos); - if (role == Qt::DisplayRole) { - return pack.name; - } else if (role == Qt::DecorationRole) { - return pack.icon; - } else if (role == Qt::UserRole) { - QVariant v; - v.setValue(pack); - return v; - } else if (role == Qt::ToolTipRole) { - return tr("Minecraft %1").arg(pack.mcVersion); + if (role == Qt::ToolTipRole) { } - return QVariant(); + switch (role) { + case Qt::ToolTipRole: + return tr("Minecraft %1").arg(pack.mcVersion); + case Qt::DecorationRole: + return pack.icon; + case Qt::UserRole: { + QVariant v; + v.setValue(pack); + return v; + } + case Qt::DisplayRole: + return pack.name; + case Qt::SizeHintRole: + return QSize(0, 58); + // Custom data + case UserDataTypes::TITLE: + return pack.name; + case UserDataTypes::DESCRIPTION: + return tr("Minecraft %1").arg(pack.mcVersion); + case UserDataTypes::SELECTED: + return false; + case UserDataTypes::INSTALLED: + return false; + default: + break; + } + + return {}; +} + +FilterModel::FilterModel(QObject* parent) : QSortFilterProxyModel(parent) +{ + currentSorting = Sorting::ByGameVersion; + sortings.insert(tr("Sort by Name"), Sorting::ByName); + sortings.insert(tr("Sort by Game Version"), Sorting::ByGameVersion); +} + +bool FilterModel::lessThan(const QModelIndex& left, const QModelIndex& right) const +{ + Modpack leftPack = sourceModel()->data(left, Qt::UserRole).value(); + Modpack rightPack = sourceModel()->data(right, Qt::UserRole).value(); + + if (currentSorting == Sorting::ByGameVersion) { + Version lv(leftPack.mcVersion); + Version rv(rightPack.mcVersion); + return lv < rv; + + } else if (currentSorting == Sorting::ByName) { + return StringUtils::naturalCompare(leftPack.name, rightPack.name, Qt::CaseSensitive) >= 0; + } + + // UHM, some inavlid value set?! + qWarning() << "Invalid sorting set!"; + return true; +} + +bool FilterModel::filterAcceptsRow([[maybe_unused]] int sourceRow, [[maybe_unused]] const QModelIndex& sourceParent) const +{ + if (searchTerm.isEmpty()) { + return true; + } + QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); + Modpack pack = sourceModel()->data(index, Qt::UserRole).value(); + return pack.name.contains(searchTerm, Qt::CaseInsensitive); +} + +void FilterModel::setSearchTerm(const QString term) +{ + searchTerm = term.trimmed(); + invalidate(); +} + +const QMap FilterModel::getAvailableSortings() +{ + return sortings; +} + +QString FilterModel::translateCurrentSorting() +{ + return sortings.key(currentSorting); +} + +void FilterModel::setSorting(Sorting s) +{ + currentSorting = s; + invalidate(); +} + +FilterModel::Sorting FilterModel::getCurrentSorting() +{ + return currentSorting; } } // namespace FTBImportAPP \ No newline at end of file diff --git a/launcher/ui/pages/modplatform/import_ftb/ListModel.h b/launcher/ui/pages/modplatform/import_ftb/ListModel.h index c67aa8963..111928276 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ListModel.h +++ b/launcher/ui/pages/modplatform/import_ftb/ListModel.h @@ -20,11 +20,33 @@ #include #include +#include #include #include "modplatform/import_ftb/PackHelpers.h" namespace FTBImportAPP { +class FilterModel : public QSortFilterProxyModel { + Q_OBJECT + public: + FilterModel(QObject* parent = Q_NULLPTR); + enum Sorting { ByName, ByGameVersion }; + const QMap getAvailableSortings(); + QString translateCurrentSorting(); + void setSorting(Sorting sorting); + Sorting getCurrentSorting(); + void setSearchTerm(QString term); + + protected: + bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override; + bool lessThan(const QModelIndex& left, const QModelIndex& right) const override; + + private: + QMap sortings; + Sorting currentSorting; + QString searchTerm; +}; + class ListModel : public QAbstractListModel { Q_OBJECT diff --git a/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp b/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp index 356d919d9..49666cf6e 100644 --- a/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp +++ b/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp @@ -41,6 +41,7 @@ #include #include "StringUtils.h" +#include "ui/widgets/ProjectItem.h" #include #include @@ -79,7 +80,20 @@ bool FilterModel::lessThan(const QModelIndex& left, const QModelIndex& right) co bool FilterModel::filterAcceptsRow([[maybe_unused]] int sourceRow, [[maybe_unused]] const QModelIndex& sourceParent) const { - return true; + if (searchTerm.isEmpty()) { + return true; + } + QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); + Modpack pack = sourceModel()->data(index, Qt::UserRole).value(); + if (searchTerm.startsWith("#")) + return pack.packCode == searchTerm.mid(1); + return pack.name.contains(searchTerm, Qt::CaseInsensitive); +} + +void FilterModel::setSearchTerm(const QString term) +{ + searchTerm = term.trimmed(); + invalidate(); } const QMap FilterModel::getAvailableSortings() @@ -139,39 +153,57 @@ QVariant ListModel::data(const QModelIndex& index, int role) const } Modpack pack = modpacks.at(pos); - if (role == Qt::DisplayRole) { - return pack.name + "\n" + translatePackType(pack.type); - } else if (role == Qt::ToolTipRole) { - if (pack.description.length() > 100) { - // some magic to prevent to long tooltips and replace html linebreaks - QString edit = pack.description.left(97); - edit = edit.left(edit.lastIndexOf("
")).left(edit.lastIndexOf(" ")).append("..."); - return edit; + switch (role) { + case Qt::ToolTipRole: { + if (pack.description.length() > 100) { + // some magic to prevent to long tooltips and replace html linebreaks + QString edit = pack.description.left(97); + edit = edit.left(edit.lastIndexOf("
")).left(edit.lastIndexOf(" ")).append("..."); + return edit; + } + return pack.description; } - return pack.description; - } else if (role == Qt::DecorationRole) { - if (m_logoMap.contains(pack.logo)) { - return (m_logoMap.value(pack.logo)); + case Qt::DecorationRole: { + if (m_logoMap.contains(pack.logo)) { + return (m_logoMap.value(pack.logo)); + } + QIcon icon = APPLICATION->getThemedIcon("screenshot-placeholder"); + ((ListModel*)this)->requestLogo(pack.logo); + return icon; } - QIcon icon = APPLICATION->getThemedIcon("screenshot-placeholder"); - ((ListModel*)this)->requestLogo(pack.logo); - return icon; - } else if (role == Qt::ForegroundRole) { - if (pack.broken) { - // FIXME: Hardcoded color - return QColor(255, 0, 50); - } else if (pack.bugged) { - // FIXME: Hardcoded color - // bugged pack, currently only indicates bugged xml - return QColor(244, 229, 66); + case Qt::UserRole: { + QVariant v; + v.setValue(pack); + return v; } - } else if (role == Qt::UserRole) { - QVariant v; - v.setValue(pack); - return v; + case Qt::ForegroundRole: { + if (pack.broken) { + // FIXME: Hardcoded color + return QColor(255, 0, 50); + } else if (pack.bugged) { + // FIXME: Hardcoded color + // bugged pack, currently only indicates bugged xml + return QColor(244, 229, 66); + } + } + case Qt::DisplayRole: + return pack.name; + case Qt::SizeHintRole: + return QSize(0, 58); + // Custom data + case UserDataTypes::TITLE: + return pack.name; + case UserDataTypes::DESCRIPTION: + return pack.description; + case UserDataTypes::SELECTED: + return false; + case UserDataTypes::INSTALLED: + return false; + default: + break; } - return QVariant(); + return {}; } void ListModel::fill(ModpackList modpacks_) diff --git a/launcher/ui/pages/modplatform/legacy_ftb/ListModel.h b/launcher/ui/pages/modplatform/legacy_ftb/ListModel.h index 51a58d991..c802a4b56 100644 --- a/launcher/ui/pages/modplatform/legacy_ftb/ListModel.h +++ b/launcher/ui/pages/modplatform/legacy_ftb/ListModel.h @@ -25,6 +25,7 @@ class FilterModel : public QSortFilterProxyModel { QString translateCurrentSorting(); void setSorting(Sorting sorting); Sorting getCurrentSorting(); + void setSearchTerm(QString term); protected: bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override; @@ -33,6 +34,7 @@ class FilterModel : public QSortFilterProxyModel { private: QMap sortings; Sorting currentSorting; + QString searchTerm; }; class ListModel : public QAbstractListModel { diff --git a/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp b/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp index 0103bbaa2..4104f1391 100644 --- a/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp +++ b/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp @@ -35,6 +35,7 @@ */ #include "Page.h" +#include "ui/widgets/ProjectItem.h" #include "ui_Page.h" #include @@ -110,6 +111,8 @@ Page::Page(NewInstanceDialog* dialog, QWidget* parent) : QWidget(parent), dialog connect(ui->sortByBox, &QComboBox::currentTextChanged, this, &Page::onSortingSelectionChanged); connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &Page::onVersionSelectionItemChanged); + connect(ui->searchEdit, &QLineEdit::textChanged, this, &Page::triggerSearch); + connect(ui->publicPackList->selectionModel(), &QItemSelectionModel::currentChanged, this, &Page::onPublicPackSelectionChanged); connect(ui->thirdPartyPackList->selectionModel(), &QItemSelectionModel::currentChanged, this, &Page::onThirdPartyPackSelectionChanged); connect(ui->privatePackList->selectionModel(), &QItemSelectionModel::currentChanged, this, &Page::onPrivatePackSelectionChanged); @@ -125,6 +128,9 @@ Page::Page(NewInstanceDialog* dialog, QWidget* parent) : QWidget(parent), dialog ui->thirdPartyPackList->selectionModel()->reset(); ui->privatePackList->selectionModel()->reset(); + ui->publicPackList->setItemDelegate(new ProjectItemDelegate(this)); + ui->thirdPartyPackList->setItemDelegate(new ProjectItemDelegate(this)); + ui->privatePackList->setItemDelegate(new ProjectItemDelegate(this)); onTabChanged(ui->tabWidget->currentIndex()); } @@ -319,6 +325,8 @@ void Page::onTabChanged(int tab) currentModpackInfo = ui->publicPackDescription; } + triggerSearch(); + currentList->selectionModel()->reset(); QModelIndex idx = currentList->currentIndex(); if (idx.isValid()) { @@ -358,4 +366,9 @@ void Page::onRemovePackClicked() onPackSelectionChanged(); } +void Page::triggerSearch() +{ + currentModel->setSearchTerm(ui->searchEdit->text()); +} + } // namespace LegacyFTB diff --git a/launcher/ui/pages/modplatform/legacy_ftb/Page.h b/launcher/ui/pages/modplatform/legacy_ftb/Page.h index a12b0745d..4d317b7c0 100644 --- a/launcher/ui/pages/modplatform/legacy_ftb/Page.h +++ b/launcher/ui/pages/modplatform/legacy_ftb/Page.h @@ -43,7 +43,6 @@ #include "QObjectPtr.h" #include "modplatform/legacy_ftb/PackFetchTask.h" #include "modplatform/legacy_ftb/PackHelpers.h" -#include "tasks/Task.h" #include "ui/pages/BasePage.h" class NewInstanceDialog; @@ -56,8 +55,6 @@ class Page; class ListModel; class FilterModel; -class PrivatePackListModel; -class PrivatePackFilterModel; class PrivatePackManager; class Page : public QWidget, public BasePage { @@ -98,6 +95,8 @@ class Page : public QWidget, public BasePage { void onAddPackClicked(); void onRemovePackClicked(); + void triggerSearch(); + private: FilterModel* currentModel = nullptr; QTreeView* currentList = nullptr; diff --git a/launcher/ui/pages/modplatform/legacy_ftb/Page.ui b/launcher/ui/pages/modplatform/legacy_ftb/Page.ui index ad08dc255..56cba7485 100644 --- a/launcher/ui/pages/modplatform/legacy_ftb/Page.ui +++ b/launcher/ui/pages/modplatform/legacy_ftb/Page.ui @@ -10,8 +10,29 @@ 602 - - + + + + + + + Search and filter... + + + true + + + + + + + Search + + + + + + 0 @@ -36,9 +57,9 @@ - - true - + + true + @@ -50,10 +71,10 @@ - - true - - + + true + + @@ -104,16 +125,16 @@ - - true - - + + true + + - + diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp index ebc5556c6..16949ebae 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -38,8 +38,8 @@ #include "BuildConfig.h" #include "Json.h" -#include "minecraft/MinecraftInstance.h" -#include "minecraft/PackProfile.h" +#include "modplatform/modrinth/ModrinthAPI.h" +#include "net/NetJob.h" #include "ui/widgets/ProjectItem.h" #include "net/ApiDownload.h" @@ -130,7 +130,28 @@ bool ModpackListModel::setData(const QModelIndex& index, const QVariant& value, void ModpackListModel::performPaginatedSearch() { - // TODO: Move to standalone API + if (hasActiveSearchJob()) + return; + + if (currentSearchTerm.startsWith("#")) { + auto projectId = currentSearchTerm.removeFirst(); + if (!projectId.isEmpty()) { + ResourceAPI::ProjectInfoCallbacks callbacks; + + // Use defaults if no callbacks are set + if (!callbacks.on_fail) + callbacks.on_fail = [this](QString reason) { searchRequestFailed(reason); }; + + if (!callbacks.on_succeed) + callbacks.on_succeed = [this](auto& doc, auto pack) { searchRequestForOneSucceeded(doc); }; + static const ModrinthAPI api; + if (auto job = api.getProjectInfo({ projectId }, std::move(callbacks)); job) { + jobPtr = job; + jobPtr->start(); + } + return; + } + } // TODO: Move to standalone API auto netJob = makeShared("Modrinth::SearchModpack", APPLICATION->network()); auto searchAllUrl = QString(BuildConfig.MODRINTH_PROD_URL + "/search?" @@ -167,16 +188,17 @@ void ModpackListModel::performPaginatedSearch() void ModpackListModel::refresh() { - if (jobPtr) { + if (hasActiveSearchJob()) { jobPtr->abort(); searchState = ResetRequested; return; - } else { - beginResetModel(); - modpacks.clear(); - endResetModel(); - searchState = None; } + + beginResetModel(); + modpacks.clear(); + endResetModel(); + searchState = None; + nextSearchOffset = 0; performPaginatedSearch(); } @@ -307,9 +329,29 @@ void ModpackListModel::searchRequestFinished(QJsonDocument& doc_all) endInsertRows(); } +void ModpackListModel::searchRequestForOneSucceeded(QJsonDocument& doc) +{ + jobPtr.reset(); + + auto packObj = doc.object(); + + Modrinth::Modpack pack; + try { + Modrinth::loadIndexedPack(pack, packObj); + pack.id = Json::ensureString(packObj, "id", pack.id); + } catch (const JSONValidationError& e) { + qWarning() << "Error while loading mod from " << m_parent->debugName() << ": " << e.cause(); + return; + } + + beginInsertRows(QModelIndex(), modpacks.size(), modpacks.size() + 1); + modpacks.append({ pack }); + endInsertRows(); +} + void ModpackListModel::searchRequestFailed(QString reason) { - auto failed_action = jobPtr->getFailedActions().at(0); + auto failed_action = dynamic_cast(jobPtr.get())->getFailedActions().at(0); if (!failed_action->m_reply) { // Network error QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load modpacks.")); diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h index 721c69f55..f5e686a80 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h @@ -73,6 +73,8 @@ class ModpackListModel : public QAbstractListModel { void refresh(); void searchWithTerm(const QString& term, const int sort); + [[nodiscard]] bool hasActiveSearchJob() const { return jobPtr && jobPtr->isRunning(); } + void getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback); inline auto canFetchMore(const QModelIndex& parent) const -> bool override @@ -83,6 +85,7 @@ class ModpackListModel : public QAbstractListModel { public slots: void searchRequestFinished(QJsonDocument& doc_all); void searchRequestFailed(QString reason); + void searchRequestForOneSucceeded(QJsonDocument&); protected slots: @@ -111,7 +114,7 @@ class ModpackListModel : public QAbstractListModel { int nextSearchOffset = 0; enum SearchState { None, CanPossiblyFetchMore, ResetRequested, Finished } searchState = None; - NetJob::Ptr jobPtr; + Task::Ptr jobPtr; std::shared_ptr m_all_response = std::make_shared(); QByteArray m_specific_response; diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index 41fd5003f..72c9da358 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -64,6 +64,11 @@ ModrinthPage::ModrinthPage(NewInstanceDialog* dialog, QWidget* parent) : QWidget ui->versionSelectionBox->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); ui->versionSelectionBox->view()->parentWidget()->setMaximumHeight(300); + m_search_timer.setTimerType(Qt::TimerType::CoarseTimer); + m_search_timer.setSingleShot(true); + + connect(&m_search_timer, &QTimer::timeout, this, &ModrinthPage::triggerSearch); + ui->sortByBox->addItem(tr("Sort by Relevance")); ui->sortByBox->addItem(tr("Sort by Total Downloads")); ui->sortByBox->addItem(tr("Sort by Follows")); @@ -102,6 +107,11 @@ bool ModrinthPage::eventFilter(QObject* watched, QEvent* event) this->triggerSearch(); keyEvent->accept(); return true; + } else { + if (m_search_timer.isActive()) + m_search_timer.stop(); + + m_search_timer.start(350); } } return QObject::eventFilter(watched, event); diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h index b7054c886..0705ca99b 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h @@ -42,6 +42,7 @@ #include "modplatform/modrinth/ModrinthPackManifest.h" +#include #include namespace Ui { @@ -88,4 +89,7 @@ class ModrinthPage : public QWidget, public BasePage { Modrinth::Modpack current; QString selectedVersion; + + // Used to do instant searching with a delay to cache quick changes + QTimer m_search_timer; }; diff --git a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp index e8c5ac922..3cd1d9a2d 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp +++ b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp @@ -39,6 +39,7 @@ #include "Json.h" #include "net/ApiDownload.h" +#include "ui/widgets/ProjectItem.h" #include @@ -54,21 +55,47 @@ QVariant Technic::ListModel::data(const QModelIndex& index, int role) const } Modpack pack = modpacks.at(pos); - if (role == Qt::DisplayRole) { - return pack.name; - } else if (role == Qt::DecorationRole) { - if (m_logoMap.contains(pack.logoName)) { - return (m_logoMap.value(pack.logoName)); + switch (role) { + case Qt::ToolTipRole: { + if (pack.description.length() > 100) { + // some magic to prevent to long tooltips and replace html linebreaks + QString edit = pack.description.left(97); + edit = edit.left(edit.lastIndexOf("
")).left(edit.lastIndexOf(" ")).append("..."); + return edit; + } + return pack.description; } - QIcon icon = APPLICATION->getThemedIcon("screenshot-placeholder"); - ((ListModel*)this)->requestLogo(pack.logoName, pack.logoUrl); - return icon; - } else if (role == Qt::UserRole) { - QVariant v; - v.setValue(pack); - return v; + case Qt::DecorationRole: { + if (m_logoMap.contains(pack.logoName)) { + return (m_logoMap.value(pack.logoName)); + } + QIcon icon = APPLICATION->getThemedIcon("screenshot-placeholder"); + ((ListModel*)this)->requestLogo(pack.logoName, pack.logoUrl); + return icon; + } + case Qt::UserRole: { + QVariant v; + v.setValue(pack); + return v; + } + case Qt::DisplayRole: + return pack.name; + case Qt::SizeHintRole: + return QSize(0, 58); + // Custom data + case UserDataTypes::TITLE: + return pack.name; + case UserDataTypes::DESCRIPTION: + return pack.description; + case UserDataTypes::SELECTED: + return false; + case UserDataTypes::INSTALLED: + return false; + default: + break; } - return QVariant(); + + return {}; } int Technic::ListModel::columnCount(const QModelIndex& parent) const @@ -87,21 +114,25 @@ void Technic::ListModel::searchWithTerm(const QString& term) return; } currentSearchTerm = term; - if (jobPtr) { + if (hasActiveSearchJob()) { jobPtr->abort(); searchState = ResetRequested; return; - } else { - beginResetModel(); - modpacks.clear(); - endResetModel(); - searchState = None; } + + beginResetModel(); + modpacks.clear(); + endResetModel(); + searchState = None; + performSearch(); } void Technic::ListModel::performSearch() { + if (hasActiveSearchJob()) + return; + auto netJob = makeShared("Technic::Search", APPLICATION->network()); QString searchUrl = ""; if (currentSearchTerm.isEmpty()) { @@ -113,6 +144,9 @@ void Technic::ListModel::performSearch() } else if (currentSearchTerm.startsWith("https://api.technicpack.net/modpack/")) { searchUrl = QString("%1?build=%2").arg(currentSearchTerm, BuildConfig.TECHNIC_API_BUILD); searchMode = Single; + } else if (currentSearchTerm.startsWith("#")) { + searchUrl = QString("https://api.technicpack.net/modpack/%1?build=%2").arg(currentSearchTerm.mid(1), BuildConfig.TECHNIC_API_BUILD); + searchMode = Single; } else { searchUrl = QString("%1search?build=%2&q=%3").arg(BuildConfig.TECHNIC_API_BASE_URL, BuildConfig.TECHNIC_API_BUILD, currentSearchTerm); diff --git a/launcher/ui/pages/modplatform/technic/TechnicModel.h b/launcher/ui/pages/modplatform/technic/TechnicModel.h index d7a635d41..c0d13ae82 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicModel.h +++ b/launcher/ui/pages/modplatform/technic/TechnicModel.h @@ -58,6 +58,8 @@ class ListModel : public QAbstractListModel { void getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback); void searchWithTerm(const QString& term); + [[nodiscard]] bool hasActiveSearchJob() const { return jobPtr && jobPtr->isRunning(); } + private slots: void searchRequestFinished(); void searchRequestFailed(); diff --git a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp index 54b86feba..518d049e8 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp +++ b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp @@ -34,6 +34,7 @@ */ #include "TechnicPage.h" +#include "ui/widgets/ProjectItem.h" #include "ui_TechnicPage.h" #include @@ -59,8 +60,15 @@ TechnicPage::TechnicPage(NewInstanceDialog* dialog, QWidget* parent) : QWidget(p model = new Technic::ListModel(this); ui->packView->setModel(model); + m_search_timer.setTimerType(Qt::TimerType::CoarseTimer); + m_search_timer.setSingleShot(true); + + connect(&m_search_timer, &QTimer::timeout, this, &TechnicPage::triggerSearch); + connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &TechnicPage::onSelectionChanged); connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &TechnicPage::onVersionSelectionChanged); + + ui->packView->setItemDelegate(new ProjectItemDelegate(this)); } bool TechnicPage::eventFilter(QObject* watched, QEvent* event) @@ -71,6 +79,11 @@ bool TechnicPage::eventFilter(QObject* watched, QEvent* event) triggerSearch(); keyEvent->accept(); return true; + } else { + if (m_search_timer.isActive()) + m_search_timer.stop(); + + m_search_timer.start(350); } } return QWidget::eventFilter(watched, event); diff --git a/launcher/ui/pages/modplatform/technic/TechnicPage.h b/launcher/ui/pages/modplatform/technic/TechnicPage.h index 91b61eaf2..1e36fbd31 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicPage.h +++ b/launcher/ui/pages/modplatform/technic/TechnicPage.h @@ -35,12 +35,12 @@ #pragma once +#include #include #include #include "TechnicData.h" #include "net/NetJob.h" -#include "tasks/Task.h" #include "ui/pages/BasePage.h" namespace Ui { @@ -91,4 +91,7 @@ class TechnicPage : public QWidget, public BasePage { NetJob::Ptr jobPtr; std::shared_ptr response = std::make_shared(); + + // Used to do instant searching with a delay to cache quick changes + QTimer m_search_timer; }; diff --git a/launcher/ui/widgets/ProjectItem.cpp b/launcher/ui/widgets/ProjectItem.cpp index 1481c1b6b..60b92b28b 100644 --- a/launcher/ui/widgets/ProjectItem.cpp +++ b/launcher/ui/widgets/ProjectItem.cpp @@ -34,8 +34,8 @@ void ProjectItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& o icon_width = icon_size.width(); icon_height = icon_size.height(); - icon_x_margin = (rect.height() - icon_width) / 2; icon_y_margin = (rect.height() - icon_height) / 2; + icon_x_margin = icon_y_margin; // use same margins for consistency } // Centralize icon with a margin to separate from the other elements From 4c52b18bdd3112b5e92de61860fd8eff8ecdde9b Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 18 Aug 2023 20:19:51 +0300 Subject: [PATCH 137/268] replaced removeFirst with mid Signed-off-by: Trial97 --- launcher/ui/pages/modplatform/ResourceModel.cpp | 2 +- launcher/ui/pages/modplatform/flame/FlameModel.cpp | 2 +- launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp index 96803531b..74c1d2171 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.cpp +++ b/launcher/ui/pages/modplatform/ResourceModel.cpp @@ -133,7 +133,7 @@ void ResourceModel::search() return; if (m_search_term.startsWith("#")) { - auto projectId = m_search_term.removeFirst(); + auto projectId = m_search_term.mid(1); if (!projectId.isEmpty()) { ResourceAPI::ProjectInfoCallbacks callbacks; diff --git a/launcher/ui/pages/modplatform/flame/FlameModel.cpp b/launcher/ui/pages/modplatform/flame/FlameModel.cpp index e488f0787..e4388c246 100644 --- a/launcher/ui/pages/modplatform/flame/FlameModel.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameModel.cpp @@ -164,7 +164,7 @@ void ListModel::fetchMore(const QModelIndex& parent) void ListModel::performPaginatedSearch() { if (currentSearchTerm.startsWith("#")) { - auto projectId = currentSearchTerm.removeFirst(); + auto projectId = currentSearchTerm.mid(1); if (!projectId.isEmpty()) { ResourceAPI::ProjectInfoCallbacks callbacks; diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp index 16949ebae..36a1c47a9 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -134,7 +134,7 @@ void ModpackListModel::performPaginatedSearch() return; if (currentSearchTerm.startsWith("#")) { - auto projectId = currentSearchTerm.removeFirst(); + auto projectId = currentSearchTerm.mid(1); if (!projectId.isEmpty()) { ResourceAPI::ProjectInfoCallbacks callbacks; From 58efd3e9e27e09d55dac1d185f8de81cf2121f23 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 18 Aug 2023 21:43:57 +0300 Subject: [PATCH 138/268] fixed code scaning Signed-off-by: Trial97 --- launcher/modplatform/ResourceAPI.h | 2 +- launcher/ui/pages/modplatform/ResourceModel.cpp | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/launcher/modplatform/ResourceAPI.h b/launcher/modplatform/ResourceAPI.h index bd0d2824f..8af5ebd14 100644 --- a/launcher/modplatform/ResourceAPI.h +++ b/launcher/modplatform/ResourceAPI.h @@ -108,7 +108,7 @@ class ResourceAPI { void operator=(ProjectInfoArgs other) { pack = other.pack; } }; struct ProjectInfoCallbacks { - std::function on_succeed; + std::function on_succeed; std::function on_fail; std::function on_abort; }; diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp index 74c1d2171..018721f94 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.cpp +++ b/launcher/ui/pages/modplatform/ResourceModel.cpp @@ -152,7 +152,7 @@ void ResourceModel::search() }; if (!callbacks.on_succeed) - callbacks.on_succeed = [this](auto& doc, auto pack) { + callbacks.on_succeed = [this](auto& doc, auto& pack) { if (!s_running_models.constFind(this).value()) return; searchRequestForOneSucceeded(doc); @@ -219,9 +219,10 @@ void ResourceModel::loadEntry(QModelIndex& entry) // Use default if no callbacks are set if (!callbacks.on_succeed) - callbacks.on_succeed = [this, entry](auto& doc, auto pack) { + callbacks.on_succeed = [this, entry](auto& doc, auto& newpack) { if (!s_running_models.constFind(this).value()) return; + auto pack = newpack; infoRequestSucceeded(doc, pack, entry); }; if (!callbacks.on_fail) From 05094b7382aafc0a9dcda17c13d5294dbaa89c36 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 18 Aug 2023 22:24:30 +0300 Subject: [PATCH 139/268] more fixes Signed-off-by: Trial97 --- launcher/ui/pages/modplatform/flame/FlameModel.cpp | 2 +- launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/ui/pages/modplatform/flame/FlameModel.cpp b/launcher/ui/pages/modplatform/flame/FlameModel.cpp index e4388c246..17875a604 100644 --- a/launcher/ui/pages/modplatform/flame/FlameModel.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameModel.cpp @@ -173,7 +173,7 @@ void ListModel::performPaginatedSearch() callbacks.on_fail = [this](QString reason) { searchRequestFailed(reason); }; if (!callbacks.on_succeed) - callbacks.on_succeed = [this](auto& doc, auto pack) { searchRequestForOneSucceeded(doc); }; + callbacks.on_succeed = [this](auto& doc, auto& pack) { searchRequestForOneSucceeded(doc); }; static const FlameAPI api; if (auto job = api.getProjectInfo({ projectId }, std::move(callbacks)); job) { jobPtr = job; diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp index 36a1c47a9..efb1fe44e 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -143,7 +143,7 @@ void ModpackListModel::performPaginatedSearch() callbacks.on_fail = [this](QString reason) { searchRequestFailed(reason); }; if (!callbacks.on_succeed) - callbacks.on_succeed = [this](auto& doc, auto pack) { searchRequestForOneSucceeded(doc); }; + callbacks.on_succeed = [this](auto& doc, auto& pack) { searchRequestForOneSucceeded(doc); }; static const ModrinthAPI api; if (auto job = api.getProjectInfo({ projectId }, std::move(callbacks)); job) { jobPtr = job; From 0138cd65cb259966fa9902732bb03e3e7888cc64 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 19 Aug 2023 09:00:59 +0300 Subject: [PATCH 140/268] feat:neoforge can download forge mods Signed-off-by: Trial97 --- launcher/modplatform/flame/FlameAPI.h | 7 ++++--- launcher/modplatform/modrinth/ModrinthAPI.h | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h index a1256e175..e423189a8 100644 --- a/launcher/modplatform/flame/FlameAPI.h +++ b/launcher/modplatform/flame/FlameAPI.h @@ -47,9 +47,10 @@ class FlameAPI : public NetworkResourceAPI { return 4; // TODO: remove this once Quilt drops official Fabric support if (loaders & Quilt) // NOTE: Most if not all Fabric mods should work *currently* - return 4; // FIXME: implement multiple loaders filter - if (loaders & NeoForge) - return 6; + return 4; // FIXME: implement multiple loaders filter (this should be 5) + // TODO: remove this once NeoForge drops official Forge support + if (loaders & NeoForge) // NOTE: Most if not all Forge mods should work *currently* + return 1; // FIXME: implement multiple loaders filter (this should be 6) return 0; } diff --git a/launcher/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h index 0f150e97a..fb42c532c 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.h +++ b/launcher/modplatform/modrinth/ModrinthAPI.h @@ -43,6 +43,8 @@ class ModrinthAPI : public NetworkResourceAPI { l << getModLoaderString(loader); } } + if ((types & NeoForge) && (~types & Forge)) // Add Forge if NeoForge is in use, if Forge isn't already there + l << getModLoaderString(Forge); if ((types & Quilt) && (~types & Fabric)) // Add Fabric if Quilt is in use, if Fabric isn't already there l << getModLoaderString(Fabric); return l; From acf586ef82e13e5bb8c2eeace37085970d9917e4 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sat, 19 Aug 2023 10:21:46 +0200 Subject: [PATCH 141/268] chore: add fixme about UI code in Minecraftinstance Signed-off-by: Sefa Eyeoglu --- launcher/minecraft/MinecraftInstance.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index d930d6811..7e7b3554b 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -233,6 +233,7 @@ QSet MinecraftInstance::traits() const return profile->getTraits(); } +// FIXME: move UI code out of MinecraftInstance void MinecraftInstance::populateLaunchMenu(QMenu* menu) { QAction* normalLaunch = menu->addAction(tr("&Launch")); From d25e89a4c1304092f99b3516e91a5b989a110212 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 20 Aug 2023 00:48:46 +0300 Subject: [PATCH 142/268] refactor packwiz file write Signed-off-by: Trial97 --- .../minecraft/mod/tasks/ModFolderLoadTask.cpp | 2 +- launcher/modplatform/packwiz/Packwiz.cpp | 43 +++++++++++-------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp index 9f79ba098..2094df4fc 100644 --- a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp +++ b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp @@ -122,7 +122,7 @@ void ModFolderLoadTask::getFromMetadata() auto metadata = Metadata::get(m_index_dir, entry); if (!metadata.isValid()) { - return; + continue; } auto* mod = new Mod(m_mods_dir, metadata); diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 71f66bf3e..381476697 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include "FileSystem.h" #include "StringUtils.h" @@ -161,31 +163,36 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod) // Put TOML data into the file QTextStream in_stream(&index_file); - auto addToStream = [&in_stream](QString&& key, QString value) { in_stream << QString("%1 = \"%2\"\n").arg(key, value); }; - { - addToStream("name", mod.name); - addToStream("filename", mod.filename); - addToStream("side", mod.side); - - in_stream << QString("\n[download]\n"); - addToStream("mode", mod.mode); - addToStream("url", mod.url.toString()); - addToStream("hash-format", mod.hash_format); - addToStream("hash", mod.hash); - - in_stream << QString("\n[update]\n"); - in_stream << QString("[update.%1]\n").arg(ProviderCaps.name(mod.provider)); + toml::table update; switch (mod.provider) { case (ModPlatform::ResourceProvider::FLAME): - in_stream << QString("file-id = %1\n").arg(mod.file_id.toString()); - in_stream << QString("project-id = %1\n").arg(mod.project_id.toString()); + update = toml::table{ + { "file-id", mod.file_id.toInt() }, + { "project-id", mod.project_id.toInt() }, + }; break; case (ModPlatform::ResourceProvider::MODRINTH): - addToStream("mod-id", mod.mod_id().toString()); - addToStream("version", mod.version().toString()); + update = toml::table{ + { "mod-id", mod.mod_id().toString().toStdString() }, + { "version", mod.version().toString().toStdString() }, + }; break; } + auto tbl = toml::table{ { "name", mod.name.toStdString() }, + { "filename", mod.filename.toStdString() }, + { "side", mod.side.toStdString() }, + { "download", + toml::table{ + { "mode", mod.mode.toStdString() }, + { "url", mod.url.toString().toStdString() }, + { "hash-format", mod.hash_format.toStdString() }, + { "hash", mod.hash.toStdString() }, + } }, + { "update", toml::table{ { ProviderCaps.name(mod.provider), update } } } }; + std::stringstream ss; + ss << tbl; + in_stream << QString::fromStdString(ss.str()); } index_file.flush(); From d3c7850f53a87d5d6b5f7aab3c6c56f3808c7071 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 20 Aug 2023 00:16:56 +0000 Subject: [PATCH 143/268] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:nixos/nixpkgs/f0451844bbdf545f696f029d1448de4906c7f753' (2023-08-12) → 'github:nixos/nixpkgs/ca3c9ac9f4cdd4bea19f592b32bb59b74ab7d783' (2023-08-19) • Updated input 'pre-commit-hooks': 'github:cachix/pre-commit-hooks.nix/c5ac3aa3324bd8aebe8622a3fc92eeb3975d317a' (2023-08-11) → 'github:cachix/pre-commit-hooks.nix/7e3517c03d46159fdbf8c0e5c97f82d5d4b0c8fa' (2023-08-17) --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index bfe151758..28ef60f30 100644 --- a/flake.lock +++ b/flake.lock @@ -91,11 +91,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1691853136, - "narHash": "sha256-wTzDsRV4HN8A2Sl0SVQY0q8ILs90CD43Ha//7gNZE+E=", + "lastModified": 1692463654, + "narHash": "sha256-F8hZmsQINI+S6UROM4jyxAMbQLtzE44pI8Nk6NtMdao=", "owner": "nixos", "repo": "nixpkgs", - "rev": "f0451844bbdf545f696f029d1448de4906c7f753", + "rev": "ca3c9ac9f4cdd4bea19f592b32bb59b74ab7d783", "type": "github" }, "original": { @@ -138,11 +138,11 @@ ] }, "locked": { - "lastModified": 1691747570, - "narHash": "sha256-J3fnIwJtHVQ0tK2JMBv4oAmII+1mCdXdpeCxtIsrL2A=", + "lastModified": 1692274144, + "narHash": "sha256-BxTQuRUANQ81u8DJznQyPmRsg63t4Yc+0kcyq6OLz8s=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "c5ac3aa3324bd8aebe8622a3fc92eeb3975d317a", + "rev": "7e3517c03d46159fdbf8c0e5c97f82d5d4b0c8fa", "type": "github" }, "original": { From 963627fe9855c8903926f4e2809727169c1742e9 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 20 Aug 2023 13:16:02 +0200 Subject: [PATCH 144/268] Revert "chore: better explain quilt loader beacon" This reverts commit a2a09ffe01fe8eb6cd1f557b0feb98ed0271151e. Signed-off-by: Sefa Eyeoglu --- launcher/minecraft/MinecraftInstance.cpp | 2 +- launcher/ui/pages/global/MinecraftPage.ui | 5 +---- launcher/ui/pages/instance/InstanceSettingsPage.ui | 5 +---- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 699aaffaf..c74ac7ae6 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -3,7 +3,7 @@ * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 Jamie Mansfield - * Copyright (C) 2023 TheKodeToad + * Copyright (C) 2023 TheKodeToad \ * Copyright (c) 2023 seth * * This program is free software: you can redistribute it and/or modify diff --git a/launcher/ui/pages/global/MinecraftPage.ui b/launcher/ui/pages/global/MinecraftPage.ui index 98e90e4ec..f8ac4896a 100644 --- a/launcher/ui/pages/global/MinecraftPage.ui +++ b/launcher/ui/pages/global/MinecraftPage.ui @@ -199,10 +199,7 @@ - Disable Quilt Loader Beacon - - - Disable Quilt loader's beacon for counting monthly active users + Disable Quilt's Beacon diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index fcc2a5d0a..1b443f54c 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -598,10 +598,7 @@ - Disable Quilt Loader Beacon - - - Disable Quilt loader's beacon for counting monthly active users + Disable Quilt's Beacon From 5b4dcae7d948cd3516bc691f6e696f0951041721 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 20 Aug 2023 13:17:17 +0200 Subject: [PATCH 145/268] Revert "feat: add toggle for quilt beacon" This reverts commit 89aaedc06c3eb7a035d8be593a7bbe417cb2f712. Signed-off-by: Sefa Eyeoglu --- launcher/Application.cpp | 4 ---- launcher/minecraft/MinecraftInstance.cpp | 13 +---------- launcher/ui/pages/global/MinecraftPage.cpp | 6 ----- launcher/ui/pages/global/MinecraftPage.ui | 16 -------------- .../pages/instance/InstanceSettingsPage.cpp | 13 ----------- .../ui/pages/instance/InstanceSettingsPage.ui | 22 ------------------- 6 files changed, 1 insertion(+), 73 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 5a952c74f..000104656 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -9,7 +9,6 @@ * Copyright (C) 2022 Tayou * Copyright (C) 2023 TheKodeToad * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> - * Copyright (C) 2023 seth * * 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 @@ -580,9 +579,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) m_settings->registerSetting("IgnoreJavaCompatibility", false); m_settings->registerSetting("IgnoreJavaWizard", false); - // Mod loader settings - m_settings->registerSetting("DisableQuiltBeacon", false); - // Native library workarounds m_settings->registerSetting("UseNativeOpenAL", false); m_settings->registerSetting("CustomOpenALPath", ""); diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index c74ac7ae6..0da97c146 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -3,8 +3,7 @@ * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 Jamie Mansfield - * Copyright (C) 2023 TheKodeToad \ - * Copyright (c) 2023 seth + * Copyright (C) 2023 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 @@ -185,10 +184,6 @@ void MinecraftInstance::loadSpecificSettings() m_settings->registerOverride(global_settings->getSetting("CloseAfterLaunch"), miscellaneousOverride); m_settings->registerOverride(global_settings->getSetting("QuitAfterGameStop"), miscellaneousOverride); - // Mod loader specific options - auto modLoaderSettings = m_settings->registerSetting("OverrideModLoaderSettings", false); - m_settings->registerOverride(global_settings->getSetting("DisableQuiltBeacon"), modLoaderSettings); - m_settings->set("InstanceType", "OneSix"); } @@ -434,12 +429,6 @@ QStringList MinecraftInstance::extraArguments() list.append("-javaagent:" + jar[0] + (agent->argument().isEmpty() ? "" : "=" + agent->argument())); } - { - const auto loaders = version->getModLoaders(); - if (loaders.has_value() && loaders.value() & ResourceAPI::Quilt && settings()->get("DisableQuiltBeacon").toBool()) - list.append("-Dloader.disable_beacon=true"); - } - { QString openALPath; QString glfwPath; diff --git a/launcher/ui/pages/global/MinecraftPage.cpp b/launcher/ui/pages/global/MinecraftPage.cpp index 1c7747210..f0d50b638 100644 --- a/launcher/ui/pages/global/MinecraftPage.cpp +++ b/launcher/ui/pages/global/MinecraftPage.cpp @@ -2,7 +2,6 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield - * Copyright (C) 2023 seth * * 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 @@ -119,9 +118,6 @@ void MinecraftPage::applySettings() // Miscellaneous s->set("CloseAfterLaunch", ui->closeAfterLaunchCheck->isChecked()); s->set("QuitAfterGameStop", ui->quitAfterGameStopCheck->isChecked()); - - // Mod loader settings - s->set("DisableQuiltBeacon", ui->disableQuiltBeaconCheckBox->isChecked()); } void MinecraftPage::loadSettings() @@ -172,8 +168,6 @@ void MinecraftPage::loadSettings() ui->closeAfterLaunchCheck->setChecked(s->get("CloseAfterLaunch").toBool()); ui->quitAfterGameStopCheck->setChecked(s->get("QuitAfterGameStop").toBool()); - - ui->disableQuiltBeaconCheckBox->setChecked(s->get("DisableQuiltBeacon").toBool()); } void MinecraftPage::retranslate() diff --git a/launcher/ui/pages/global/MinecraftPage.ui b/launcher/ui/pages/global/MinecraftPage.ui index f8ac4896a..ef4a22874 100644 --- a/launcher/ui/pages/global/MinecraftPage.ui +++ b/launcher/ui/pages/global/MinecraftPage.ui @@ -190,22 +190,6 @@ Tweaks - - - - Mod loader settings - - - - - - Disable Quilt's Beacon - - - - - - diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index 108f2c274..d5dbb80b8 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -3,7 +3,6 @@ * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * Copyright (C) 2022 Sefa Eyeoglu - * Copyright (C) 2023 seth * * 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 @@ -254,14 +253,6 @@ void InstanceSettingsPage::applySettings() m_settings->reset("InstanceAccountId"); } - bool overrideModLoaderSettings = ui->modLoaderSettingsGroupBox->isChecked(); - m_settings->set("OverrideModLoaderSettings", overrideModLoaderSettings); - if (overrideModLoaderSettings) { - m_settings->set("DisableQuiltBeacon", ui->disableQuiltBeaconCheckBox->isChecked()); - } else { - m_settings->reset("DisableQuiltBeacon"); - } - // FIXME: This should probably be called by a signal instead m_instance->updateRuntimeContext(); } @@ -365,10 +356,6 @@ void InstanceSettingsPage::loadSettings() ui->instanceAccountGroupBox->setChecked(m_settings->get("UseAccountForInstance").toBool()); updateAccountsMenu(); - - // Mod loader specific settings - ui->modLoaderSettingsGroupBox->setChecked(m_settings->get("OverrideModLoaderSettings").toBool()); - ui->disableQuiltBeaconCheckBox->setChecked(m_settings->get("DisableQuiltBeacon").toBool()); } void InstanceSettingsPage::on_javaDetectBtn_clicked() diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index 1b443f54c..81cf7093e 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -583,28 +583,6 @@ Miscellaneous - - - - true - - - false - - - Mod loader settings - - - - - - Disable Quilt's Beacon - - - - - - From 254444470f020b086648ac496ebfffb7d3e9ce05 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 20 Aug 2023 16:40:58 +0300 Subject: [PATCH 146/268] renamed enum type Signed-off-by: Trial97 --- launcher/modplatform/ModIndex.cpp | 40 +++++++++------------ launcher/modplatform/ModIndex.h | 29 ++++++++------- launcher/modplatform/flame/FlamePackIndex.h | 2 -- 3 files changed, 30 insertions(+), 41 deletions(-) diff --git a/launcher/modplatform/ModIndex.cpp b/launcher/modplatform/ModIndex.cpp index 04fd42a7a..17edcdb5d 100644 --- a/launcher/modplatform/ModIndex.cpp +++ b/launcher/modplatform/ModIndex.cpp @@ -24,30 +24,32 @@ namespace ModPlatform { -static const QMap s_indexed_version_type_names = { { "release", IndexedVersionType::Enum::Release }, - { "beta", IndexedVersionType::Enum::Beta }, - { "alpha", IndexedVersionType::Enum::Alpha } }; +static const QMap s_indexed_version_type_names = { + { "release", IndexedVersionType::VersionType::Release }, + { "beta", IndexedVersionType::VersionType::Beta }, + { "alpha", IndexedVersionType::VersionType::Alpha } +}; IndexedVersionType::IndexedVersionType(const QString& type) : IndexedVersionType(enumFromString(type)) {} -IndexedVersionType::IndexedVersionType(int type) +IndexedVersionType::IndexedVersionType(int flame_type) { - switch (type) { + switch (flame_type) { case 1: - m_type = IndexedVersionType::Enum::Release; + m_type = IndexedVersionType::VersionType::Release; break; case 2: - m_type = IndexedVersionType::Enum::Beta; + m_type = IndexedVersionType::VersionType::Beta; break; case 3: - m_type = IndexedVersionType::Enum::Alpha; + m_type = IndexedVersionType::VersionType::Alpha; break; default: - m_type = IndexedVersionType::Enum::UNKNOWN; + m_type = IndexedVersionType::VersionType::Unknown; } } -IndexedVersionType::IndexedVersionType(const IndexedVersionType::Enum& type) +IndexedVersionType::IndexedVersionType(const IndexedVersionType::VersionType& type) { m_type = type; } @@ -63,24 +65,14 @@ IndexedVersionType& IndexedVersionType::operator=(const IndexedVersionType& othe return *this; } -const QString IndexedVersionType::toString(const IndexedVersionType::Enum& type) +const QString IndexedVersionType::toString(const IndexedVersionType::VersionType& type) { - switch (type) { - case IndexedVersionType::Enum::Release: - return "release"; - case IndexedVersionType::Enum::Beta: - return "beta"; - case IndexedVersionType::Enum::Alpha: - return "alpha"; - case IndexedVersionType::Enum::UNKNOWN: - default: - return "unknown"; - } + return s_indexed_version_type_names.key(type, "unknown"); } -IndexedVersionType::Enum IndexedVersionType::enumFromString(const QString& type) +IndexedVersionType::VersionType IndexedVersionType::enumFromString(const QString& type) { - return s_indexed_version_type_names.value(type, IndexedVersionType::Enum::UNKNOWN); + return s_indexed_version_type_names.value(type, IndexedVersionType::VersionType::Unknown); } auto ProviderCapabilities::name(ResourceProvider p) -> const char* diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index e430f2b00..e56c282a2 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -57,32 +57,32 @@ struct DonationData { }; struct IndexedVersionType { - enum class Enum { Release = 1, Beta, Alpha, UNKNOWN }; + enum class VersionType { Release = 1, Beta, Alpha, Unknown }; IndexedVersionType(const QString& type); - IndexedVersionType(int type); - IndexedVersionType(const IndexedVersionType::Enum& type); + IndexedVersionType(int flame_type); + IndexedVersionType(const IndexedVersionType::VersionType& type); IndexedVersionType(const IndexedVersionType& type); - IndexedVersionType() : IndexedVersionType(IndexedVersionType::Enum::UNKNOWN) {} - static const QString toString(const IndexedVersionType::Enum& type); - static IndexedVersionType::Enum enumFromString(const QString& type); - bool isValid() const { return m_type != IndexedVersionType::Enum::UNKNOWN; } + IndexedVersionType() : IndexedVersionType(IndexedVersionType::VersionType::Unknown) {} + static const QString toString(const IndexedVersionType::VersionType& type); + static IndexedVersionType::VersionType enumFromString(const QString& type); + bool isValid() const { return m_type != IndexedVersionType::VersionType::Unknown; } IndexedVersionType& operator=(const IndexedVersionType& other); bool operator==(const IndexedVersionType& other) const { return m_type == other.m_type; } - bool operator==(const IndexedVersionType::Enum& type) const { return m_type == type; } + bool operator==(const IndexedVersionType::VersionType& type) const { return m_type == type; } bool operator!=(const IndexedVersionType& other) const { return m_type != other.m_type; } - bool operator!=(const IndexedVersionType::Enum& type) const { return m_type != type; } + bool operator!=(const IndexedVersionType::VersionType& type) const { return m_type != type; } bool operator<(const IndexedVersionType& other) const { return m_type < other.m_type; } - bool operator<(const IndexedVersionType::Enum& type) const { return m_type < type; } + bool operator<(const IndexedVersionType::VersionType& type) const { return m_type < type; } bool operator<=(const IndexedVersionType& other) const { return m_type <= other.m_type; } - bool operator<=(const IndexedVersionType::Enum& type) const { return m_type <= type; } + bool operator<=(const IndexedVersionType::VersionType& type) const { return m_type <= type; } bool operator>(const IndexedVersionType& other) const { return m_type > other.m_type; } - bool operator>(const IndexedVersionType::Enum& type) const { return m_type > type; } + bool operator>(const IndexedVersionType::VersionType& type) const { return m_type > type; } bool operator>=(const IndexedVersionType& other) const { return m_type >= other.m_type; } - bool operator>=(const IndexedVersionType::Enum& type) const { return m_type >= type; } + bool operator>=(const IndexedVersionType::VersionType& type) const { return m_type >= type; } QString toString() const { return toString(m_type); } - IndexedVersionType::Enum m_type; + IndexedVersionType::VersionType m_type; }; struct Dependency { @@ -159,7 +159,6 @@ struct IndexedPack { return std::any_of(versions.constBegin(), versions.constEnd(), [](auto const& v) { return v.is_currently_selected; }); } }; -QString getMetaURL(ResourceProvider provider, QVariant projectID); struct OverrideDep { QString quilt; diff --git a/launcher/modplatform/flame/FlamePackIndex.h b/launcher/modplatform/flame/FlamePackIndex.h index 5f642aceb..b2a12a67f 100644 --- a/launcher/modplatform/flame/FlamePackIndex.h +++ b/launcher/modplatform/flame/FlamePackIndex.h @@ -6,8 +6,6 @@ #include #include "modplatform/ModIndex.h" -#include "modplatform/ModIndex.h" - namespace Flame { struct ModpackAuthor { From 19316e22b40b47cf88856592b9ff528e860aa4a5 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 20 Aug 2023 22:46:44 +0300 Subject: [PATCH 147/268] fixed status label after instance remove Signed-off-by: Trial97 --- launcher/ui/MainWindow.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 73b8dbe5e..9228928fc 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -560,7 +560,8 @@ void MainWindow::updateLaunchButton() launchMenu->clear(); else launchMenu = new QMenu(this); - m_selectedInstance->populateLaunchMenu(launchMenu); + if (m_selectedInstance) + m_selectedInstance->populateLaunchMenu(launchMenu); ui->actionLaunchInstance->setMenu(launchMenu); } @@ -1351,10 +1352,11 @@ void MainWindow::on_actionDeleteInstance_triggered() if (APPLICATION->instances()->trashInstance(id)) { ui->actionUndoTrashInstance->setEnabled(APPLICATION->instances()->trashedSomething()); - return; + } else { + APPLICATION->instances()->deleteInstance(id); } - - APPLICATION->instances()->deleteInstance(id); + APPLICATION->settings()->set("SelectedInstance", QString()); + selectionBad(); } void MainWindow::on_actionExportInstanceZip_triggered() @@ -1652,10 +1654,6 @@ void MainWindow::instanceChanged(const QModelIndex& current, [[maybe_unused]] co connect(m_selectedInstance.get(), &BaseInstance::runningStatusChanged, this, &MainWindow::refreshCurrentInstance); connect(m_selectedInstance.get(), &BaseInstance::profilerChanged, this, &MainWindow::refreshCurrentInstance); } else { - ui->instanceToolBar->setEnabled(false); - setInstanceActionsEnabled(false); - ui->actionLaunchInstance->setEnabled(false); - ui->actionKillInstance->setEnabled(false); APPLICATION->settings()->set("SelectedInstance", QString()); selectionBad(); return; @@ -1680,6 +1678,7 @@ void MainWindow::selectionBad() { // start by reseting everything... m_selectedInstance = nullptr; + m_statusLeft->setText(tr("No instance selected")); statusBar()->clearMessage(); ui->instanceToolBar->setEnabled(false); From 8e5be6f4203f777ba10d61608c2d5b80c05cc0d4 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 21 Aug 2023 00:09:46 +0300 Subject: [PATCH 148/268] added suport for atlauncher browser download Signed-off-by: Trial97 --- .../atlauncher/ATLPackInstallTask.cpp | 98 ++++++++++++++++--- launcher/ui/dialogs/BlockedModsDialog.cpp | 6 +- launcher/ui/dialogs/BlockedModsDialog.h | 3 +- 3 files changed, 88 insertions(+), 19 deletions(-) diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp index e5771b7cd..a3f26e1f7 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp @@ -37,6 +37,7 @@ #include "ATLPackInstallTask.h" #include +#include #include @@ -50,6 +51,7 @@ #include "minecraft/MinecraftInstance.h" #include "minecraft/OneSixVersionFormat.h" #include "minecraft/PackProfile.h" +#include "modplatform/atlauncher/ATLPackManifest.h" #include "net/ChecksumValidator.h" #include "settings/INISettingsObject.h" @@ -57,6 +59,7 @@ #include "Application.h" #include "BuildConfig.h" +#include "ui/dialogs/BlockedModsDialog.h" namespace ATLauncher { @@ -717,6 +720,8 @@ void PackInstallTask::downloadMods() jarmods.clear(); jobPtr.reset(new NetJob(tr("Mod download"), APPLICATION->network())); + + QList blocked_mods; for (const auto& mod : m_version.mods) { // skip non-client mods if (!mod.client) @@ -731,9 +736,10 @@ void PackInstallTask::downloadMods() case DownloadType::Server: url = BuildConfig.ATL_DOWNLOAD_SERVER_URL + mod.url; break; - case DownloadType::Browser: - emitFailed(tr("Unsupported download type: %1").arg(mod.download_raw)); - return; + case DownloadType::Browser: { + blocked_mods.append(mod); + continue; + } case DownloadType::Direct: url = mod.url; break; @@ -805,24 +811,86 @@ void PackInstallTask::downloadMods() modsToCopy[entry->getFullPath()] = path; } } + if (!blocked_mods.isEmpty()) { + QList mods; + + for (auto mod : blocked_mods) { + BlockedMod blocked_mod; + blocked_mod.name = mod.file; + blocked_mod.websiteUrl = mod.url; + blocked_mod.hash = mod.md5; + blocked_mod.matched = false; + blocked_mod.localPath = ""; + + mods.append(blocked_mod); + } + + qWarning() << "Blocked mods found, displaying mod list"; + + BlockedModsDialog message_dialog(nullptr, 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."), + mods, "md5"); + + message_dialog.setModal(true); + + if (message_dialog.exec()) { + qDebug() << "Post dialog blocked mods list: " << mods; + for (auto blocked : mods) { + if (!blocked.matched) { + qDebug() << blocked.name << "was not matched to a local file, skipping copy"; + continue; + } + auto modIter = std::find_if(blocked_mods.begin(), blocked_mods.end(), + [blocked](VersionMod mod) { return mod.url == blocked.websiteUrl; }); + if (modIter == blocked_mods.end()) + continue; + auto mod = *modIter; + if (mod.type == ModType::Extract || mod.type == ModType::TexturePackExtract || mod.type == ModType::ResourcePackExtract) { + modsToExtract.insert(blocked.localPath, mod); + } else if (mod.type == ModType::Decomp) { + modsToDecomp.insert(blocked.localPath, mod); + } else { + auto relpath = getDirForModType(mod.type, mod.type_raw); + if (relpath == Q_NULLPTR) + continue; + + auto path = FS::PathCombine(m_stagingPath, "minecraft", relpath, mod.file); + + if (mod.type == ModType::Forge) { + auto ver = getComponentVersion("net.minecraftforge", mod.version); + if (ver) { + componentsToInstall.insert("net.minecraftforge", ver); + continue; + } + + qDebug() << "Jarmod: " + path; + jarmods.push_back(path); + } + + if (mod.type == ModType::Jar) { + qDebug() << "Jarmod: " + path; + jarmods.push_back(path); + } + + modsToCopy[blocked.localPath] = path; + } + } + } else { + emitFailed(tr("Unknown download type: %1").arg("browser")); + return; + } + } connect(jobPtr.get(), &NetJob::succeeded, this, &PackInstallTask::onModsDownloaded); - connect(jobPtr.get(), &NetJob::failed, [&](QString reason) { - abortable = false; - jobPtr.reset(); - emitFailed(reason); - }); - connect(jobPtr.get(), &NetJob::progress, [&](qint64 current, qint64 total) { + connect(jobPtr.get(), &NetJob::progress, [this](qint64 current, qint64 total) { setDetails(tr("%1 out of %2 complete").arg(current).arg(total)); abortable = true; setProgress(current, total); }); connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propagateStepProgress); - connect(jobPtr.get(), &NetJob::aborted, [&] { - abortable = false; - jobPtr.reset(); - emitAborted(); - }); + connect(jobPtr.get(), &NetJob::aborted, &PackInstallTask::emitAborted); + connect(jobPtr.get(), &NetJob::failed, &PackInstallTask::emitFailed); jobPtr->start(); } @@ -843,7 +911,7 @@ void PackInstallTask::onModsDownloaded() QtConcurrent::run(QThreadPool::globalInstance(), this, &PackInstallTask::extractMods, modsToExtract, modsToDecomp, modsToCopy); #endif connect(&m_modExtractFutureWatcher, &QFutureWatcher::finished, this, &PackInstallTask::onModsExtracted); - connect(&m_modExtractFutureWatcher, &QFutureWatcher::canceled, this, [&]() { emitAborted(); }); + connect(&m_modExtractFutureWatcher, &QFutureWatcher::canceled, this, &PackInstallTask::emitAborted); m_modExtractFutureWatcher.setFuture(m_modExtractFuture); } else { install(); diff --git a/launcher/ui/dialogs/BlockedModsDialog.cpp b/launcher/ui/dialogs/BlockedModsDialog.cpp index 727c06148..f105b4b5f 100644 --- a/launcher/ui/dialogs/BlockedModsDialog.cpp +++ b/launcher/ui/dialogs/BlockedModsDialog.cpp @@ -41,8 +41,8 @@ #include #include -BlockedModsDialog::BlockedModsDialog(QWidget* parent, const QString& title, const QString& text, QList& mods) - : QDialog(parent), ui(new Ui::BlockedModsDialog), m_mods(mods) +BlockedModsDialog::BlockedModsDialog(QWidget* parent, const QString& title, const QString& text, QList& mods, QString hash_type) + : QDialog(parent), ui(new Ui::BlockedModsDialog), m_mods(mods), m_hash_type(hash_type) { m_hashing_task = shared_qobject_ptr(new ConcurrentTask(this, "MakeHashesTask", 10)); connect(m_hashing_task.get(), &Task::finished, this, &BlockedModsDialog::hashTaskFinished); @@ -254,7 +254,7 @@ void BlockedModsDialog::addHashTask(QString path) /// @param path the path to the local file being hashed void BlockedModsDialog::buildHashTask(QString path) { - auto hash_task = Hashing::createBlockedModHasher(path, ModPlatform::ResourceProvider::FLAME, "sha1"); + auto hash_task = Hashing::createBlockedModHasher(path, ModPlatform::ResourceProvider::FLAME, m_hash_type); qDebug() << "[Blocked Mods Dialog] Creating Hash task for path: " << path; diff --git a/launcher/ui/dialogs/BlockedModsDialog.h b/launcher/ui/dialogs/BlockedModsDialog.h index e3b7c9756..09722bce9 100644 --- a/launcher/ui/dialogs/BlockedModsDialog.h +++ b/launcher/ui/dialogs/BlockedModsDialog.h @@ -54,7 +54,7 @@ class BlockedModsDialog : public QDialog { Q_OBJECT public: - BlockedModsDialog(QWidget* parent, const QString& title, const QString& text, QList& mods); + BlockedModsDialog(QWidget* parent, const QString& title, const QString& text, QList& mods, QString hash_type = "sha1"); ~BlockedModsDialog() override; @@ -73,6 +73,7 @@ class BlockedModsDialog : public QDialog { QSet m_pending_hash_paths; bool m_rehash_pending; QPushButton* m_openMissingButton; + QString m_hash_type; void openAll(bool missingOnly); void addDownloadFolder(); From 8092c321a943ab64faab2c15ee5c44b83b56289d Mon Sep 17 00:00:00 2001 From: Danny Dorazio Date: Sun, 20 Aug 2023 23:00:25 -0400 Subject: [PATCH 149/268] =?UTF-8?q?Replace=20instances=20of=20=E2=80=9CMin?= =?UTF-8?q?ecraft=20account=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- launcher/ui/MainWindow.cpp | 2 +- launcher/ui/pages/instance/VersionPage.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 73b8dbe5e..780147ef8 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -870,7 +870,7 @@ void MainWindow::finalizeInstance(InstancePtr inst) } else { CustomMessageBox::selectable(this, tr("Error"), tr("The launcher cannot download Minecraft or update instances unless you have at least " - "one account added.\nPlease add your Mojang or Minecraft account."), + "one account added.\nPlease add your Microsoft or Mojang account."), QMessageBox::Warning) ->show(); } diff --git a/launcher/ui/pages/instance/VersionPage.cpp b/launcher/ui/pages/instance/VersionPage.cpp index ef029d1d9..fa5064311 100644 --- a/launcher/ui/pages/instance/VersionPage.cpp +++ b/launcher/ui/pages/instance/VersionPage.cpp @@ -408,7 +408,7 @@ void VersionPage::on_actionDownload_All_triggered() if (!APPLICATION->accounts()->anyAccountIsValid()) { CustomMessageBox::selectable(this, tr("Error"), tr("Cannot download Minecraft or update instances unless you have at least " - "one account added.\nPlease add your Mojang or Minecraft account."), + "one account added.\nPlease add your Microsoft or Mojang account."), QMessageBox::Warning) ->show(); return; From 3638fc0a7db049e83e7e4ffab0d2100b9e2e7c58 Mon Sep 17 00:00:00 2001 From: Danny Dorazio Date: Sun, 20 Aug 2023 23:00:50 -0400 Subject: [PATCH 150/268] =?UTF-8?q?Update=20=E2=80=9CWelcome=E2=80=9D=20me?= =?UTF-8?q?ssage=20on=20account=20list=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- launcher/ui/pages/global/AccountListPage.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/launcher/ui/pages/global/AccountListPage.cpp b/launcher/ui/pages/global/AccountListPage.cpp index 5c6fb092b..c0bd1607d 100644 --- a/launcher/ui/pages/global/AccountListPage.cpp +++ b/launcher/ui/pages/global/AccountListPage.cpp @@ -64,7 +64,8 @@ AccountListPage::AccountListPage(QWidget* parent) : QMainWindow(parent), ui(new ui->setupUi(this); ui->listView->setEmptyString( tr("Welcome!\n" - "If you're new here, you can click the \"Add\" button to add your Mojang or Minecraft account.")); + "If you're new here, you can select the \"Add Microsoft\" or \"Add Mojang\" buttons to link your Microsoft and/or Mojang " + "accounts.")); ui->listView->setEmptyMode(VersionListView::String); ui->listView->setContextMenuPolicy(Qt::CustomContextMenu); From 02264f67f35b47a6074ea7cdb925e94ea8ac590d Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 21 Aug 2023 14:15:51 +0300 Subject: [PATCH 153/268] Fixed codeql Signed-off-by: Trial97 --- launcher/modplatform/atlauncher/ATLPackInstallTask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp index a3f26e1f7..8ae8145de 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp @@ -842,7 +842,7 @@ void PackInstallTask::downloadMods() continue; } auto modIter = std::find_if(blocked_mods.begin(), blocked_mods.end(), - [blocked](VersionMod mod) { return mod.url == blocked.websiteUrl; }); + [blocked](const VersionMod& mod) { return mod.url == blocked.websiteUrl; }); if (modIter == blocked_mods.end()) continue; auto mod = *modIter; From 492bf373c65123ad3b3f8b1508cb8fb97628053d Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 21 Aug 2023 15:00:11 +0300 Subject: [PATCH 154/268] updated memory allocation on quick setup Signed-off-by: Trial97 --- launcher/ui/widgets/JavaSettingsWidget.cpp | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp index 42279a663..4129e3af6 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.cpp +++ b/launcher/ui/widgets/JavaSettingsWidget.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -186,12 +187,12 @@ QString JavaSettingsWidget::javaPath() const int JavaSettingsWidget::maxHeapSize() const { - return m_maxMemSpinBox->value(); + return std::max(m_minMemSpinBox->value(), m_maxMemSpinBox->value()); } int JavaSettingsWidget::minHeapSize() const { - return m_minMemSpinBox->value(); + return std::min(m_minMemSpinBox->value(), m_maxMemSpinBox->value()); } bool JavaSettingsWidget::permGenEnabled() const @@ -214,17 +215,9 @@ void JavaSettingsWidget::memoryValueChanged(int) if (obj == m_minMemSpinBox && min != observedMinMemory) { observedMinMemory = min; actuallyChanged = true; - if (min > max) { - observedMaxMemory = min; - m_maxMemSpinBox->setValue(min); - } } else if (obj == m_maxMemSpinBox && max != observedMaxMemory) { observedMaxMemory = max; actuallyChanged = true; - if (min > max) { - observedMinMemory = max; - m_minMemSpinBox->setValue(max); - } } else if (obj == m_permGenSpinBox && permgen != observedPermGenMemory) { observedPermGenMemory = permgen; actuallyChanged = true; From 1eb75b685280c056e5e9a43685e6356c8fab2e2a Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 21 Aug 2023 15:34:45 +0300 Subject: [PATCH 155/268] fixed build Signed-off-by: Trial97 --- launcher/ui/widgets/JavaSettingsWidget.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp index 4129e3af6..ec33d991e 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.cpp +++ b/launcher/ui/widgets/JavaSettingsWidget.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include @@ -187,12 +186,20 @@ QString JavaSettingsWidget::javaPath() const int JavaSettingsWidget::maxHeapSize() const { - return std::max(m_minMemSpinBox->value(), m_maxMemSpinBox->value()); + auto min = m_minMemSpinBox->value(); + auto max = m_maxMemSpinBox->value(); + if (max < min) + max = min; + return max; } int JavaSettingsWidget::minHeapSize() const { - return std::min(m_minMemSpinBox->value(), m_maxMemSpinBox->value()); + auto min = m_minMemSpinBox->value(); + auto max = m_maxMemSpinBox->value(); + if (min > max) + min = max; + return min; } bool JavaSettingsWidget::permGenEnabled() const From d7dadabfbf61a46996d13894ed591d057bb8c1b3 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Mon, 21 Aug 2023 13:55:28 +0100 Subject: [PATCH 156/268] List fixes Double-click to toggle profile components. Restore double-click to toggle resources. Fix clicking on checkbox to select account. Double-click to select account. Signed-off-by: TheKodeToad --- launcher/minecraft/auth/AccountList.cpp | 5 +++-- launcher/ui/pages/global/AccountListPage.cpp | 2 ++ launcher/ui/pages/instance/ExternalResourcesPage.cpp | 1 + launcher/ui/pages/instance/VersionPage.cpp | 7 +++++-- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/launcher/minecraft/auth/AccountList.cpp b/launcher/minecraft/auth/AccountList.cpp index c534ea3bd..84dbd8416 100644 --- a/launcher/minecraft/auth/AccountList.cpp +++ b/launcher/minecraft/auth/AccountList.cpp @@ -415,7 +415,7 @@ Qt::ItemFlags AccountList::flags(const QModelIndex& index) const bool AccountList::setData(const QModelIndex& idx, const QVariant& value, int role) { - if (idx.row() < 0 || idx.row() >= rowCount(idx) || !idx.isValid()) { + if (idx.row() < 0 || idx.row() >= rowCount(idx.parent()) || !idx.isValid()) { return false; } @@ -423,7 +423,8 @@ bool AccountList::setData(const QModelIndex& idx, const QVariant& value, int rol if (value == Qt::Checked) { MinecraftAccountPtr account = at(idx.row()); setDefaultAccount(account); - } + } else if (m_defaultAccount == at(idx.row())) + setDefaultAccount(nullptr); } emit dataChanged(idx, index(idx.row(), columnCount(QModelIndex()) - 1)); diff --git a/launcher/ui/pages/global/AccountListPage.cpp b/launcher/ui/pages/global/AccountListPage.cpp index 5c6fb092b..a92e3dd95 100644 --- a/launcher/ui/pages/global/AccountListPage.cpp +++ b/launcher/ui/pages/global/AccountListPage.cpp @@ -85,6 +85,8 @@ AccountListPage::AccountListPage(QWidget* parent) : QMainWindow(parent), ui(new connect(selectionModel, &QItemSelectionModel::selectionChanged, [this]([[maybe_unused]] const QItemSelection& sel, [[maybe_unused]] const QItemSelection& dsel) { updateButtonStates(); }); connect(ui->listView, &VersionListView::customContextMenuRequested, this, &AccountListPage::ShowContextMenu); + connect(ui->listView, &VersionListView::activated, this, + [this](const QModelIndex& index) { m_accounts->setDefaultAccount(m_accounts->at(index.row())); }); connect(m_accounts.get(), &AccountList::listChanged, this, &AccountListPage::listChanged); connect(m_accounts.get(), &AccountList::listActivityChanged, this, &AccountListPage::listChanged); diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.cpp b/launcher/ui/pages/instance/ExternalResourcesPage.cpp index 719db8799..1a8fafa9b 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.cpp +++ b/launcher/ui/pages/instance/ExternalResourcesPage.cpp @@ -152,6 +152,7 @@ void ExternalResourcesPage::retranslate() void ExternalResourcesPage::itemActivated(const QModelIndex&) { auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()); + m_model->setResourceEnabled(selection.indexes(), EnableAction::TOGGLE); } void ExternalResourcesPage::filterTextChanged(const QString& newContents) diff --git a/launcher/ui/pages/instance/VersionPage.cpp b/launcher/ui/pages/instance/VersionPage.cpp index ef029d1d9..b1060704b 100644 --- a/launcher/ui/pages/instance/VersionPage.cpp +++ b/launcher/ui/pages/instance/VersionPage.cpp @@ -166,14 +166,17 @@ VersionPage::VersionPage(MinecraftInstance* inst, QWidget* parent) : QMainWindow ui->packageView->setSelectionMode(QAbstractItemView::SingleSelection); ui->packageView->setContextMenuPolicy(Qt::CustomContextMenu); - connect(ui->packageView->selectionModel(), &QItemSelectionModel::currentChanged, this, &VersionPage::versionCurrent); auto smodel = ui->packageView->selectionModel(); + connect(smodel, &QItemSelectionModel::currentChanged, this, &VersionPage::versionCurrent); connect(smodel, &QItemSelectionModel::currentChanged, this, &VersionPage::packageCurrent); - connect(m_profile.get(), &PackProfile::minecraftChanged, this, &VersionPage::updateVersionControls); updateVersionControls(); preselect(0); connect(ui->packageView, &ModListView::customContextMenuRequested, this, &VersionPage::showContextMenu); + connect(ui->packageView, &QAbstractItemView::activated, this, [this](const QModelIndex& index) { + auto component = m_profile->getComponent(index.row()); + component->setEnabled(!component->isEnabled()); + }); connect(ui->filterEdit, &QLineEdit::textChanged, this, &VersionPage::onFilterTextChanged); } From 45dad27a6d945e3d6257d76ff0652307ad42788a Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 21 Aug 2023 17:46:50 +0300 Subject: [PATCH 157/268] fixed check Signed-off-by: Trial97 --- launcher/ui/widgets/JavaSettingsWidget.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp index ec33d991e..4ffa3b168 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.cpp +++ b/launcher/ui/widgets/JavaSettingsWidget.cpp @@ -361,8 +361,8 @@ void JavaSettingsWidget::checkJavaPath(const QString& path) setJavaStatus(JavaStatus::Pending); m_checker.reset(new JavaChecker()); m_checker->m_path = path; - m_checker->m_minMem = m_minMemSpinBox->value(); - m_checker->m_maxMem = m_maxMemSpinBox->value(); + m_checker->m_minMem = minHeapSize(); + m_checker->m_maxMem = maxHeapSize(); if (m_permGenSpinBox->isVisible()) { m_checker->m_permGen = m_permGenSpinBox->value(); } @@ -415,6 +415,9 @@ void JavaSettingsWidget::updateThresholds() } else if (observedMaxMemory > (m_availableMemory * 0.9)) { iconName = "status-yellow"; m_labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation approaches your system memory capacity.")); + } else if (observedMaxMemory < observedMinMemory) { + iconName = "status-yellow"; + m_labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation is smaller that the minimum value")); } else { iconName = "status-good"; m_labelMaxMemIcon->setToolTip(""); From 7acfe36a625e2f0c4fb5ba9c11a15be45484a4f6 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 21 Aug 2023 21:30:44 +0300 Subject: [PATCH 158/268] fixed httpmetacache load Signed-off-by: Trial97 --- launcher/net/HttpMetaCache.cpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/launcher/net/HttpMetaCache.cpp b/launcher/net/HttpMetaCache.cpp index 7809d40fc..f37bc0bf8 100644 --- a/launcher/net/HttpMetaCache.cpp +++ b/launcher/net/HttpMetaCache.cpp @@ -218,9 +218,24 @@ void HttpMetaCache::Load() if (!index.open(QIODevice::ReadOnly)) return; - QJsonDocument json = QJsonDocument::fromJson(index.readAll()); + QJsonParseError parseError; + QJsonDocument json = QJsonDocument::fromJson(index.readAll(), &parseError); - auto root = Json::requireObject(json, "HttpMetaCache root"); + // Fail if the JSON is invalid. + if (parseError.error != QJsonParseError::NoError) { + qCritical() << QString("Failed to parse HttpMetaCache file: %1 at offset %2") + .arg(parseError.errorString(), QString::number(parseError.offset)) + .toUtf8(); + return; + } + + // Make sure the root is an object. + if (!json.isObject()) { + qCritical() << "HttpMetaCache root should be an object."; + return; + } + + auto root = json.object(); // check file version first auto version_val = Json::ensureString(root, "version"); From c3d03f0c3392051a845be5afeef246d386e0ca18 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 21 Aug 2023 22:20:33 +0300 Subject: [PATCH 159/268] added check for valid query items in curse url install Signed-off-by: Trial97 --- launcher/ui/MainWindow.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 780147ef8..cbfd4b9a4 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -932,6 +932,11 @@ void MainWindow::processURLs(QList urls) // format of url curseforge://install?addonId=IDHERE&fileId=IDHERE QUrlQuery query(url); + if (query.allQueryItemValues("addonId").isEmpty() || query.allQueryItemValues("fileId").isEmpty()) { + qDebug() << "Invalid curseforge link:" << url; + continue; + } + auto addonId = query.allQueryItemValues("addonId")[0]; auto fileId = query.allQueryItemValues("fileId")[0]; From 09aca7a0b573047df567d349ba9b792d2ffbc16e Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 22 Aug 2023 10:52:35 +0300 Subject: [PATCH 160/268] Added warning to settings page Signed-off-by: Trial97 --- launcher/ui/pages/global/JavaPage.cpp | 4 ++++ launcher/ui/pages/instance/InstanceSettingsPage.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/launcher/ui/pages/global/JavaPage.cpp b/launcher/ui/pages/global/JavaPage.cpp index 45d8f0180..d36fca99d 100644 --- a/launcher/ui/pages/global/JavaPage.cpp +++ b/launcher/ui/pages/global/JavaPage.cpp @@ -185,6 +185,7 @@ void JavaPage::updateThresholds() { auto sysMiB = Sys::getSystemRam() / Sys::mebibyte; unsigned int maxMem = ui->maxMemSpinBox->value(); + unsigned int minMem = ui->minMemSpinBox->value(); QString iconName; @@ -194,6 +195,9 @@ void JavaPage::updateThresholds() } else if (maxMem > (sysMiB * 0.9)) { iconName = "status-yellow"; ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation approaches your system memory capacity.")); + } else if (maxMem < minMem) { + iconName = "status-yellow"; + ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation is smaller that the minimum value")); } else { iconName = "status-good"; ui->labelMaxMemIcon->setToolTip(""); diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index d5dbb80b8..7fa18674f 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -478,6 +478,7 @@ void InstanceSettingsPage::updateThresholds() { auto sysMiB = Sys::getSystemRam() / Sys::mebibyte; unsigned int maxMem = ui->maxMemSpinBox->value(); + unsigned int minMem = ui->minMemSpinBox->value(); QString iconName; @@ -487,6 +488,9 @@ void InstanceSettingsPage::updateThresholds() } else if (maxMem > (sysMiB * 0.9)) { iconName = "status-yellow"; ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation approaches your system memory capacity.")); + } else if (maxMem < minMem) { + iconName = "status-yellow"; + ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation is smaller that the minimum value")); } else { iconName = "status-good"; ui->labelMaxMemIcon->setToolTip(""); From 3574d89e0f6ea9d507e5c4844d07a2aeac9ade97 Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Tue, 22 Aug 2023 16:16:50 +0300 Subject: [PATCH 161/268] Update launcher/ui/pages/global/JavaPage.cpp Co-authored-by: Tayou <31988415+TayouVR@users.noreply.github.com> Signed-off-by: Alexandru Ionut Tripon --- launcher/ui/pages/global/JavaPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/pages/global/JavaPage.cpp b/launcher/ui/pages/global/JavaPage.cpp index d36fca99d..ac50319ec 100644 --- a/launcher/ui/pages/global/JavaPage.cpp +++ b/launcher/ui/pages/global/JavaPage.cpp @@ -197,7 +197,7 @@ void JavaPage::updateThresholds() ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation approaches your system memory capacity.")); } else if (maxMem < minMem) { iconName = "status-yellow"; - ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation is smaller that the minimum value")); + ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation is smaller than the minimum value")); } else { iconName = "status-good"; ui->labelMaxMemIcon->setToolTip(""); From 06dbd381f8e0ed2222193424812d7e83a4db1adf Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Tue, 22 Aug 2023 16:16:57 +0300 Subject: [PATCH 162/268] Update launcher/ui/pages/instance/InstanceSettingsPage.cpp Co-authored-by: Tayou <31988415+TayouVR@users.noreply.github.com> Signed-off-by: Alexandru Ionut Tripon --- launcher/ui/pages/instance/InstanceSettingsPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index 7fa18674f..7aa6bd322 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -490,7 +490,7 @@ void InstanceSettingsPage::updateThresholds() ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation approaches your system memory capacity.")); } else if (maxMem < minMem) { iconName = "status-yellow"; - ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation is smaller that the minimum value")); + ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation is smaller than the minimum value")); } else { iconName = "status-good"; ui->labelMaxMemIcon->setToolTip(""); From eed7e996da260db2c3bc7e062953bee7bb04b8e7 Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Tue, 22 Aug 2023 16:17:05 +0300 Subject: [PATCH 163/268] Update launcher/ui/widgets/JavaSettingsWidget.cpp Co-authored-by: Tayou <31988415+TayouVR@users.noreply.github.com> Signed-off-by: Alexandru Ionut Tripon --- launcher/ui/widgets/JavaSettingsWidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp index 4ffa3b168..d2d921918 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.cpp +++ b/launcher/ui/widgets/JavaSettingsWidget.cpp @@ -417,7 +417,7 @@ void JavaSettingsWidget::updateThresholds() m_labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation approaches your system memory capacity.")); } else if (observedMaxMemory < observedMinMemory) { iconName = "status-yellow"; - m_labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation is smaller that the minimum value")); + m_labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation is smaller than the minimum value")); } else { iconName = "status-good"; m_labelMaxMemIcon->setToolTip(""); From 4704c522e002e0da2c9ce49f71456fce684ab3a1 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 23 Aug 2023 10:26:57 +0300 Subject: [PATCH 164/268] moved modloaderTypes to ModPlatform Signed-off-by: Trial97 --- launcher/minecraft/PackProfile.cpp | 16 +++++----- launcher/minecraft/PackProfile.h | 2 +- .../mod/tasks/GetModDependenciesTask.cpp | 6 ++-- .../mod/tasks/GetModDependenciesTask.h | 2 +- launcher/modplatform/CheckUpdateTask.h | 4 +-- launcher/modplatform/ModIndex.cpp | 21 +++++++++++++ launcher/modplatform/ModIndex.h | 6 +++- launcher/modplatform/ResourceAPI.h | 30 ++----------------- launcher/modplatform/flame/FlameAPI.h | 23 +++++++------- launcher/modplatform/flame/FlameCheckUpdate.h | 2 +- .../modplatform/import_ftb/PackHelpers.cpp | 8 ++--- launcher/modplatform/import_ftb/PackHelpers.h | 2 +- .../import_ftb/PackInstallTask.cpp | 12 ++++---- launcher/modplatform/modrinth/ModrinthAPI.cpp | 4 +-- launcher/modplatform/modrinth/ModrinthAPI.h | 24 ++++++++------- .../modrinth/ModrinthCheckUpdate.cpp | 6 ++-- .../modrinth/ModrinthCheckUpdate.h | 2 +- launcher/ui/dialogs/ModUpdateDialog.cpp | 2 +- launcher/ui/pages/modplatform/ModPage.h | 2 +- .../modplatform/flame/FlameResourcePages.cpp | 2 +- .../modplatform/flame/FlameResourcePages.h | 2 +- .../modrinth/ModrinthResourcePages.cpp | 2 +- .../modrinth/ModrinthResourcePages.h | 2 +- 23 files changed, 94 insertions(+), 88 deletions(-) diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp index 92988808a..dd7364851 100644 --- a/launcher/minecraft/PackProfile.cpp +++ b/launcher/minecraft/PackProfile.cpp @@ -62,11 +62,11 @@ #include "Application.h" #include "modplatform/ResourceAPI.h" -static const QMap modloaderMapping{ { "net.neoforged", ResourceAPI::NeoForge }, - { "net.minecraftforge", ResourceAPI::Forge }, - { "net.fabricmc.fabric-loader", ResourceAPI::Fabric }, - { "org.quiltmc.quilt-loader", ResourceAPI::Quilt }, - { "com.mumfrey.liteloader", ResourceAPI::LiteLoader } }; +static const QMap modloaderMapping{ { "net.neoforged", ModPlatform::NeoForge }, + { "net.minecraftforge", ModPlatform::Forge }, + { "net.fabricmc.fabric-loader", ModPlatform::Fabric }, + { "org.quiltmc.quilt-loader", ModPlatform::Quilt }, + { "com.mumfrey.liteloader", ModPlatform::LiteLoader } }; PackProfile::PackProfile(MinecraftInstance* instance) : QAbstractListModel() { @@ -990,12 +990,12 @@ void PackProfile::disableInteraction(bool disable) } } -std::optional PackProfile::getModLoaders() +std::optional PackProfile::getModLoaders() { - ResourceAPI::ModLoaderTypes result; + ModPlatform::ModLoaderTypes result; bool has_any_loader = false; - QMapIterator i(modloaderMapping); + QMapIterator i(modloaderMapping); while (i.hasNext()) { i.next(); diff --git a/launcher/minecraft/PackProfile.h b/launcher/minecraft/PackProfile.h index ce44fa588..4b93d654c 100644 --- a/launcher/minecraft/PackProfile.h +++ b/launcher/minecraft/PackProfile.h @@ -146,7 +146,7 @@ class PackProfile : public QAbstractListModel { // todo(merged): is this the best approach void appendComponent(ComponentPtr component); - std::optional getModLoaders(); + std::optional getModLoaders(); private: void scheduleSave(); diff --git a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp index 0a0f57bf3..46b489ce0 100644 --- a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp +++ b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp @@ -39,7 +39,7 @@ static Version mcVersion(BaseInstance* inst) return static_cast(inst)->getPackProfile()->getComponent("net.minecraft")->getVersion(); } -static ResourceAPI::ModLoaderTypes mcLoaders(BaseInstance* inst) +static ModPlatform::ModLoaderTypes mcLoaders(BaseInstance* inst) { return static_cast(inst)->getPackProfile()->getModLoaders().value(); } @@ -75,7 +75,7 @@ void GetModDependenciesTask::prepare() ModPlatform::Dependency GetModDependenciesTask::getOverride(const ModPlatform::Dependency& dep, const ModPlatform::ResourceProvider providerName) { - if (auto isQuilt = m_loaderType & ResourceAPI::Quilt; isQuilt || m_loaderType & ResourceAPI::Fabric) { + if (auto isQuilt = m_loaderType & ModPlatform::Quilt; isQuilt || m_loaderType & ModPlatform::Fabric) { auto overide = ModPlatform::getOverrideDeps(); auto over = std::find_if(overide.cbegin(), overide.cend(), [dep, providerName, isQuilt](auto o) { return o.provider == providerName && dep.addonId == (isQuilt ? o.fabric : o.quilt); @@ -191,7 +191,7 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen } pDep->version = provider.mod->loadDependencyVersions(dep, arr); if (!pDep->version.addonId.isValid()) { - if (m_loaderType & ResourceAPI::Quilt) { // falback for quilt + if (m_loaderType & ModPlatform::Quilt) { // falback for quilt auto overide = ModPlatform::getOverrideDeps(); auto over = std::find_if(overide.cbegin(), overide.cend(), [dep, provider](auto o) { return o.provider == provider.name && dep.addonId == o.quilt; }); diff --git a/launcher/minecraft/mod/tasks/GetModDependenciesTask.h b/launcher/minecraft/mod/tasks/GetModDependenciesTask.h index 50eba6afc..a8b9953d3 100644 --- a/launcher/minecraft/mod/tasks/GetModDependenciesTask.h +++ b/launcher/minecraft/mod/tasks/GetModDependenciesTask.h @@ -80,5 +80,5 @@ class GetModDependenciesTask : public SequentialTask { Provider m_modrinth_provider; Version m_version; - ResourceAPI::ModLoaderTypes m_loaderType; + ModPlatform::ModLoaderTypes m_loaderType; }; diff --git a/launcher/modplatform/CheckUpdateTask.h b/launcher/modplatform/CheckUpdateTask.h index 6d968ea48..d125a5879 100644 --- a/launcher/modplatform/CheckUpdateTask.h +++ b/launcher/modplatform/CheckUpdateTask.h @@ -14,7 +14,7 @@ class CheckUpdateTask : public Task { public: CheckUpdateTask(QList& mods, std::list& mcVersions, - std::optional loaders, + std::optional loaders, std::shared_ptr mods_folder) : Task(nullptr), m_mods(mods), m_game_versions(mcVersions), m_loaders(loaders), m_mods_folder(mods_folder){}; @@ -53,7 +53,7 @@ class CheckUpdateTask : public Task { protected: QList& m_mods; std::list& m_game_versions; - std::optional m_loaders; + std::optional m_loaders; std::shared_ptr m_mods_folder; std::vector m_updatable; diff --git a/launcher/modplatform/ModIndex.cpp b/launcher/modplatform/ModIndex.cpp index 350a9f10b..e8e4a38dd 100644 --- a/launcher/modplatform/ModIndex.cpp +++ b/launcher/modplatform/ModIndex.cpp @@ -83,4 +83,25 @@ QString getMetaURL(ResourceProvider provider, QVariant projectID) projectID.toString(); } +auto getModLoaderString(ModLoaderType type) -> const QString +{ + switch (type) { + case NeoForge: + return "neoforge"; + case Forge: + return "forge"; + case Cauldron: + return "cauldron"; + case LiteLoader: + return "liteloader"; + case Fabric: + return "fabric"; + case Quilt: + return "quilt"; + default: + break; + } + return ""; +} + } // namespace ModPlatform diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index cad217034..2178422f0 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -30,6 +30,9 @@ class QIODevice; namespace ModPlatform { +enum ModLoaderType { NeoForge = 1 << 0, Forge = 1 << 1, Cauldron = 1 << 2, LiteLoader = 1 << 3, Fabric = 1 << 4, Quilt = 1 << 5 }; +Q_DECLARE_FLAGS(ModLoaderTypes, ModLoaderType) + enum class ResourceProvider { MODRINTH, FLAME }; enum class ResourceType { MOD, RESOURCE_PACK, SHADER_PACK }; @@ -128,7 +131,6 @@ struct IndexedPack { return std::any_of(versions.constBegin(), versions.constEnd(), [](auto const& v) { return v.is_currently_selected; }); } }; -QString getMetaURL(ResourceProvider provider, QVariant projectID); struct OverrideDep { QString quilt; @@ -148,6 +150,8 @@ inline auto getOverrideDeps() -> QList QString getMetaURL(ResourceProvider provider, QVariant projectID); +auto getModLoaderString(ModLoaderType type) -> const QString; + } // namespace ModPlatform Q_DECLARE_METATYPE(ModPlatform::IndexedPack) diff --git a/launcher/modplatform/ResourceAPI.h b/launcher/modplatform/ResourceAPI.h index f6ccb426d..7965d0f53 100644 --- a/launcher/modplatform/ResourceAPI.h +++ b/launcher/modplatform/ResourceAPI.h @@ -54,9 +54,6 @@ class ResourceAPI { public: virtual ~ResourceAPI() = default; - enum ModLoaderType { NeoForge = 1 << 0, Forge = 1 << 1, Cauldron = 1 << 2, LiteLoader = 1 << 3, Fabric = 1 << 4, Quilt = 1 << 5 }; - Q_DECLARE_FLAGS(ModLoaderTypes, ModLoaderType) - struct SortingMethod { // The index of the sorting method. Used to allow for arbitrary ordering in the list of methods. // Used by Flame in the API request. @@ -74,7 +71,7 @@ class ResourceAPI { std::optional search; std::optional sorting; - std::optional loaders; + std::optional loaders; std::optional > versions; }; struct SearchCallbacks { @@ -87,7 +84,7 @@ class ResourceAPI { ModPlatform::IndexedPack pack; std::optional > mcVersions; - std::optional loaders; + std::optional loaders; VersionSearchArgs(VersionSearchArgs const&) = default; void operator=(VersionSearchArgs other) @@ -114,7 +111,7 @@ class ResourceAPI { struct DependencySearchArgs { ModPlatform::Dependency dependency; Version mcVersion; - ModLoaderTypes loader; + ModPlatform::ModLoaderTypes loader; }; struct DependencySearchCallbacks { @@ -161,27 +158,6 @@ class ResourceAPI { return nullptr; } - static auto getModLoaderString(ModLoaderType type) -> const QString - { - switch (type) { - case NeoForge: - return "neoforge"; - case Forge: - return "forge"; - case Cauldron: - return "cauldron"; - case LiteLoader: - return "liteloader"; - case Fabric: - return "fabric"; - case Quilt: - return "quilt"; - default: - break; - } - return ""; - } - protected: [[nodiscard]] inline QString debugName() const { return "External resource API"; } diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h index e423189a8..a36b99a47 100644 --- a/launcher/modplatform/flame/FlameAPI.h +++ b/launcher/modplatform/flame/FlameAPI.h @@ -24,7 +24,10 @@ class FlameAPI : public NetworkResourceAPI { [[nodiscard]] auto getSortingMethods() const -> QList override; - static inline auto validateModLoaders(ModLoaderTypes loaders) -> bool { return loaders & (NeoForge | Forge | Fabric | Quilt); } + static inline auto validateModLoaders(ModPlatform::ModLoaderTypes loaders) -> bool + { + return loaders & (ModPlatform::NeoForge | ModPlatform::Forge | ModPlatform::Fabric | ModPlatform::Quilt); + } private: static int getClassId(ModPlatform::ResourceType type) @@ -38,19 +41,19 @@ class FlameAPI : public NetworkResourceAPI { } } - static int getMappedModLoader(ModLoaderTypes loaders) + static int getMappedModLoader(ModPlatform::ModLoaderTypes loaders) { // https://docs.curseforge.com/?http#tocS_ModLoaderType - if (loaders & Forge) + if (loaders & ModPlatform::Forge) return 1; - if (loaders & Fabric) + if (loaders & ModPlatform::Fabric) return 4; // TODO: remove this once Quilt drops official Fabric support - if (loaders & Quilt) // NOTE: Most if not all Fabric mods should work *currently* - return 4; // FIXME: implement multiple loaders filter (this should be 5) + if (loaders & ModPlatform::Quilt) // NOTE: Most if not all Fabric mods should work *currently* + return 4; // FIXME: implement multiple loaders filter (this should be 5) // TODO: remove this once NeoForge drops official Forge support - if (loaders & NeoForge) // NOTE: Most if not all Forge mods should work *currently* - return 1; // FIXME: implement multiple loaders filter (this should be 6) + if (loaders & ModPlatform::NeoForge) // NOTE: Most if not all Forge mods should work *currently* + return 1; // FIXME: implement multiple loaders filter (this should be 6) return 0; } @@ -93,7 +96,7 @@ class FlameAPI : public NetworkResourceAPI { if (args.loaders.has_value()) { int mappedModLoader = getMappedModLoader(args.loaders.value()); - if (args.loaders.value() & Quilt) { + if (args.loaders.value() & ModPlatform::Quilt) { auto overide = ModPlatform::getOverrideDeps(); auto over = std::find_if(overide.cbegin(), overide.cend(), [addonId](auto dep) { return dep.provider == ModPlatform::ResourceProvider::FLAME && addonId == dep.quilt; @@ -113,7 +116,7 @@ class FlameAPI : public NetworkResourceAPI { { auto mappedModLoader = getMappedModLoader(args.loader); auto addonId = args.dependency.addonId.toString(); - if (args.loader & Quilt) { + if (args.loader & ModPlatform::Quilt) { auto overide = ModPlatform::getOverrideDeps(); auto over = std::find_if(overide.cbegin(), overide.cend(), [addonId](auto dep) { return dep.provider == ModPlatform::ResourceProvider::FLAME && addonId == dep.quilt; diff --git a/launcher/modplatform/flame/FlameCheckUpdate.h b/launcher/modplatform/flame/FlameCheckUpdate.h index e3465d7e2..05c619a70 100644 --- a/launcher/modplatform/flame/FlameCheckUpdate.h +++ b/launcher/modplatform/flame/FlameCheckUpdate.h @@ -10,7 +10,7 @@ class FlameCheckUpdate : public CheckUpdateTask { public: FlameCheckUpdate(QList& mods, std::list& mcVersions, - std::optional loaders, + std::optional loaders, std::shared_ptr mods_folder) : CheckUpdateTask(mods, mcVersions, loaders, mods_folder) {} diff --git a/launcher/modplatform/import_ftb/PackHelpers.cpp b/launcher/modplatform/import_ftb/PackHelpers.cpp index 118bdd157..ecf973452 100644 --- a/launcher/modplatform/import_ftb/PackHelpers.cpp +++ b/launcher/modplatform/import_ftb/PackHelpers.cpp @@ -60,19 +60,19 @@ Modpack parseDirectory(QString path) auto name = Json::requireString(obj, "name", "name"); auto version = Json::requireString(obj, "version", "version"); if (name == "neoforge") { - modpack.loaderType = ResourceAPI::NeoForge; + modpack.loaderType = ModPlatform::NeoForge; modpack.version = version; break; } else if (name == "forge") { - modpack.loaderType = ResourceAPI::Forge; + modpack.loaderType = ModPlatform::Forge; modpack.version = version; break; } else if (name == "fabric") { - modpack.loaderType = ResourceAPI::Fabric; + modpack.loaderType = ModPlatform::Fabric; modpack.version = version; break; } else if (name == "quilt") { - modpack.loaderType = ResourceAPI::Quilt; + modpack.loaderType = ModPlatform::Quilt; modpack.version = version; break; } diff --git a/launcher/modplatform/import_ftb/PackHelpers.h b/launcher/modplatform/import_ftb/PackHelpers.h index 8ea4f3faf..5400252b6 100644 --- a/launcher/modplatform/import_ftb/PackHelpers.h +++ b/launcher/modplatform/import_ftb/PackHelpers.h @@ -39,7 +39,7 @@ struct Modpack { // not needed for instance creation QVariant jvmArgs; - std::optional loaderType; + std::optional loaderType; QString loaderVersion; QIcon icon; diff --git a/launcher/modplatform/import_ftb/PackInstallTask.cpp b/launcher/modplatform/import_ftb/PackInstallTask.cpp index 9e4decb0c..9a3b2595b 100644 --- a/launcher/modplatform/import_ftb/PackInstallTask.cpp +++ b/launcher/modplatform/import_ftb/PackInstallTask.cpp @@ -68,25 +68,25 @@ void PackInstallTask::copySettings() auto modloader = m_pack.loaderType; if (modloader.has_value()) switch (modloader.value()) { - case ResourceAPI::NeoForge: { + case ModPlatform::NeoForge: { components->setComponentVersion("net.neoforged", m_pack.version, true); break; } - case ResourceAPI::Forge: { + case ModPlatform::Forge: { components->setComponentVersion("net.minecraftforge", m_pack.version, true); break; } - case ResourceAPI::Fabric: { + case ModPlatform::Fabric: { components->setComponentVersion("net.fabricmc.fabric-loader", m_pack.version, true); break; } - case ResourceAPI::Quilt: { + case ModPlatform::Quilt: { components->setComponentVersion("org.quiltmc.quilt-loader", m_pack.version, true); break; } - case ResourceAPI::Cauldron: + case ModPlatform::Cauldron: break; - case ResourceAPI::LiteLoader: + case ModPlatform::LiteLoader: break; } components->saveNow(); diff --git a/launcher/modplatform/modrinth/ModrinthAPI.cpp b/launcher/modplatform/modrinth/ModrinthAPI.cpp index 466c5b102..f453f5cb9 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.cpp +++ b/launcher/modplatform/modrinth/ModrinthAPI.cpp @@ -41,7 +41,7 @@ Task::Ptr ModrinthAPI::currentVersions(const QStringList& hashes, QString hash_f Task::Ptr ModrinthAPI::latestVersion(QString hash, QString hash_format, std::optional> mcVersions, - std::optional loaders, + std::optional loaders, std::shared_ptr response) { auto netJob = makeShared(QString("Modrinth::GetLatestVersion"), APPLICATION->network()); @@ -71,7 +71,7 @@ Task::Ptr ModrinthAPI::latestVersion(QString hash, Task::Ptr ModrinthAPI::latestVersions(const QStringList& hashes, QString hash_format, std::optional> mcVersions, - std::optional loaders, + std::optional loaders, std::shared_ptr response) { auto netJob = makeShared(QString("Modrinth::GetLatestVersions"), APPLICATION->network()); diff --git a/launcher/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h index fb42c532c..6ff8f7bcd 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.h +++ b/launcher/modplatform/modrinth/ModrinthAPI.h @@ -19,13 +19,13 @@ class ModrinthAPI : public NetworkResourceAPI { auto latestVersion(QString hash, QString hash_format, std::optional> mcVersions, - std::optional loaders, + std::optional loaders, std::shared_ptr response) -> Task::Ptr; auto latestVersions(const QStringList& hashes, QString hash_format, std::optional> mcVersions, - std::optional loaders, + std::optional loaders, std::shared_ptr response) -> Task::Ptr; Task::Ptr getProjects(QStringList addonIds, std::shared_ptr response) const override; @@ -35,22 +35,24 @@ class ModrinthAPI : public NetworkResourceAPI { inline auto getAuthorURL(const QString& name) const -> QString { return "https://modrinth.com/user/" + name; }; - static auto getModLoaderStrings(const ModLoaderTypes types) -> const QStringList + static auto getModLoaderStrings(const ModPlatform::ModLoaderTypes types) -> const QStringList { QStringList l; - for (auto loader : { NeoForge, Forge, Fabric, Quilt, LiteLoader }) { + for (auto loader : + { ModPlatform::NeoForge, ModPlatform::Forge, ModPlatform::Fabric, ModPlatform::Quilt, ModPlatform::LiteLoader }) { if (types & loader) { l << getModLoaderString(loader); } } - if ((types & NeoForge) && (~types & Forge)) // Add Forge if NeoForge is in use, if Forge isn't already there - l << getModLoaderString(Forge); - if ((types & Quilt) && (~types & Fabric)) // Add Fabric if Quilt is in use, if Fabric isn't already there - l << getModLoaderString(Fabric); + if ((types & ModPlatform::NeoForge) && + (~types & ModPlatform::Forge)) // Add Forge if NeoForge is in use, if Forge isn't already there + l << getModLoaderString(ModPlatform::Forge); + if ((types & ModPlatform::Quilt) && (~types & ModPlatform::Fabric)) // Add Fabric if Quilt is in use, if Fabric isn't already there + l << getModLoaderString(ModPlatform::Fabric); return l; } - static auto getModLoaderFilters(ModLoaderTypes types) -> const QString + static auto getModLoaderFilters(ModPlatform::ModLoaderTypes types) -> const QString { QStringList l; for (auto loader : getModLoaderStrings(types)) { @@ -143,9 +145,9 @@ class ModrinthAPI : public NetworkResourceAPI { return s.isEmpty() ? QString() : s; } - static inline auto validateModLoaders(ModLoaderTypes loaders) -> bool + static inline auto validateModLoaders(ModPlatform::ModLoaderTypes loaders) -> bool { - return loaders & (NeoForge | Forge | Fabric | Quilt | LiteLoader); + return loaders & (ModPlatform::NeoForge | ModPlatform::Forge | ModPlatform::Fabric | ModPlatform::Quilt | ModPlatform::LiteLoader); } [[nodiscard]] std::optional getDependencyURL(DependencySearchArgs const& args) const override diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp index bff8fa2fe..ba3aad844 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp @@ -111,11 +111,11 @@ void ModrinthCheckUpdate::executeTask() // so we may want to filter it QString loader_filter; if (m_loaders.has_value()) { - static auto flags = { ResourceAPI::ModLoaderType::NeoForge, ResourceAPI::ModLoaderType::Forge, - ResourceAPI::ModLoaderType::Fabric, ResourceAPI::ModLoaderType::Quilt }; + static auto flags = { ModPlatform::ModLoaderType::NeoForge, ModPlatform::ModLoaderType::Forge, + ModPlatform::ModLoaderType::Fabric, ModPlatform::ModLoaderType::Quilt }; for (auto flag : flags) { if (m_loaders.value().testFlag(flag)) { - loader_filter = api.getModLoaderString(flag); + loader_filter = ModPlatform::getModLoaderString(flag); break; } } diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.h b/launcher/modplatform/modrinth/ModrinthCheckUpdate.h index 4583dd6ce..f2f2c7e92 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.h +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.h @@ -10,7 +10,7 @@ class ModrinthCheckUpdate : public CheckUpdateTask { public: ModrinthCheckUpdate(QList& mods, std::list& mcVersions, - std::optional loaders, + std::optional loaders, std::shared_ptr mods_folder) : CheckUpdateTask(mods, mcVersions, loaders, mods_folder) {} diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index 0af1ec59b..3e18b224e 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -30,7 +30,7 @@ static std::list mcVersions(BaseInstance* inst) return { static_cast(inst)->getPackProfile()->getComponent("net.minecraft")->getVersion() }; } -static std::optional mcLoaders(BaseInstance* inst) +static std::optional mcLoaders(BaseInstance* inst) { return { static_cast(inst)->getPackProfile()->getModLoaders() }; } diff --git a/launcher/ui/pages/modplatform/ModPage.h b/launcher/ui/pages/modplatform/ModPage.h index 5510c1911..5a43e49a6 100644 --- a/launcher/ui/pages/modplatform/ModPage.h +++ b/launcher/ui/pages/modplatform/ModPage.h @@ -55,7 +55,7 @@ class ModPage : public ResourcePage { virtual auto validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, - std::optional loaders = {}) const -> bool = 0; + std::optional loaders = {}) const -> bool = 0; [[nodiscard]] bool supportsFiltering() const override { return true; }; auto getFilter() const -> const std::shared_ptr { return m_filter; } diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp index dc17e705a..5650fb95d 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp @@ -68,7 +68,7 @@ FlameModPage::FlameModPage(ModDownloadDialog* dialog, BaseInstance& instance) : auto FlameModPage::validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, - std::optional loaders) const -> bool + std::optional loaders) const -> bool { Q_UNUSED(loaders); return ver.mcVersion.contains(mineVer) && !ver.downloadUrl.isEmpty(); diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.h b/launcher/ui/pages/modplatform/flame/FlameResourcePages.h index c6ebc1eac..035da2d5f 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourcePages.h +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.h @@ -95,7 +95,7 @@ class FlameModPage : public ModPage { bool validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, - std::optional loaders = {}) const override; + std::optional loaders = {}) const override; bool optedOut(ModPlatform::IndexedVersion& ver) const override; void openUrl(const QUrl& url) override; diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp index 616c1a815..e944a2a98 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp @@ -65,7 +65,7 @@ ModrinthModPage::ModrinthModPage(ModDownloadDialog* dialog, BaseInstance& instan auto ModrinthModPage::validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, - std::optional loaders) const -> bool + std::optional loaders) const -> bool { auto loaderCompatible = !loaders.has_value(); diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h index 86ba1ccb2..311bcfe32 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h @@ -93,7 +93,7 @@ class ModrinthModPage : public ModPage { [[nodiscard]] inline auto helpPage() const -> QString override { return "Mod-platform"; } - auto validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, std::optional loaders = {}) const + auto validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, std::optional loaders = {}) const -> bool override; }; From f8f9ffa1182c76cd22c2c181820bdd9516d7958f Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 23 Aug 2023 11:33:45 +0300 Subject: [PATCH 165/268] added loaders for flame version Signed-off-by: Trial97 --- launcher/modplatform/ModIndex.h | 2 +- launcher/modplatform/flame/FileResolvingTask.cpp | 3 ++- launcher/modplatform/flame/FlameModIndex.cpp | 13 +++++++++++++ .../modplatform/modrinth/ModrinthPackIndex.cpp | 13 ++++++++++++- launcher/ui/pages/modplatform/ModPage.cpp | 1 - launcher/ui/pages/modplatform/ShaderPackPage.cpp | 3 ++- .../pages/modplatform/flame/FlameResourcePages.cpp | 3 +-- .../modplatform/modrinth/ModrinthResourcePages.cpp | 14 +------------- 8 files changed, 32 insertions(+), 20 deletions(-) diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index 2178422f0..780f68b4d 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -73,7 +73,7 @@ struct IndexedVersion { QString downloadUrl; QString date; QString fileName; - QStringList loaders = {}; + ModLoaderTypes loaders = {}; QString hash_type; QString hash; bool is_preferred = true; diff --git a/launcher/modplatform/flame/FileResolvingTask.cpp b/launcher/modplatform/flame/FileResolvingTask.cpp index 860d7340f..07a3ae632 100644 --- a/launcher/modplatform/flame/FileResolvingTask.cpp +++ b/launcher/modplatform/flame/FileResolvingTask.cpp @@ -1,5 +1,6 @@ #include "FileResolvingTask.h" +#include #include "Json.h" #include "net/ApiDownload.h" #include "net/ApiUpload.h" @@ -153,7 +154,7 @@ void Flame::FileResolvingTask::modrinthCheckFinished() // If there's more than one mod loader for this version, we can't know for sure // which file is relative to each loader, so it's best to not use any one and // let the user download it manually. - if (file.loaders.size() <= 1) { + if (std::bitset<8>(file.loaders.toInt()).count() <= 1) { out->url = file.downloadUrl; qDebug() << "Found alternative on modrinth " << out->fileName; } else { diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index 19803cf6d..8924913bc 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -115,6 +115,19 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) -> if (str.contains('.')) file.mcVersion.append(str); + auto loader = str.toLower(); + if (loader == "neoforge") + file.loaders |= ModPlatform::NeoForge; + if (loader == "forge") + file.loaders |= ModPlatform::Forge; + if (loader == "cauldron") + file.loaders |= ModPlatform::Cauldron; + if (loader == "liteloader") + file.loaders |= ModPlatform::LiteLoader; + if (loader == "fabric") + file.loaders |= ModPlatform::Fabric; + if (loader == "quilt") + file.loaders |= ModPlatform::Quilt; } file.addonId = Json::requireInteger(obj, "modId"); diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index 85e66a91e..1ff5a4b9e 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -134,7 +134,18 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t } auto loaders = Json::requireArray(obj, "loaders"); for (auto loader : loaders) { - file.loaders.append(loader.toString()); + if (loader == "neoforge") + file.loaders |= ModPlatform::NeoForge; + if (loader == "forge") + file.loaders |= ModPlatform::Forge; + if (loader == "cauldron") + file.loaders |= ModPlatform::Cauldron; + if (loader == "liteloader") + file.loaders |= ModPlatform::LiteLoader; + if (loader == "fabric") + file.loaders |= ModPlatform::Fabric; + if (loader == "quilt") + file.loaders |= ModPlatform::Quilt; } file.version = Json::requireString(obj, "name"); file.version_number = Json::requireString(obj, "version_number"); diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index 60a43128a..c9270d399 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -124,7 +124,6 @@ void ModPage::updateVersionList() auto version = current_pack->versions[i]; bool valid = false; for (auto& mcVer : m_filter->versions) { - // NOTE: Flame doesn't care about loader, so passing it changes nothing. if (validateVersion(version, mcVer.toString(), packProfile->getModLoaders())) { valid = true; break; diff --git a/launcher/ui/pages/modplatform/ShaderPackPage.cpp b/launcher/ui/pages/modplatform/ShaderPackPage.cpp index fbf94e844..586dffc55 100644 --- a/launcher/ui/pages/modplatform/ShaderPackPage.cpp +++ b/launcher/ui/pages/modplatform/ShaderPackPage.cpp @@ -3,6 +3,7 @@ // SPDX-License-Identifier: GPL-3.0-only #include "ShaderPackPage.h" +#include "modplatform/ModIndex.h" #include "ui_ResourcePage.h" #include "ShaderPackModel.h" @@ -48,7 +49,7 @@ void ShaderPackResourcePage::addResourceToPage(ModPlatform::IndexedPack::Ptr pac const std::shared_ptr base_model) { QString custom_target_folder; - if (version.loaders.contains(QStringLiteral("canvas"))) + if (version.loaders & ModPlatform::Cauldron) custom_target_folder = QStringLiteral("resourcepacks"); m_model->addPack(pack, version, base_model, false, custom_target_folder); } diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp index 5650fb95d..222ceedc6 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp @@ -70,8 +70,7 @@ auto FlameModPage::validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, std::optional loaders) const -> bool { - Q_UNUSED(loaders); - return ver.mcVersion.contains(mineVer) && !ver.downloadUrl.isEmpty(); + return ver.mcVersion.contains(mineVer) && !ver.downloadUrl.isEmpty() && (!loaders.has_value() || loaders.value() & ver.loaders); } bool FlameModPage::optedOut(ModPlatform::IndexedVersion& ver) const diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp index e944a2a98..bcd0a4cf4 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp @@ -67,19 +67,7 @@ auto ModrinthModPage::validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, std::optional loaders) const -> bool { - auto loaderCompatible = !loaders.has_value(); - - if (!loaderCompatible) { - auto loaderStrings = ModrinthAPI::getModLoaderStrings(loaders.value()); - for (auto remoteLoader : ver.loaders) { - if (loaderStrings.contains(remoteLoader)) { - loaderCompatible = true; - break; - } - } - } - - return ver.mcVersion.contains(mineVer) && loaderCompatible; + return ver.mcVersion.contains(mineVer) && (!loaders.has_value() || loaders.value() & ver.loaders); } ModrinthResourcePackPage::ModrinthResourcePackPage(ResourcePackDownloadDialog* dialog, BaseInstance& instance) From b93cd88292c152175abcd79a6de1d276301de003 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Wed, 23 Aug 2023 11:52:31 +0200 Subject: [PATCH 166/268] fix(nix): add canonicalize-jars-hook See https://github.com/NixOS/nixpkgs/pull/250757 Signed-off-by: Sefa Eyeoglu --- nix/package.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nix/package.nix b/nix/package.nix index 1dbadd405..074214c4b 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -1,6 +1,7 @@ { lib, stdenv, + canonicalize-jars-hook, cmake, cmark, Cocoa, @@ -26,7 +27,7 @@ assert lib.assertMsg (stdenv.isLinux || !gamemodeSupport) "gamemodeSupport is on src = lib.cleanSource self; - nativeBuildInputs = [extra-cmake-modules cmake jdk17 ninja]; + nativeBuildInputs = [extra-cmake-modules cmake jdk17 ninja canonicalize-jars-hook]; buildInputs = [ qtbase From e6ba2f4970abe69f2203bb5b85503e4cf3177016 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 23 Aug 2023 12:52:51 +0300 Subject: [PATCH 167/268] Added loaders check on versions load Signed-off-by: Trial97 --- launcher/minecraft/ComponentUpdateTask.cpp | 4 +- launcher/minecraft/PackProfile.cpp | 20 +++++++-- launcher/minecraft/PackProfile.h | 8 ++-- .../mod/tasks/GetModDependenciesTask.cpp | 3 +- launcher/modplatform/flame/FlameAPI.cpp | 11 +---- launcher/modplatform/flame/FlameAPI.h | 45 +++++++++---------- .../modplatform/flame/FlameCheckUpdate.cpp | 2 - launcher/modplatform/flame/FlameModIndex.cpp | 12 +++-- launcher/modplatform/flame/FlameModIndex.h | 2 +- launcher/modplatform/modrinth/ModrinthAPI.h | 5 --- .../modrinth/ModrinthCheckUpdate.cpp | 1 - .../modrinth/ModrinthPackIndex.cpp | 23 ++++++---- .../modplatform/modrinth/ModrinthPackIndex.h | 7 +-- launcher/ui/MainWindow.cpp | 11 +++-- launcher/ui/dialogs/ModUpdateDialog.cpp | 4 +- .../ui/dialogs/ResourceDownloadDialog.cpp | 2 +- launcher/ui/pages/modplatform/ModModel.cpp | 4 +- launcher/ui/pages/modplatform/ModPage.cpp | 2 +- .../modplatform/flame/FlameResourceModels.cpp | 2 +- .../modrinth/ModrinthResourceModels.cpp | 10 ++--- 20 files changed, 89 insertions(+), 89 deletions(-) diff --git a/launcher/minecraft/ComponentUpdateTask.cpp b/launcher/minecraft/ComponentUpdateTask.cpp index 0b85b81aa..bb838043a 100644 --- a/launcher/minecraft/ComponentUpdateTask.cpp +++ b/launcher/minecraft/ComponentUpdateTask.cpp @@ -2,14 +2,14 @@ #include "Component.h" #include "ComponentUpdateTask_p.h" -#include "OneSixVersionFormat.h" #include "PackProfile.h" #include "PackProfile_p.h" #include "Version.h" #include "cassert" #include "meta/Index.h" #include "meta/Version.h" -#include "meta/VersionList.h" +#include "minecraft/OneSixVersionFormat.h" +#include "minecraft/ProfileUtils.h" #include "net/Mode.h" #include "Application.h" diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp index dd7364851..9e42c5dd6 100644 --- a/launcher/minecraft/PackProfile.cpp +++ b/launcher/minecraft/PackProfile.cpp @@ -58,9 +58,8 @@ #include "ComponentUpdateTask.h" #include "PackProfile.h" #include "PackProfile_p.h" - -#include "Application.h" -#include "modplatform/ResourceAPI.h" +#include "minecraft/mod/Mod.h" +#include "modplatform/ModIndex.h" static const QMap modloaderMapping{ { "net.neoforged", ModPlatform::NeoForge }, { "net.minecraftforge", ModPlatform::Forge }, @@ -1009,3 +1008,18 @@ std::optional PackProfile::getModLoaders() return {}; return result; } + +std::optional PackProfile::getSupportedModLoaders() +{ + auto loadersOpt = getModLoaders(); + if (!loadersOpt.has_value()) + return loadersOpt; + auto loaders = loadersOpt.value(); + // TODO: remove this or add version condition once Quilt drops official Fabric support + if (loaders & ModPlatform::Quilt) + loaders |= ModPlatform::Fabric; + // TODO: remove this or add version condition once NeoForge drops official Forge support + if (loaders & ModPlatform::NeoForge) + loaders |= ModPlatform::Forge; + return loaders; +} diff --git a/launcher/minecraft/PackProfile.h b/launcher/minecraft/PackProfile.h index 4b93d654c..c41601fb2 100644 --- a/launcher/minecraft/PackProfile.h +++ b/launcher/minecraft/PackProfile.h @@ -45,13 +45,9 @@ #include #include -#include "BaseVersion.h" #include "Component.h" #include "LaunchProfile.h" -#include "Library.h" -#include "MojangDownloadInfo.h" -#include "ProfileUtils.h" -#include "modplatform/ResourceAPI.h" +#include "modplatform/ModIndex.h" #include "net/Mode.h" class MinecraftInstance; @@ -147,6 +143,8 @@ class PackProfile : public QAbstractListModel { void appendComponent(ComponentPtr component); std::optional getModLoaders(); + // this returns aditional loaders(Quilt supports fabric and NeoForge supports Forge) + std::optional getSupportedModLoaders(); private: void scheduleSave(); diff --git a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp index 46b489ce0..93e1b2ed9 100644 --- a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp +++ b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp @@ -41,7 +41,8 @@ static Version mcVersion(BaseInstance* inst) static ModPlatform::ModLoaderTypes mcLoaders(BaseInstance* inst) { - return static_cast(inst)->getPackProfile()->getModLoaders().value(); + return static_cast(inst)->getPackProfile()->getSupportedModLoaders().value_or( + ModPlatform::ModLoaderTypes::fromInt(0)); } GetModDependenciesTask::GetModDependenciesTask(QObject* parent, diff --git a/launcher/modplatform/flame/FlameAPI.cpp b/launcher/modplatform/flame/FlameAPI.cpp index 74d7db975..8d01f779d 100644 --- a/launcher/modplatform/flame/FlameAPI.cpp +++ b/launcher/modplatform/flame/FlameAPI.cpp @@ -6,7 +6,6 @@ #include "FlameModIndex.h" #include "Application.h" -#include "BuildConfig.h" #include "Json.h" #include "net/ApiDownload.h" #include "net/ApiUpload.h" @@ -131,19 +130,13 @@ auto FlameAPI::getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::Indexe auto obj = Json::requireObject(doc); auto arr = Json::requireArray(obj, "data"); - QJsonObject latest_file_obj; - ModPlatform::IndexedVersion ver_tmp; - for (auto file : arr) { auto file_obj = Json::requireObject(file); auto file_tmp = FlameMod::loadIndexedPackVersion(file_obj); - if (file_tmp.date > ver_tmp.date) { - ver_tmp = file_tmp; - latest_file_obj = file_obj; - } + if (file_tmp.date > ver.date && (!args.loaders.has_value() || args.loaders.value() & file_tmp.loaders)) + ver = file_tmp; } - ver = FlameMod::loadIndexedPackVersion(latest_file_obj); } catch (Json::JsonException& e) { qCritical() << "Failed to parse response from a version request."; qCritical() << e.what(); diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h index a36b99a47..12cccb6f9 100644 --- a/launcher/modplatform/flame/FlameAPI.h +++ b/launcher/modplatform/flame/FlameAPI.h @@ -48,15 +48,29 @@ class FlameAPI : public NetworkResourceAPI { return 1; if (loaders & ModPlatform::Fabric) return 4; - // TODO: remove this once Quilt drops official Fabric support - if (loaders & ModPlatform::Quilt) // NOTE: Most if not all Fabric mods should work *currently* - return 4; // FIXME: implement multiple loaders filter (this should be 5) - // TODO: remove this once NeoForge drops official Forge support - if (loaders & ModPlatform::NeoForge) // NOTE: Most if not all Forge mods should work *currently* - return 1; // FIXME: implement multiple loaders filter (this should be 6) + if (loaders & ModPlatform::Quilt) + return 5; + if (loaders & ModPlatform::NeoForge) + return 6; return 0; } + static auto getModLoaderStrings(const ModPlatform::ModLoaderTypes types) -> const QStringList + { + QStringList l; + for (auto loader : { ModPlatform::NeoForge, ModPlatform::Forge, ModPlatform::Fabric, ModPlatform::Quilt }) { + if (types & loader) { + l << QString::number(getMappedModLoader(loader)); + } + } + return l; + } + + static auto getModLoaderFilters(ModPlatform::ModLoaderTypes types) -> const QString + { + return "[" + getModLoaderStrings(types).join(',') + "]"; + } + private: [[nodiscard]] std::optional getSearchURL(SearchArgs const& args) const override { @@ -73,7 +87,7 @@ class FlameAPI : public NetworkResourceAPI { get_arguments.append(QString("sortField=%1").arg(args.sorting.value().index)); get_arguments.append("sortOrder=desc"); if (args.loaders.has_value()) - get_arguments.append(QString("modLoaderType=%1").arg(getMappedModLoader(args.loaders.value()))); + get_arguments.append(QString("modLoaderTypes=%1").arg(getModLoaderFilters(args.loaders.value()))); get_arguments.append(gameVersionStr); return "https://api.curseforge.com/v1/mods/search?gameId=432&" + get_arguments.join('&'); @@ -92,23 +106,6 @@ class FlameAPI : public NetworkResourceAPI { QStringList get_parameters; if (args.mcVersions.has_value()) get_parameters.append(QString("gameVersion=%1").arg(args.mcVersions.value().front().toString())); - - if (args.loaders.has_value()) { - int mappedModLoader = getMappedModLoader(args.loaders.value()); - - if (args.loaders.value() & ModPlatform::Quilt) { - auto overide = ModPlatform::getOverrideDeps(); - auto over = std::find_if(overide.cbegin(), overide.cend(), [addonId](auto dep) { - return dep.provider == ModPlatform::ResourceProvider::FLAME && addonId == dep.quilt; - }); - if (over != overide.cend()) { - mappedModLoader = 5; - } - } - - get_parameters.append(QString("modLoaderType=%1").arg(mappedModLoader)); - } - return url + get_parameters.join('&'); }; diff --git a/launcher/modplatform/flame/FlameCheckUpdate.cpp b/launcher/modplatform/flame/FlameCheckUpdate.cpp index e10fedc3c..476a4667a 100644 --- a/launcher/modplatform/flame/FlameCheckUpdate.cpp +++ b/launcher/modplatform/flame/FlameCheckUpdate.cpp @@ -5,13 +5,11 @@ #include #include -#include "FileSystem.h" #include "Json.h" #include "ResourceDownloadTask.h" #include "minecraft/mod/ModFolderModel.h" -#include "minecraft/mod/ResourceFolderModel.h" #include "net/ApiDownload.h" diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index 8924913bc..67e3468f9 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -81,6 +81,7 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QVector unsortedVersions; auto profile = (dynamic_cast(inst))->getPackProfile(); QString mcVersion = profile->getComponentVersion("net.minecraft"); + auto loaders = profile->getSupportedModLoaders(); for (auto versionIter : arr) { auto obj = versionIter.toObject(); @@ -89,7 +90,8 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, if (!file.addonId.isValid()) file.addonId = pack.addonId; - if (file.fileId.isValid()) // Heuristic to check if the returned value is valid + if (file.fileId.isValid() && + (!loaders.has_value() || loaders.value() & file.loaders)) // Heuristic to check if the returned value is valid unsortedVersions.append(file); } @@ -186,8 +188,11 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) -> return file; } -ModPlatform::IndexedVersion FlameMod::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) +ModPlatform::IndexedVersion FlameMod::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr, const BaseInstance* inst) { + auto profile = (dynamic_cast(inst))->getPackProfile(); + QString mcVersion = profile->getComponentVersion("net.minecraft"); + auto loaders = profile->getSupportedModLoaders(); QVector versions; for (auto versionIter : arr) { auto obj = versionIter.toObject(); @@ -196,7 +201,8 @@ ModPlatform::IndexedVersion FlameMod::loadDependencyVersions(const ModPlatform:: if (!file.addonId.isValid()) file.addonId = m.addonId; - if (file.fileId.isValid()) // Heuristic to check if the returned value is valid + if (file.fileId.isValid() && + (!loaders.has_value() || loaders.value() & file.loaders)) // Heuristic to check if the returned value is valid versions.append(file); } diff --git a/launcher/modplatform/flame/FlameModIndex.h b/launcher/modplatform/flame/FlameModIndex.h index aa0d6f812..1bcaa44ba 100644 --- a/launcher/modplatform/flame/FlameModIndex.h +++ b/launcher/modplatform/flame/FlameModIndex.h @@ -19,5 +19,5 @@ void loadIndexedPackVersions(ModPlatform::IndexedPack& pack, const shared_qobject_ptr& network, const BaseInstance* inst); auto loadIndexedPackVersion(QJsonObject& obj, bool load_changelog = false) -> ModPlatform::IndexedVersion; -auto loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion; +auto loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr, const BaseInstance* inst) -> ModPlatform::IndexedVersion; } // namespace FlameMod \ No newline at end of file diff --git a/launcher/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h index 6ff8f7bcd..d0f0811b2 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.h +++ b/launcher/modplatform/modrinth/ModrinthAPI.h @@ -44,11 +44,6 @@ class ModrinthAPI : public NetworkResourceAPI { l << getModLoaderString(loader); } } - if ((types & ModPlatform::NeoForge) && - (~types & ModPlatform::Forge)) // Add Forge if NeoForge is in use, if Forge isn't already there - l << getModLoaderString(ModPlatform::Forge); - if ((types & ModPlatform::Quilt) && (~types & ModPlatform::Fabric)) // Add Fabric if Quilt is in use, if Fabric isn't already there - l << getModLoaderString(ModPlatform::Fabric); return l; } diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp index ba3aad844..c65f4fa80 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp @@ -11,7 +11,6 @@ #include "tasks/ConcurrentTask.h" #include "minecraft/mod/ModFolderModel.h" -#include "minecraft/mod/ResourceFolderModel.h" static ModrinthAPI api; static ModPlatform::ProviderCapabilities ProviderCaps; diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index 1ff5a4b9e..d857e82a8 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -93,19 +93,19 @@ void Modrinth::loadExtraPackData(ModPlatform::IndexedPack& pack, QJsonObject& ob pack.extraDataLoaded = true; } -void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, - QJsonArray& arr, - [[maybe_unused]] const shared_qobject_ptr& network, - const BaseInstance* inst) +void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArray& arr, const BaseInstance* inst) { QVector unsortedVersions; - QString mcVersion = (static_cast(inst))->getPackProfile()->getComponentVersion("net.minecraft"); + auto profile = (dynamic_cast(inst))->getPackProfile(); + QString mcVersion = profile->getComponentVersion("net.minecraft"); + auto loaders = profile->getSupportedModLoaders(); for (auto versionIter : arr) { auto obj = versionIter.toObject(); auto file = loadIndexedPackVersion(obj); - if (file.fileId.isValid()) // Heuristic to check if the returned value is valid + if (file.fileId.isValid() && + (!loaders.has_value() || loaders.value() & file.loaders)) // Heuristic to check if the returned value is valid unsortedVersions.append(file); } auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool { @@ -229,15 +229,20 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t return {}; } -auto Modrinth::loadDependencyVersions([[maybe_unused]] const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion +auto Modrinth::loadDependencyVersions([[maybe_unused]] const ModPlatform::Dependency& m, QJsonArray& arr, const BaseInstance* inst) + -> ModPlatform::IndexedVersion { - QVector versions; + auto profile = (dynamic_cast(inst))->getPackProfile(); + QString mcVersion = profile->getComponentVersion("net.minecraft"); + auto loaders = profile->getSupportedModLoaders(); + QVector versions; for (auto versionIter : arr) { auto obj = versionIter.toObject(); auto file = loadIndexedPackVersion(obj); - if (file.fileId.isValid()) // Heuristic to check if the returned value is valid + if (file.fileId.isValid() && + (!loaders.has_value() || loaders.value() & file.loaders)) // Heuristic to check if the returned value is valid versions.append(file); } auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool { diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.h b/launcher/modplatform/modrinth/ModrinthPackIndex.h index 58a0f227c..93f91eec2 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.h +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.h @@ -26,11 +26,8 @@ namespace Modrinth { void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj); void loadExtraPackData(ModPlatform::IndexedPack& m, QJsonObject& obj); -void loadIndexedPackVersions(ModPlatform::IndexedPack& pack, - QJsonArray& arr, - const shared_qobject_ptr& network, - const BaseInstance* inst); +void loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArray& arr, const BaseInstance* inst); auto loadIndexedPackVersion(QJsonObject& obj, QString hash_type = "sha512", QString filename_prefer = "") -> ModPlatform::IndexedVersion; -auto loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion; +auto loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr, const BaseInstance* inst) -> ModPlatform::IndexedVersion; } // namespace Modrinth diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index b3b60714a..7900e6c45 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -43,7 +43,6 @@ #include "FileSystem.h" #include "MainWindow.h" -#include "ui/dialogs/ExportToModListDialog.h" #include "ui_MainWindow.h" #include @@ -90,17 +89,14 @@ #include #include #include -#include "InstancePageProvider.h" #include "InstanceWindow.h" -#include "JavaCommon.h" -#include "LaunchController.h" #include "ui/dialogs/AboutDialog.h" #include "ui/dialogs/CopyInstanceDialog.h" #include "ui/dialogs/CustomMessageBox.h" -#include "ui/dialogs/EditAccountDialog.h" #include "ui/dialogs/ExportInstanceDialog.h" #include "ui/dialogs/ExportPackDialog.h" +#include "ui/dialogs/ExportToModListDialog.h" #include "ui/dialogs/IconPickerDialog.h" #include "ui/dialogs/ImportResourceDialog.h" #include "ui/dialogs/NewInstanceDialog.h" @@ -113,9 +109,13 @@ #include "ui/themes/ThemeManager.h" #include "ui/widgets/LabeledToolButton.h" +#include "minecraft/PackProfile.h" +#include "minecraft/VersionFile.h" #include "minecraft/WorldList.h" #include "minecraft/mod/ModFolderModel.h" +#include "minecraft/mod/ResourcePackFolderModel.h" #include "minecraft/mod/ShaderPackFolderModel.h" +#include "minecraft/mod/TexturePackFolderModel.h" #include "minecraft/mod/tasks/LocalResourceParse.h" #include "modplatform/flame/FlameAPI.h" @@ -123,7 +123,6 @@ #include "KonamiCode.h" #include "InstanceCopyTask.h" -#include "InstanceImportTask.h" #include "Json.h" diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index 3e18b224e..1f0fa7cd2 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -5,8 +5,6 @@ #include "ScrollMessageBox.h" #include "ui_ReviewMessageBox.h" -#include "FileSystem.h" -#include "Json.h" #include "Markdown.h" #include "tasks/ConcurrentTask.h" @@ -32,7 +30,7 @@ static std::list mcVersions(BaseInstance* inst) static std::optional mcLoaders(BaseInstance* inst) { - return { static_cast(inst)->getPackProfile()->getModLoaders() }; + return { static_cast(inst)->getPackProfile()->getSupportedModLoaders() }; } ModUpdateDialog::ModUpdateDialog(QWidget* parent, diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp index 4b82c0c5c..9e121bb61 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp +++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp @@ -279,7 +279,7 @@ QList ModDownloadDialog::getPages() { QList pages; - auto loaders = static_cast(m_instance)->getPackProfile()->getModLoaders().value(); + auto loaders = static_cast(m_instance)->getPackProfile()->getSupportedModLoaders().value(); if (ModrinthAPI::validateModLoaders(loaders)) pages.append(ModrinthModPage::create(this, *m_instance)); diff --git a/launcher/ui/pages/modplatform/ModModel.cpp b/launcher/ui/pages/modplatform/ModModel.cpp index b75378905..c628f74ac 100644 --- a/launcher/ui/pages/modplatform/ModModel.cpp +++ b/launcher/ui/pages/modplatform/ModModel.cpp @@ -33,7 +33,7 @@ ResourceAPI::SearchArgs ModModel::createSearchArguments() auto sort = getCurrentSortingMethodByIndex(); - return { ModPlatform::ResourceType::MOD, m_next_search_offset, m_search_term, sort, profile->getModLoaders(), versions }; + return { ModPlatform::ResourceType::MOD, m_next_search_offset, m_search_term, sort, profile->getSupportedModLoaders(), versions }; } ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(QModelIndex& entry) @@ -48,7 +48,7 @@ ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(QModelIndex& en if (!m_filter->versions.empty()) versions = m_filter->versions; - return { pack, versions, profile->getModLoaders() }; + return { pack, versions, profile->getSupportedModLoaders() }; } ResourceAPI::ProjectInfoArgs ModModel::createInfoArguments(QModelIndex& entry) diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index c9270d399..7eb08f6a2 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -124,7 +124,7 @@ void ModPage::updateVersionList() auto version = current_pack->versions[i]; bool valid = false; for (auto& mcVer : m_filter->versions) { - if (validateVersion(version, mcVer.toString(), packProfile->getModLoaders())) { + if (validateVersion(version, mcVer.toString(), packProfile->getSupportedModLoaders())) { valid = true; break; } diff --git a/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp index 2b020c48d..c80e4f999 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp @@ -31,7 +31,7 @@ void FlameModModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonAr auto FlameModModel::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion { - return FlameMod::loadDependencyVersions(m, arr); + return FlameMod::loadDependencyVersions(m, arr, &m_base_instance); } auto FlameModModel::documentToArray(QJsonDocument& obj) const -> QJsonArray diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp index ce7475b6e..856018294 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp @@ -39,12 +39,12 @@ void ModrinthModModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObjec void ModrinthModModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) { - ::Modrinth::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_base_instance); + ::Modrinth::loadIndexedPackVersions(m, arr, &m_base_instance); } auto ModrinthModModel::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion { - return ::Modrinth::loadDependencyVersions(m, arr); + return ::Modrinth::loadDependencyVersions(m, arr, &m_base_instance); } auto ModrinthModModel::documentToArray(QJsonDocument& obj) const -> QJsonArray @@ -66,7 +66,7 @@ void ModrinthResourcePackModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, Q void ModrinthResourcePackModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) { - ::Modrinth::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_base_instance); + ::Modrinth::loadIndexedPackVersions(m, arr, &m_base_instance); } auto ModrinthResourcePackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray @@ -88,7 +88,7 @@ void ModrinthTexturePackModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJ void ModrinthTexturePackModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) { - ::Modrinth::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_base_instance); + ::Modrinth::loadIndexedPackVersions(m, arr, &m_base_instance); } auto ModrinthTexturePackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray @@ -110,7 +110,7 @@ void ModrinthShaderPackModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJs void ModrinthShaderPackModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) { - ::Modrinth::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_base_instance); + ::Modrinth::loadIndexedPackVersions(m, arr, &m_base_instance); } auto ModrinthShaderPackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray From aa065f2b5173dc56614ac45649ada06b74681a53 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 23 Aug 2023 13:28:23 +0300 Subject: [PATCH 168/268] Updated dependency resolution Signed-off-by: Trial97 --- .../modplatform/flame/FileResolvingTask.cpp | 4 ++-- launcher/modplatform/flame/FlameAPI.h | 22 ++++--------------- .../helpers/NetworkResourceAPI.cpp | 2 +- 3 files changed, 7 insertions(+), 21 deletions(-) diff --git a/launcher/modplatform/flame/FileResolvingTask.cpp b/launcher/modplatform/flame/FileResolvingTask.cpp index 07a3ae632..b13274a6b 100644 --- a/launcher/modplatform/flame/FileResolvingTask.cpp +++ b/launcher/modplatform/flame/FileResolvingTask.cpp @@ -103,7 +103,7 @@ void Flame::FileResolvingTask::netJobFinished() auto url = QString("https://api.modrinth.com/v2/version_file/%1?algorithm=sha1").arg(hash); auto output = std::make_shared(); auto dl = Net::ApiDownload::makeByteArray(QUrl(url), output); - QObject::connect(dl.get(), &Net::Download::succeeded, [&out]() { out.resolved = true; }); + QObject::connect(dl.get(), &Net::ApiDownload::succeeded, [&out]() { out.resolved = true; }); m_checkJob->addNetAction(dl); blockedProjects.insert(&out, output); @@ -176,7 +176,7 @@ void Flame::FileResolvingTask::modrinthCheckFinished() auto url = QString("https://api.curseforge.com/v1/mods/%1").arg(projectId); auto dl = Net::ApiDownload::makeByteArray(url, output); qDebug() << "Fetching url slug for file:" << mod->fileName; - QObject::connect(dl.get(), &Net::Download::succeeded, [block, index, output]() { + QObject::connect(dl.get(), &Net::ApiDownload::succeeded, [block, index, output]() { auto mod = block->at(index); // use the shared_ptr so it is captured and only freed when we are done auto json = QJsonDocument::fromJson(*output); auto base = diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h index 12cccb6f9..e0386991d 100644 --- a/launcher/modplatform/flame/FlameAPI.h +++ b/launcher/modplatform/flame/FlameAPI.h @@ -101,30 +101,16 @@ class FlameAPI : public NetworkResourceAPI { [[nodiscard]] std::optional getVersionsURL(VersionSearchArgs const& args) const override { auto addonId = args.pack.addonId.toString(); - QString url{ QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&").arg(addonId) }; + QString url = QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000").arg(addonId); - QStringList get_parameters; if (args.mcVersions.has_value()) - get_parameters.append(QString("gameVersion=%1").arg(args.mcVersions.value().front().toString())); - return url + get_parameters.join('&'); + url += QString("&gameVersion=%1").arg(args.mcVersions.value().front().toString()); + return url; }; [[nodiscard]] std::optional getDependencyURL(DependencySearchArgs const& args) const override { - auto mappedModLoader = getMappedModLoader(args.loader); auto addonId = args.dependency.addonId.toString(); - if (args.loader & ModPlatform::Quilt) { - auto overide = ModPlatform::getOverrideDeps(); - auto over = std::find_if(overide.cbegin(), overide.cend(), [addonId](auto dep) { - return dep.provider == ModPlatform::ResourceProvider::FLAME && addonId == dep.quilt; - }); - if (over != overide.cend()) { - mappedModLoader = 5; - } - } - return QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&gameVersion=%2&modLoaderType=%3") - .arg(addonId) - .arg(args.mcVersion.toString()) - .arg(mappedModLoader); + return QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&gameVersion=%2").arg(addonId, args.mcVersion.toString()); }; }; diff --git a/launcher/modplatform/helpers/NetworkResourceAPI.cpp b/launcher/modplatform/helpers/NetworkResourceAPI.cpp index 46b966620..78b39fffa 100644 --- a/launcher/modplatform/helpers/NetworkResourceAPI.cpp +++ b/launcher/modplatform/helpers/NetworkResourceAPI.cpp @@ -131,7 +131,7 @@ Task::Ptr NetworkResourceAPI::getDependencyVersion(DependencySearchArgs&& args, auto netJob = makeShared(QString("%1::Dependency").arg(args.dependency.addonId.toString()), APPLICATION->network()); auto response = std::make_shared(); - netJob->addNetAction(Net::Download::makeByteArray(versions_url, response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(versions_url, response)); QObject::connect(netJob.get(), &NetJob::succeeded, [=] { QJsonParseError parse_error{}; From 6178e5a975d7e967df0d3d45e402ac0fcfb4b43a Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 23 Aug 2023 13:36:31 +0300 Subject: [PATCH 169/268] reverted change for optional Signed-off-by: Trial97 --- launcher/minecraft/PackProfile.h | 1 + launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp | 3 +-- launcher/modplatform/flame/FileResolvingTask.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/launcher/minecraft/PackProfile.h b/launcher/minecraft/PackProfile.h index c41601fb2..e72b6ebfe 100644 --- a/launcher/minecraft/PackProfile.h +++ b/launcher/minecraft/PackProfile.h @@ -44,6 +44,7 @@ #include #include #include +#include #include "Component.h" #include "LaunchProfile.h" diff --git a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp index 93e1b2ed9..b273d70d7 100644 --- a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp +++ b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp @@ -41,8 +41,7 @@ static Version mcVersion(BaseInstance* inst) static ModPlatform::ModLoaderTypes mcLoaders(BaseInstance* inst) { - return static_cast(inst)->getPackProfile()->getSupportedModLoaders().value_or( - ModPlatform::ModLoaderTypes::fromInt(0)); + return static_cast(inst)->getPackProfile()->getSupportedModLoaders().value(); } GetModDependenciesTask::GetModDependenciesTask(QObject* parent, diff --git a/launcher/modplatform/flame/FileResolvingTask.cpp b/launcher/modplatform/flame/FileResolvingTask.cpp index b13274a6b..463014b6e 100644 --- a/launcher/modplatform/flame/FileResolvingTask.cpp +++ b/launcher/modplatform/flame/FileResolvingTask.cpp @@ -154,7 +154,7 @@ void Flame::FileResolvingTask::modrinthCheckFinished() // If there's more than one mod loader for this version, we can't know for sure // which file is relative to each loader, so it's best to not use any one and // let the user download it manually. - if (std::bitset<8>(file.loaders.toInt()).count() <= 1) { + if (std::bitset<8>(file.loaders).count() <= 1) { out->url = file.downloadUrl; qDebug() << "Found alternative on modrinth " << out->fileName; } else { From 095de5ed4ba373a44211ae3eb616139c69a0151a Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Wed, 23 Aug 2023 13:46:04 +0200 Subject: [PATCH 170/268] chore(nix): update clang-format to clang-tools 16 Signed-off-by: Sefa Eyeoglu --- nix/dev.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nix/dev.nix b/nix/dev.nix index c39e15653..6699cac6a 100644 --- a/nix/dev.nix +++ b/nix/dev.nix @@ -23,6 +23,8 @@ types_or = ["c" "c++" "java" "json" "objective-c"]; }; }; + + tools.clang-tools = pkgs.clang-tools_16; }; }; From 85c23b26de7d8161729abfa3bc94915645192087 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Wed, 23 Aug 2023 13:46:23 +0200 Subject: [PATCH 171/268] chore: reformat Signed-off-by: Sefa Eyeoglu --- launcher/Version.h | 20 ++++---------------- launcher/launch/LogModel.h | 2 +- launcher/ui/MainWindow.h | 5 +---- launcher/ui/pages/instance/ServersPage.cpp | 20 ++++---------------- libraries/LocalPeer/src/LocalPeer.cpp | 2 +- tests/FileSystem_test.cpp | 5 +---- 6 files changed, 12 insertions(+), 42 deletions(-) diff --git a/launcher/Version.h b/launcher/Version.h index 92bff9ba9..9c043b013 100644 --- a/launcher/Version.h +++ b/launcher/Version.h @@ -103,14 +103,8 @@ class Version { QString m_fullString; - [[nodiscard]] inline bool isAppendix() const - { - return m_stringPart.startsWith('+'); - } - [[nodiscard]] inline bool isPreRelease() const - { - return m_stringPart.startsWith('-') && m_stringPart.length() > 1; - } + [[nodiscard]] inline bool isAppendix() const { return m_stringPart.startsWith('+'); } + [[nodiscard]] inline bool isPreRelease() const { return m_stringPart.startsWith('-') && m_stringPart.length() > 1; } inline bool operator==(const Section& other) const { @@ -156,14 +150,8 @@ class Version { return m_fullString < other.m_fullString; } - inline bool operator!=(const Section& other) const - { - return !(*this == other); - } - inline bool operator>(const Section& other) const - { - return !(*this < other || *this == other); - } + inline bool operator!=(const Section& other) const { return !(*this == other); } + inline bool operator>(const Section& other) const { return !(*this < other || *this == other); } }; private: diff --git a/launcher/launch/LogModel.h b/launcher/launch/LogModel.h index ba2e4054a..18e51d7e3 100644 --- a/launcher/launch/LogModel.h +++ b/launcher/launch/LogModel.h @@ -30,7 +30,7 @@ class LogModel : public QAbstractListModel { enum Roles { LevelRole = Qt::UserRole }; - private /* types */: + private /* types */: struct entry { MessageLevel::Enum level; QString line; diff --git a/launcher/ui/MainWindow.h b/launcher/ui/MainWindow.h index b6f45da2e..0b6144522 100644 --- a/launcher/ui/MainWindow.h +++ b/launcher/ui/MainWindow.h @@ -151,10 +151,7 @@ class MainWindow : public QMainWindow { void deleteGroup(); void undoTrashInstance(); - inline void on_actionExportInstance_triggered() - { - on_actionExportInstanceZip_triggered(); - } + inline void on_actionExportInstance_triggered() { on_actionExportInstanceZip_triggered(); } void on_actionExportInstanceZip_triggered(); void on_actionExportInstanceMrPack_triggered(); void on_actionExportInstanceFlamePack_triggered(); diff --git a/launcher/ui/pages/instance/ServersPage.cpp b/launcher/ui/pages/instance/ServersPage.cpp index 6bf802e63..2142e6c9f 100644 --- a/launcher/ui/pages/instance/ServersPage.cpp +++ b/launcher/ui/pages/instance/ServersPage.cpp @@ -354,14 +354,8 @@ class ServersModel : public QAbstractListModel { } } - virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override - { - return parent.isValid() ? 0 : m_servers.size(); - } - int columnCount(const QModelIndex& parent) const override - { - return parent.isValid() ? 0 : COLUMN_COUNT; - } + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override { return parent.isValid() ? 0 : m_servers.size(); } + int columnCount(const QModelIndex& parent) const override { return parent.isValid() ? 0 : COLUMN_COUNT; } Server* at(int index) { @@ -445,10 +439,7 @@ class ServersModel : public QAbstractListModel { qDebug() << "Changed:" << path; load(); } - void fileChanged(const QString& path) - { - qDebug() << "Changed:" << path; - } + void fileChanged(const QString& path) { qDebug() << "Changed:" << path; } private slots: void save_internal() @@ -492,10 +483,7 @@ class ServersModel : public QAbstractListModel { m_saveTimer.stop(); } - bool saveIsScheduled() const - { - return m_dirty; - } + bool saveIsScheduled() const { return m_dirty; } void updateFSObserver() { diff --git a/libraries/LocalPeer/src/LocalPeer.cpp b/libraries/LocalPeer/src/LocalPeer.cpp index ab528c2a1..bd407042f 100644 --- a/libraries/LocalPeer/src/LocalPeer.cpp +++ b/libraries/LocalPeer/src/LocalPeer.cpp @@ -220,5 +220,5 @@ void LocalPeer::receiveConnection() socket->waitForBytesWritten(1000); socket->waitForDisconnected(1000); // make sure client reads ack delete socket; - emit messageReceived(uMsg); //### (might take a long time to return) + emit messageReceived(uMsg); // ### (might take a long time to return) } diff --git a/tests/FileSystem_test.cpp b/tests/FileSystem_test.cpp index e19410423..1d3cee85f 100644 --- a/tests/FileSystem_test.cpp +++ b/tests/FileSystem_test.cpp @@ -353,10 +353,7 @@ class FileSystemTest : public QObject { } } - void test_getDesktop() - { - QCOMPARE(FS::getDesktopDir(), QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)); - } + void test_getDesktop() { QCOMPARE(FS::getDesktopDir(), QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)); } void test_link() { From d7b6450613b7c106dcc06c6e160ba124990e4e93 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Wed, 23 Aug 2023 13:47:04 +0200 Subject: [PATCH 172/268] fix(nix): reload direnv if parts change Signed-off-by: Sefa Eyeoglu --- .envrc | 1 + 1 file changed, 1 insertion(+) diff --git a/.envrc b/.envrc index 3550a30f2..190b5b2b3 100644 --- a/.envrc +++ b/.envrc @@ -1 +1,2 @@ use flake +watch_file nix/*.nix From 41bd008f5d8d5872e1ab2646c78908e27eae7b73 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Wed, 23 Aug 2023 13:49:09 +0200 Subject: [PATCH 173/268] chore(nix): remove pre-commit tools from shell use `pre-commit` CLI instead! Signed-off-by: Sefa Eyeoglu --- nix/dev.nix | 7 ------- 1 file changed, 7 deletions(-) diff --git a/nix/dev.nix b/nix/dev.nix index 6699cac6a..a9c1dc65d 100644 --- a/nix/dev.nix +++ b/nix/dev.nix @@ -30,13 +30,6 @@ devShells.default = pkgs.mkShell { inherit (self.checks.${system}.pre-commit-check) shellHook; - packages = with pkgs; [ - nodePackages.markdownlint-cli - alejandra - deadnix - clang-tools - nil - ]; inputsFrom = [self.packages.${system}.prismlauncher-unwrapped]; buildInputs = with pkgs; [ccache ninja]; From 1515607060d23bd3467a77e2f6045681daa94998 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 23 Aug 2023 20:16:51 +0300 Subject: [PATCH 174/268] updated getMappedModLoader Signed-off-by: Trial97 --- launcher/modplatform/flame/FlameAPI.h | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h index e0386991d..0008643dd 100644 --- a/launcher/modplatform/flame/FlameAPI.h +++ b/launcher/modplatform/flame/FlameAPI.h @@ -41,18 +41,23 @@ class FlameAPI : public NetworkResourceAPI { } } - static int getMappedModLoader(ModPlatform::ModLoaderTypes loaders) + static int getMappedModLoader(ModPlatform::ModLoaderType loaders) { // https://docs.curseforge.com/?http#tocS_ModLoaderType - if (loaders & ModPlatform::Forge) - return 1; - if (loaders & ModPlatform::Fabric) - return 4; - if (loaders & ModPlatform::Quilt) - return 5; - if (loaders & ModPlatform::NeoForge) - return 6; - return 0; + switch (loaders) { + case ModPlatform::Forge: + return 1; + case ModPlatform::Cauldron: + return 2; + case ModPlatform::LiteLoader: + return 3; + case ModPlatform::Fabric: + return 4; + case ModPlatform::Quilt: + return 5; + case ModPlatform::NeoForge: + return 6; + } } static auto getModLoaderStrings(const ModPlatform::ModLoaderTypes types) -> const QStringList From 6c0492c0d174f45262f072d6900c2fcf098d209b Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 23 Aug 2023 21:09:32 +0300 Subject: [PATCH 175/268] return 0 for any Signed-off-by: Trial97 --- launcher/modplatform/flame/FlameAPI.h | 1 + 1 file changed, 1 insertion(+) diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h index 0008643dd..745f889d5 100644 --- a/launcher/modplatform/flame/FlameAPI.h +++ b/launcher/modplatform/flame/FlameAPI.h @@ -58,6 +58,7 @@ class FlameAPI : public NetworkResourceAPI { case ModPlatform::NeoForge: return 6; } + return 0; } static auto getModLoaderStrings(const ModPlatform::ModLoaderTypes types) -> const QStringList From 783af2c06a2d11391e32f1f938ad3ad3da606f38 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 24 Aug 2023 04:32:25 +0000 Subject: [PATCH 176/268] chore(deps): update determinatesystems/update-flake-lock action to v20 --- .github/workflows/update-flake.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-flake.yml b/.github/workflows/update-flake.yml index 6bad57030..ad4016ff4 100644 --- a/.github/workflows/update-flake.yml +++ b/.github/workflows/update-flake.yml @@ -19,7 +19,7 @@ jobs: - uses: actions/checkout@v3 - uses: cachix/install-nix-action@v22 - - uses: DeterminateSystems/update-flake-lock@v19 + - uses: DeterminateSystems/update-flake-lock@v20 with: commit-msg: "chore(nix): update lockfile" pr-title: "chore(nix): update lockfile" From ea384d59fbff194f26f4fc678ce2388f01b2f484 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 24 Aug 2023 12:41:37 +0300 Subject: [PATCH 177/268] use qt separtor for file path Signed-off-by: Trial97 --- launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp index 9ff6b374d..47a0de5fe 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp @@ -322,7 +322,7 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path, bool had_optional = false; for (const auto& modInfo : jsonFiles) { Modrinth::File file; - file.path = Json::requireString(modInfo, "path"); + file.path = Json::requireString(modInfo, "path").replace("\\", "/"); auto env = Json::ensureObject(modInfo, "env"); // 'env' field is optional From 2990c5d0c9ef9e36daa0ed7315521852164fe59e Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 24 Aug 2023 12:44:11 +0300 Subject: [PATCH 178/268] Added optional mods dialog Signed-off-by: Trial97 --- launcher/CMakeLists.txt | 4 + .../flame/FlameInstanceCreationTask.cpp | 29 +++- launcher/modplatform/flame/PackManifest.h | 2 +- .../modrinth/ModrinthInstanceCreationTask.cpp | 40 +++-- .../modrinth/ModrinthPackManifest.h | 1 + .../pages/modplatform/OptionalModDialog.cpp | 143 ++++++++++++++++++ .../ui/pages/modplatform/OptionalModDialog.h | 76 ++++++++++ .../ui/pages/modplatform/OptionalModDialog.ui | 105 +++++++++++++ .../atlauncher/AtlOptionalModDialog.h | 2 +- 9 files changed, 382 insertions(+), 20 deletions(-) create mode 100644 launcher/ui/pages/modplatform/OptionalModDialog.cpp create mode 100644 launcher/ui/pages/modplatform/OptionalModDialog.h create mode 100644 launcher/ui/pages/modplatform/OptionalModDialog.ui diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 18e0acab1..c73d89ec3 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -916,6 +916,9 @@ SET(LAUNCHER_SOURCES ui/pages/modplatform/ImportPage.cpp ui/pages/modplatform/ImportPage.h + ui/pages/modplatform/OptionalModDialog.cpp + ui/pages/modplatform/OptionalModDialog.h + ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp ui/pages/modplatform/modrinth/ModrinthResourceModels.h ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp @@ -1080,6 +1083,7 @@ qt_wrap_ui(LAUNCHER_UI ui/pages/modplatform/legacy_ftb/Page.ui ui/pages/modplatform/import_ftb/ImportFTBPage.ui ui/pages/modplatform/ImportPage.ui + ui/pages/modplatform/OptionalModDialog.ui ui/pages/modplatform/modrinth/ModrinthPage.ui ui/pages/modplatform/technic/TechnicPage.ui ui/widgets/InstanceCardWidget.ui diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp index 45b4e2125..ead5b464e 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp @@ -62,6 +62,7 @@ #include "minecraft/World.h" #include "minecraft/mod/tasks/LocalResourceParse.h" #include "net/ApiDownload.h" +#include "ui/pages/modplatform/OptionalModDialog.h" static const FlameAPI api; @@ -509,13 +510,33 @@ void FlameCreationTask::idResolverSucceeded(QEventLoop& loop) void FlameCreationTask::setupDownloadJob(QEventLoop& loop) { m_files_job.reset(new NetJob(tr("Mod Download Flame"), APPLICATION->network())); - for (const auto& result : m_mod_id_resolver->getResults().files) { - QString filename = result.fileName; + auto results = m_mod_id_resolver->getResults().files; + + QStringList optionalFiles; + for (auto& result : results) { if (!result.required) { - filename += ".disabled"; + optionalFiles << FS::PathCombine(result.targetFolder, result.fileName); + } + } + + QStringList selectedOptionalMods; + if (!optionalFiles.empty()) { + OptionalModDialog optionalModDialog(m_parent, optionalFiles); + if (optionalModDialog.exec() == QDialog::Rejected) { + emitAborted(); + loop.quit(); + return; } - auto relpath = FS::PathCombine("minecraft", result.targetFolder, filename); + selectedOptionalMods = optionalModDialog.getResult(); + } + for (const auto& result : results) { + auto relpath = FS::PathCombine(result.targetFolder, result.fileName); + if (!result.required && !selectedOptionalMods.contains(relpath)) { + relpath += ".disabled"; + } + + relpath = FS::PathCombine("minecraft", relpath); auto path = FS::PathCombine(m_stagingPath, relpath); switch (result.type) { diff --git a/launcher/modplatform/flame/PackManifest.h b/launcher/modplatform/flame/PackManifest.h index 854cdbc41..4417c2430 100644 --- a/launcher/modplatform/flame/PackManifest.h +++ b/launcher/modplatform/flame/PackManifest.h @@ -48,7 +48,7 @@ struct File { int projectId = 0; int fileId = 0; - // NOTE: the opposite to 'optional'. This is at the time of writing unused. + // NOTE: the opposite to 'optional' bool required = true; QString hash; // NOTE: only set on blocked files ! Empty otherwise. diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp index 47a0de5fe..1be58c2b8 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp @@ -9,6 +9,7 @@ #include "modplatform/helpers/OverrideUtils.h" +#include "modplatform/modrinth/ModrinthPackManifest.h" #include "net/ChecksumValidator.h" #include "net/ApiDownload.h" @@ -16,8 +17,10 @@ #include "settings/INISettingsObject.h" #include "ui/dialogs/CustomMessageBox.h" +#include "ui/pages/modplatform/OptionalModDialog.h" #include +#include bool ModrinthCreationTask::abort() { @@ -319,7 +322,7 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path, } auto jsonFiles = Json::requireIsArrayOf(obj, "files", "modrinth.index.json"); - bool had_optional = false; + std::vector optionalFiles; for (const auto& modInfo : jsonFiles) { Modrinth::File file; file.path = Json::requireString(modInfo, "path").replace("\\", "/"); @@ -331,18 +334,7 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path, if (support == "unsupported") { continue; } else if (support == "optional") { - // TODO: Make a review dialog for choosing which ones the user wants! - if (!had_optional && show_optional_dialog) { - had_optional = true; - auto info = CustomMessageBox::selectable( - m_parent, tr("Optional mod detected!"), - tr("One or more mods from this modpack are optional. They will be downloaded, but disabled by default!"), - QMessageBox::Information); - info->exec(); - } - - if (file.path.endsWith(".jar")) - file.path += ".disabled"; + file.required = false; } } @@ -385,9 +377,29 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path, } } - files.push_back(file); + (file.required ? files : optionalFiles).push_back(file); } + if (!optionalFiles.empty()) { + QStringList oFiles; + for (auto file : optionalFiles) + oFiles.push_back(file.path); + OptionalModDialog optionalModDialog(m_parent, oFiles); + if (optionalModDialog.exec() == QDialog::Rejected) { + emitAborted(); + return false; + } + + auto selectedMods = optionalModDialog.getResult(); + for (auto file : optionalFiles) { + if (selectedMods.contains(file.path)) { + file.required = true; + } else if (file.path.endsWith(".jar")) { + file.path += ".disabled"; + } + files.push_back(file); + } + } if (set_internal_data) { auto dependencies = Json::requireObject(obj, "dependencies", "modrinth.index.json"); for (auto it = dependencies.begin(), end = dependencies.end(); it != end; ++it) { diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.h b/launcher/modplatform/modrinth/ModrinthPackManifest.h index effa1a84a..93ae69697 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.h +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.h @@ -55,6 +55,7 @@ struct File { QCryptographicHash::Algorithm hashAlgorithm; QByteArray hash; QQueue downloads; + bool required = true; }; struct DonationData { diff --git a/launcher/ui/pages/modplatform/OptionalModDialog.cpp b/launcher/ui/pages/modplatform/OptionalModDialog.cpp new file mode 100644 index 000000000..620de4f74 --- /dev/null +++ b/launcher/ui/pages/modplatform/OptionalModDialog.cpp @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "OptionalModDialog.h" +#include "ui_OptionalModDialog.h" + +OptionalModListModel::OptionalModListModel(QWidget* parent, QStringList mods) : QAbstractListModel(parent), m_mods(mods) {} + +QStringList OptionalModListModel::getResult() +{ + QStringList result; + for (const auto& mod : m_mods) { + if (m_selected.value(mod, false)) { + result << mod; + } + } + return result; +} + +int OptionalModListModel::rowCount(const QModelIndex& parent) const +{ + return parent.isValid() ? 0 : m_mods.size(); +} + +int OptionalModListModel::columnCount(const QModelIndex& parent) const +{ + // Enabled, Name + return parent.isValid() ? 0 : 2; +} + +QVariant OptionalModListModel::data(const QModelIndex& index, int role) const +{ + auto row = index.row(); + auto mod = m_mods.at(row); + + if (role == Qt::DisplayRole && index.column() == NameColumn) { + return mod; + } else if (role == Qt::CheckStateRole && index.column() == EnabledColumn) { + return m_selected.value(mod, false) ? Qt::Checked : Qt::Unchecked; + } + + return {}; +} + +bool OptionalModListModel::setData(const QModelIndex& index, [[maybe_unused]] const QVariant& value, int role) +{ + if (role == Qt::CheckStateRole) { + auto row = index.row(); + auto mod = m_mods.at(row); + + toggleMod(mod, row); + return true; + } + + return false; +} + +QVariant OptionalModListModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { + switch (section) { + case EnabledColumn: + return QString(); + case NameColumn: + return QString("Name"); + } + } + + return {}; +} + +Qt::ItemFlags OptionalModListModel::flags(const QModelIndex& index) const +{ + auto flags = QAbstractListModel::flags(index); + if (index.isValid() && index.column() == EnabledColumn) { + flags |= Qt::ItemIsUserCheckable; + } + return flags; +} + +void OptionalModListModel::toggleAll(bool enabled) +{ + for (const auto& mod : m_mods) { + m_selected[mod] = enabled; + } + + emit dataChanged(OptionalModListModel::index(0, EnabledColumn), OptionalModListModel::index(m_mods.size() - 1, EnabledColumn)); +} + +void OptionalModListModel::toggleMod(QString mod, int index) +{ + auto enable = !m_selected.value(mod, false); + + setMod(mod, index, enable); +} + +void OptionalModListModel::setMod(QString mod, int index, bool enable, bool shouldEmit) +{ + if (m_selected.value(mod, false) == enable) + return; + + m_selected[mod] = enable; + + if (shouldEmit) { + emit dataChanged(OptionalModListModel::index(index, EnabledColumn), OptionalModListModel::index(index, EnabledColumn)); + } +} + +OptionalModDialog::OptionalModDialog(QWidget* parent, QStringList mods) : QDialog(parent), ui(new Ui::OptionalModDialog) +{ + ui->setupUi(this); + + listModel = new OptionalModListModel(this, mods); + ui->treeView->setModel(listModel); + + ui->treeView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + ui->treeView->header()->setSectionResizeMode(OptionalModListModel::NameColumn, QHeaderView::Stretch); + + connect(ui->selectAllButton, &QPushButton::clicked, listModel, &OptionalModListModel::selectAll); + connect(ui->clearAllButton, &QPushButton::clicked, listModel, &OptionalModListModel::clearAll); + connect(ui->installButton, &QPushButton::clicked, this, &QDialog::accept); + connect(ui->cancelButton, &QPushButton::clicked, this, &QDialog::reject); +} + +OptionalModDialog::~OptionalModDialog() +{ + delete ui; +} diff --git a/launcher/ui/pages/modplatform/OptionalModDialog.h b/launcher/ui/pages/modplatform/OptionalModDialog.h new file mode 100644 index 000000000..4e22d0e46 --- /dev/null +++ b/launcher/ui/pages/modplatform/OptionalModDialog.h @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include + +namespace Ui { +class OptionalModDialog; +} + +class OptionalModListModel : public QAbstractListModel { + Q_OBJECT + public: + enum Columns { + EnabledColumn = 0, + NameColumn, + }; + + OptionalModListModel(QWidget* parent, QStringList mods); + + QStringList getResult(); + + int rowCount(const QModelIndex& parent) const override; + int columnCount(const QModelIndex& parent) const override; + + QVariant data(const QModelIndex& index, int role) const override; + bool setData(const QModelIndex& index, const QVariant& value, int role) override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + + Qt::ItemFlags flags(const QModelIndex& index) const override; + + public slots: + void selectAll() { toggleAll(true); } + void clearAll() { toggleAll(false); }; + void toggleAll(bool enabled); + + private: + void toggleMod(QString mod, int index); + void setMod(QString mod, int index, bool enable, bool shouldEmit = true); + + private: + QStringList m_mods; + QHash m_selected; +}; + +class OptionalModDialog : public QDialog { + Q_OBJECT + + public: + OptionalModDialog(QWidget* parent, QStringList mods); + ~OptionalModDialog() override; + + QStringList getResult() { return listModel->getResult(); } + + private: + Ui::OptionalModDialog* ui; + + OptionalModListModel* listModel; +}; diff --git a/launcher/ui/pages/modplatform/OptionalModDialog.ui b/launcher/ui/pages/modplatform/OptionalModDialog.ui new file mode 100644 index 000000000..76d1da89d --- /dev/null +++ b/launcher/ui/pages/modplatform/OptionalModDialog.ui @@ -0,0 +1,105 @@ + + + OptionalModDialog + + + + 0 + 0 + 550 + 310 + + + + Select Mods To Install + + + + + + + + + 11 + 75 + true + + + + Select optional mods to install. + + + Qt::AlignCenter + + + + + + + + true + + + + Note: All files will be downloaded but the unselected mods will be disabled. + + + Qt::AlignCenter + + + + + + + + + + + + + + true + + + Cancel + + + + + + + Clear All + + + + + + + Select All + + + + + + + Install + + + true + + + + + + + + + + ModListView + QTreeView +
ui/widgets/ModListView.h
+
+
+ + +
diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.h b/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.h index 55903003b..767d277d9 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.h +++ b/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.h @@ -38,7 +38,7 @@ #include #include -#include "modplatform/atlauncher/ATLPackIndex.h" +#include "modplatform/atlauncher/ATLPackManifest.h" #include "net/NetJob.h" namespace Ui { From bb4b89470d2c6f051d4c75de4a29c8dc1d836122 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 24 Aug 2023 12:46:23 +0300 Subject: [PATCH 179/268] fixed icon importing Signed-off-by: Trial97 --- launcher/modplatform/atlauncher/ATLPackIndex.cpp | 2 +- launcher/modplatform/flame/FlamePackIndex.cpp | 4 +++- launcher/modplatform/modrinth/ModrinthPackManifest.cpp | 3 ++- launcher/ui/dialogs/NewInstanceDialog.cpp | 3 +-- launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp | 2 +- launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp | 2 +- launcher/ui/pages/modplatform/flame/FlamePage.cpp | 3 +-- launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp | 2 +- launcher/ui/pages/modplatform/legacy_ftb/Page.cpp | 2 -- launcher/ui/pages/modplatform/technic/TechnicModel.cpp | 5 +++-- 10 files changed, 14 insertions(+), 14 deletions(-) diff --git a/launcher/modplatform/atlauncher/ATLPackIndex.cpp b/launcher/modplatform/atlauncher/ATLPackIndex.cpp index 3be169739..84b0cc9e2 100644 --- a/launcher/modplatform/atlauncher/ATLPackIndex.cpp +++ b/launcher/modplatform/atlauncher/ATLPackIndex.cpp @@ -43,5 +43,5 @@ void ATLauncher::loadIndexedPack(ATLauncher::IndexedPack& m, QJsonObject& obj) m.system = Json::ensureBoolean(obj, QString("system"), false); m.description = Json::ensureString(obj, "description", ""); - m.safeName = Json::requireString(obj, "name").replace(QRegularExpression("[^A-Za-z0-9]"), ""); + m.safeName = "atl_" + Json::requireString(obj, "name").replace(QRegularExpression("[^A-Za-z0-9]"), "").toLower() + ".png"; } diff --git a/launcher/modplatform/flame/FlamePackIndex.cpp b/launcher/modplatform/flame/FlamePackIndex.cpp index 21835a543..1b8534cee 100644 --- a/launcher/modplatform/flame/FlamePackIndex.cpp +++ b/launcher/modplatform/flame/FlamePackIndex.cpp @@ -1,4 +1,6 @@ #include "FlamePackIndex.h" +#include +#include #include "Json.h" @@ -9,8 +11,8 @@ void Flame::loadIndexedPack(Flame::IndexedPack& pack, QJsonObject& obj) pack.description = Json::ensureString(obj, "summary", ""); auto logo = Json::requireObject(obj, "logo"); - pack.logoName = Json::requireString(logo, "title"); pack.logoUrl = Json::requireString(logo, "thumbnailUrl"); + pack.logoName = Json::requireString(obj, "slug") + "." + QFileInfo(QUrl(pack.logoUrl).fileName()).suffix(); auto authors = Json::requireArray(obj, "authors"); for (auto authorIter : authors) { diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp index 0d07c6361..4565e585f 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp @@ -35,6 +35,7 @@ */ #include "ModrinthPackManifest.h" +#include #include "Json.h" #include "modplatform/modrinth/ModrinthAPI.h" @@ -56,8 +57,8 @@ void loadIndexedPack(Modpack& pack, QJsonObject& obj) pack.description = Json::ensureString(obj, "description"); auto temp_author_name = Json::ensureString(obj, "author"); pack.author = std::make_tuple(temp_author_name, api.getAuthorURL(temp_author_name)); - pack.iconName = QString("modrinth_%1").arg(Json::ensureString(obj, "slug")); pack.iconUrl = Json::ensureString(obj, "icon_url"); + pack.iconName = QString("modrinth_%1.%2").arg(Json::ensureString(obj, "slug"), QFileInfo(pack.iconUrl.fileName()).suffix()); } void loadIndexedInfo(Modpack& pack, QJsonObject& obj) diff --git a/launcher/ui/dialogs/NewInstanceDialog.cpp b/launcher/ui/dialogs/NewInstanceDialog.cpp index 9613c6b00..4c8708bc7 100644 --- a/launcher/ui/dialogs/NewInstanceDialog.cpp +++ b/launcher/ui/dialogs/NewInstanceDialog.cpp @@ -237,8 +237,7 @@ void NewInstanceDialog::setSuggestedIcon(const QString& key) InstanceTask* NewInstanceDialog::extractTask() { - InstanceTask* extracted = creationTask.get(); - creationTask.release(); + InstanceTask* extracted = creationTask.release(); InstanceName inst_name(ui->instNameTextBox->placeholderText().trimmed(), importVersion); inst_name.setName(ui->instNameTextBox->text().trimmed()); diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp index 39f4f346a..5fc9ce06f 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp @@ -56,7 +56,7 @@ QVariant ListModel::data(const QModelIndex& index, int role) const } auto icon = APPLICATION->getThemedIcon("atlauncher-placeholder"); - auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/images/%1.png").arg(pack.safeName.toLower()); + auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/images/%1").arg(pack.safeName); ((ListModel*)this)->requestLogo(pack.safeName, url); return icon; diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp index 5e3b9ecf1..61512df5e 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp @@ -113,7 +113,7 @@ void AtlPage::suggestCurrent() dialog->setSuggestedPack(selected.name, selectedVersion, new ATLauncher::PackInstallTask(uiSupport, selected.name, selectedVersion)); auto editedLogoName = selected.safeName; - auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/images/%1.png").arg(selected.safeName.toLower()); + auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/images/%1").arg(selected.safeName); listModel->getLogo(selected.safeName, url, [this, editedLogoName](QString logo) { dialog->setSuggestedIconFromFile(logo, editedLogoName); }); } diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.cpp b/launcher/ui/pages/modplatform/flame/FlamePage.cpp index 183e16f90..6891882cd 100644 --- a/launcher/ui/pages/modplatform/flame/FlamePage.cpp +++ b/launcher/ui/pages/modplatform/flame/FlamePage.cpp @@ -209,8 +209,7 @@ void FlamePage::suggestCurrent() extra_info.insert("pack_version_id", QString::number(version.fileId)); dialog->setSuggestedPack(current.name, new InstanceImportTask(version.downloadUrl, this, std::move(extra_info))); - QString editedLogoName; - editedLogoName = "curseforge_" + current.logoName; + QString editedLogoName = "curseforge_" + current.logoName; listModel->getLogo(current.logoName, current.logoUrl, [this, editedLogoName](QString logo) { dialog->setSuggestedIconFromFile(logo, editedLogoName); }); } diff --git a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp index 5c9ff63b2..b3b18e2ca 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp +++ b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp @@ -76,7 +76,7 @@ void ImportFTBPage::suggestCurrent() } dialog->setSuggestedPack(selected.name, new PackInstallTask(selected)); - QString editedLogoName = QString("ftb_%1").arg(selected.id); + QString editedLogoName = QString("ftb_%1.jpg").arg(selected.id); dialog->setSuggestedIconFromFile(FS::PathCombine(selected.path, "folder.jpg"), editedLogoName); } diff --git a/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp b/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp index 0103bbaa2..243c90dcd 100644 --- a/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp +++ b/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp @@ -180,8 +180,6 @@ void Page::suggestCurrent() editedLogoName = "ftb_" + selected.logo; } - editedLogoName = editedLogoName.left(editedLogoName.lastIndexOf(".png")); - if (selected.type == PackType::Public) { publicListModel->getLogo(selected.logo, [this, editedLogoName](QString logo) { dialog->setSuggestedIconFromFile(logo, editedLogoName); }); diff --git a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp index e8c5ac922..6fbfd7624 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp +++ b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp @@ -34,6 +34,7 @@ */ #include "TechnicModel.h" +#include #include "Application.h" #include "BuildConfig.h" #include "Json.h" @@ -159,7 +160,7 @@ void Technic::ListModel::searchRequestFinished() pack.logoName = "null"; } else { pack.logoUrl = rawURL; - pack.logoName = rawURL.section(QLatin1Char('/'), -1); + pack.logoName = QUrl(rawURL).fileName(); } pack.broken = false; newList.append(pack); @@ -181,7 +182,7 @@ void Technic::ListModel::searchRequestFinished() auto iconUrl = Json::requireString(iconObj, "url"); pack.logoUrl = iconUrl; - pack.logoName = iconUrl.section(QLatin1Char('/'), -1); + pack.logoName = QUrl(iconUrl).fileName(); } else { pack.logoUrl = "null"; pack.logoName = "null"; From f897b14e3e8ae658a6e63ffd9f61b7ad8a4259bf Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 24 Aug 2023 13:05:27 +0300 Subject: [PATCH 180/268] changed technic icon name retrival Signed-off-by: Trial97 --- .../ui/pages/modplatform/import_ftb/ImportFTBPage.cpp | 2 +- launcher/ui/pages/modplatform/legacy_ftb/Page.cpp | 8 +++----- launcher/ui/pages/modplatform/technic/TechnicModel.cpp | 7 ++++--- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp index b3b18e2ca..e54a487c6 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp +++ b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp @@ -76,7 +76,7 @@ void ImportFTBPage::suggestCurrent() } dialog->setSuggestedPack(selected.name, new PackInstallTask(selected)); - QString editedLogoName = QString("ftb_%1.jpg").arg(selected.id); + QString editedLogoName = QString("ftb_%1_%2,jpg").arg(selected.name, selected.id); dialog->setSuggestedIconFromFile(FS::PathCombine(selected.path, "folder.jpg"), editedLogoName); } diff --git a/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp b/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp index 243c90dcd..9ba70da05 100644 --- a/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp +++ b/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp @@ -173,11 +173,9 @@ void Page::suggestCurrent() } dialog->setSuggestedPack(selected.name, selectedVersion, new PackInstallTask(APPLICATION->network(), selected, selectedVersion)); - QString editedLogoName; - if (selected.logo.toLower().startsWith("ftb")) { - editedLogoName = selected.logo; - } else { - editedLogoName = "ftb_" + selected.logo; + QString editedLogoName = selected.logo; + if (!selected.logo.toLower().startsWith("ftb")) { + editedLogoName = "ftb_" + editedLogoName; } if (selected.type == PackType::Public) { diff --git a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp index 6fbfd7624..9675534e1 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp +++ b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp @@ -34,14 +34,15 @@ */ #include "TechnicModel.h" -#include #include "Application.h" #include "BuildConfig.h" #include "Json.h" #include "net/ApiDownload.h" +#include #include +#include Technic::ListModel::ListModel(QObject* parent) : QAbstractListModel(parent) {} @@ -160,7 +161,7 @@ void Technic::ListModel::searchRequestFinished() pack.logoName = "null"; } else { pack.logoUrl = rawURL; - pack.logoName = QUrl(rawURL).fileName(); + pack.logoName = pack.slug + "." + QFileInfo(QUrl(rawURL).fileName()).suffix(); } pack.broken = false; newList.append(pack); @@ -182,7 +183,7 @@ void Technic::ListModel::searchRequestFinished() auto iconUrl = Json::requireString(iconObj, "url"); pack.logoUrl = iconUrl; - pack.logoName = QUrl(iconUrl).fileName(); + pack.logoName = pack.slug + "." + QFileInfo(QUrl(iconUrl).fileName()).suffix(); } else { pack.logoUrl = "null"; pack.logoName = "null"; From 5f3e9672cd378b8f04afe469fda4e7d4b7fc64fc Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 24 Aug 2023 13:31:57 +0300 Subject: [PATCH 181/268] made the loaders check more generic Signed-off-by: Trial97 --- launcher/modplatform/flame/FileResolvingTask.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/launcher/modplatform/flame/FileResolvingTask.cpp b/launcher/modplatform/flame/FileResolvingTask.cpp index 463014b6e..259d41cc1 100644 --- a/launcher/modplatform/flame/FileResolvingTask.cpp +++ b/launcher/modplatform/flame/FileResolvingTask.cpp @@ -1,7 +1,8 @@ #include "FileResolvingTask.h" -#include +#include #include "Json.h" +#include "modplatform/ModIndex.h" #include "net/ApiDownload.h" #include "net/ApiUpload.h" #include "net/Upload.h" @@ -135,6 +136,11 @@ void Flame::FileResolvingTask::netJobFinished() m_checkJob->start(); } +constexpr bool has_single_bit(int x) noexcept +{ + return x && !(x & (x - 1)); +} + void Flame::FileResolvingTask::modrinthCheckFinished() { setProgress(2, 3); @@ -154,7 +160,7 @@ void Flame::FileResolvingTask::modrinthCheckFinished() // If there's more than one mod loader for this version, we can't know for sure // which file is relative to each loader, so it's best to not use any one and // let the user download it manually. - if (std::bitset<8>(file.loaders).count() <= 1) { + if (!file.loaders || has_single_bit(file.loaders)) { out->url = file.downloadUrl; qDebug() << "Found alternative on modrinth " << out->fileName; } else { From 172680abf89e3d6312f48f0e1a6949ed4fc260be Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 24 Aug 2023 13:42:36 +0300 Subject: [PATCH 182/268] removed aditional header Signed-off-by: Trial97 --- launcher/modplatform/flame/FileResolvingTask.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/launcher/modplatform/flame/FileResolvingTask.cpp b/launcher/modplatform/flame/FileResolvingTask.cpp index 259d41cc1..6fc3a21c5 100644 --- a/launcher/modplatform/flame/FileResolvingTask.cpp +++ b/launcher/modplatform/flame/FileResolvingTask.cpp @@ -1,6 +1,5 @@ #include "FileResolvingTask.h" -#include #include "Json.h" #include "modplatform/ModIndex.h" #include "net/ApiDownload.h" From c54fecf5d9dd21dbc2b55f9a7d721f61fde034b3 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 25 Aug 2023 09:05:04 +0300 Subject: [PATCH 183/268] added condition for empty loader type Signed-off-by: Trial97 --- launcher/modplatform/flame/FlameAPI.cpp | 2 +- launcher/modplatform/flame/FlameModIndex.cpp | 4 ++-- launcher/modplatform/modrinth/ModrinthPackIndex.cpp | 4 ++-- launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp | 3 ++- .../ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/launcher/modplatform/flame/FlameAPI.cpp b/launcher/modplatform/flame/FlameAPI.cpp index 8d01f779d..e99ce3a56 100644 --- a/launcher/modplatform/flame/FlameAPI.cpp +++ b/launcher/modplatform/flame/FlameAPI.cpp @@ -133,7 +133,7 @@ auto FlameAPI::getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::Indexe for (auto file : arr) { auto file_obj = Json::requireObject(file); auto file_tmp = FlameMod::loadIndexedPackVersion(file_obj); - if (file_tmp.date > ver.date && (!args.loaders.has_value() || args.loaders.value() & file_tmp.loaders)) + if (file_tmp.date > ver.date && (!args.loaders.has_value() || !file_tmp.loaders || args.loaders.value() & file_tmp.loaders)) ver = file_tmp; } diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index 67e3468f9..a63bdc996 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -91,7 +91,7 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, file.addonId = pack.addonId; if (file.fileId.isValid() && - (!loaders.has_value() || loaders.value() & file.loaders)) // Heuristic to check if the returned value is valid + (!loaders.has_value() || !file.loaders || loaders.value() & file.loaders)) // Heuristic to check if the returned value is valid unsortedVersions.append(file); } @@ -202,7 +202,7 @@ ModPlatform::IndexedVersion FlameMod::loadDependencyVersions(const ModPlatform:: file.addonId = m.addonId; if (file.fileId.isValid() && - (!loaders.has_value() || loaders.value() & file.loaders)) // Heuristic to check if the returned value is valid + (!loaders.has_value() || !file.loaders || loaders.value() & file.loaders)) // Heuristic to check if the returned value is valid versions.append(file); } diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index d857e82a8..107b99006 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -105,7 +105,7 @@ void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArra auto file = loadIndexedPackVersion(obj); if (file.fileId.isValid() && - (!loaders.has_value() || loaders.value() & file.loaders)) // Heuristic to check if the returned value is valid + (!loaders.has_value() || !file.loaders || loaders.value() & file.loaders)) // Heuristic to check if the returned value is valid unsortedVersions.append(file); } auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool { @@ -242,7 +242,7 @@ auto Modrinth::loadDependencyVersions([[maybe_unused]] const ModPlatform::Depend auto file = loadIndexedPackVersion(obj); if (file.fileId.isValid() && - (!loaders.has_value() || loaders.value() & file.loaders)) // Heuristic to check if the returned value is valid + (!loaders.has_value() || !file.loaders || loaders.value() & file.loaders)) // Heuristic to check if the returned value is valid versions.append(file); } auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool { diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp index 222ceedc6..1403e98f9 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp @@ -70,7 +70,8 @@ auto FlameModPage::validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, std::optional loaders) const -> bool { - return ver.mcVersion.contains(mineVer) && !ver.downloadUrl.isEmpty() && (!loaders.has_value() || loaders.value() & ver.loaders); + return ver.mcVersion.contains(mineVer) && !ver.downloadUrl.isEmpty() && + (!loaders.has_value() || !ver.loaders || loaders.value() & ver.loaders); } bool FlameModPage::optedOut(ModPlatform::IndexedVersion& ver) const diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp index bcd0a4cf4..a4197b225 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp @@ -67,7 +67,7 @@ auto ModrinthModPage::validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, std::optional loaders) const -> bool { - return ver.mcVersion.contains(mineVer) && (!loaders.has_value() || loaders.value() & ver.loaders); + return ver.mcVersion.contains(mineVer) && (!loaders.has_value() || !ver.loaders || loaders.value() & ver.loaders); } ModrinthResourcePackPage::ModrinthResourcePackPage(ResourcePackDownloadDialog* dialog, BaseInstance& instance) From abfd1a42053eb6393394fee1ac23a2b608499d47 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 25 Aug 2023 18:35:25 +0300 Subject: [PATCH 184/268] minor dependency crash fix Signed-off-by: Trial97 --- launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp | 1 + launcher/modplatform/flame/FlameModIndex.cpp | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp index b273d70d7..ee84b1f5e 100644 --- a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp +++ b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp @@ -201,6 +201,7 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen return; } } + removePack(dep.addonId); qWarning() << "Error while reading mod version empty "; qDebug() << doc; return; diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index a63bdc996..494dc2a7e 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -211,5 +211,7 @@ ModPlatform::IndexedVersion FlameMod::loadDependencyVersions(const ModPlatform:: return a.date > b.date; }; std::sort(versions.begin(), versions.end(), orderSortPredicate); - return versions.front(); + if (versions.size() != 0) + return versions.front(); + return {}; } From 288d0d1fd4562e4020c964955918a5681b547705 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 26 Aug 2023 22:26:01 +0300 Subject: [PATCH 185/268] Added back api loader filtering if just one is selected Signed-off-by: Trial97 --- launcher/modplatform/ModIndex.h | 6 ++++++ launcher/modplatform/flame/FileResolvingTask.cpp | 7 +------ launcher/modplatform/flame/FlameAPI.h | 13 ++++++++++++- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index 780f68b4d..7d144176d 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -152,6 +152,12 @@ QString getMetaURL(ResourceProvider provider, QVariant projectID); auto getModLoaderString(ModLoaderType type) -> const QString; +constexpr bool hasSingleModLoaderSelected(ModLoaderTypes l) noexcept +{ + auto x = static_cast(l); + return x && !(x & (x - 1)); +} + } // namespace ModPlatform Q_DECLARE_METATYPE(ModPlatform::IndexedPack) diff --git a/launcher/modplatform/flame/FileResolvingTask.cpp b/launcher/modplatform/flame/FileResolvingTask.cpp index 6fc3a21c5..5865bee91 100644 --- a/launcher/modplatform/flame/FileResolvingTask.cpp +++ b/launcher/modplatform/flame/FileResolvingTask.cpp @@ -135,11 +135,6 @@ void Flame::FileResolvingTask::netJobFinished() m_checkJob->start(); } -constexpr bool has_single_bit(int x) noexcept -{ - return x && !(x & (x - 1)); -} - void Flame::FileResolvingTask::modrinthCheckFinished() { setProgress(2, 3); @@ -159,7 +154,7 @@ void Flame::FileResolvingTask::modrinthCheckFinished() // If there's more than one mod loader for this version, we can't know for sure // which file is relative to each loader, so it's best to not use any one and // let the user download it manually. - if (!file.loaders || has_single_bit(file.loaders)) { + if (!file.loaders || hasSingleModLoaderSelected(file.loaders)) { out->url = file.downloadUrl; qDebug() << "Found alternative on modrinth " << out->fileName; } else { diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h index 745f889d5..47350c33e 100644 --- a/launcher/modplatform/flame/FlameAPI.h +++ b/launcher/modplatform/flame/FlameAPI.h @@ -111,12 +111,23 @@ class FlameAPI : public NetworkResourceAPI { if (args.mcVersions.has_value()) url += QString("&gameVersion=%1").arg(args.mcVersions.value().front().toString()); + + if (args.loaders.has_value() && ModPlatform::hasSingleModLoaderSelected(args.loaders.value())) { + int mappedModLoader = getMappedModLoader(static_cast(static_cast(args.loaders.value()))); + url += QString("&modLoaderType=%1").arg(mappedModLoader); + } return url; }; [[nodiscard]] std::optional getDependencyURL(DependencySearchArgs const& args) const override { auto addonId = args.dependency.addonId.toString(); - return QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&gameVersion=%2").arg(addonId, args.mcVersion.toString()); + auto url = + QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&gameVersion=%2").arg(addonId, args.mcVersion.toString()); + if (args.loader && ModPlatform::hasSingleModLoaderSelected(args.loader)) { + int mappedModLoader = getMappedModLoader(static_cast(static_cast(args.loader))); + url += QString("&modLoaderType=%1").arg(mappedModLoader); + } + return url; }; }; From a2d44744fe71af431420f93b178a074a48b9232d Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 26 Aug 2023 23:36:46 +0300 Subject: [PATCH 186/268] do not update the metadata if mod is invalid Signed-off-by: Trial97 --- launcher/modplatform/packwiz/Packwiz.cpp | 41 +++++++++++++++--------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 381476697..1757da3e0 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -156,29 +156,38 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod) FS::ensureFilePathExists(index_file.fileName()); } + toml::table update; + switch (mod.provider) { + case (ModPlatform::ResourceProvider::FLAME): + if (mod.file_id.toInt() == 0 || mod.project_id.toInt() == 0) { + qCritical() << QString("Did not write file %1 because missing information!").arg(normalized_fname); + return; + } + update = toml::table{ + { "file-id", mod.file_id.toInt() }, + { "project-id", mod.project_id.toInt() }, + }; + break; + case (ModPlatform::ResourceProvider::MODRINTH): + if (mod.mod_id().toString().isEmpty() || mod.version().toString().isEmpty()) { + qCritical() << QString("Did not write file %1 because missing information!").arg(normalized_fname); + return; + } + update = toml::table{ + { "mod-id", mod.mod_id().toString().toStdString() }, + { "version", mod.version().toString().toStdString() }, + }; + break; + } + if (!index_file.open(QIODevice::ReadWrite)) { - qCritical() << QString("Could not open file %1!").arg(indexFileName(mod.name)); + qCritical() << QString("Could not open file %1!").arg(normalized_fname); return; } // Put TOML data into the file QTextStream in_stream(&index_file); { - toml::table update; - switch (mod.provider) { - case (ModPlatform::ResourceProvider::FLAME): - update = toml::table{ - { "file-id", mod.file_id.toInt() }, - { "project-id", mod.project_id.toInt() }, - }; - break; - case (ModPlatform::ResourceProvider::MODRINTH): - update = toml::table{ - { "mod-id", mod.mod_id().toString().toStdString() }, - { "version", mod.version().toString().toStdString() }, - }; - break; - } auto tbl = toml::table{ { "name", mod.name.toStdString() }, { "filename", mod.filename.toStdString() }, { "side", mod.side.toStdString() }, From 479335dfe08466ce77f769e550c7f68a10b522a5 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sun, 27 Aug 2023 00:49:21 +0100 Subject: [PATCH 187/268] Rewrite optional mod dialog Signed-off-by: TheKodeToad --- .../pages/modplatform/OptionalModDialog.cpp | 146 ++++------------- .../ui/pages/modplatform/OptionalModDialog.h | 41 +---- .../ui/pages/modplatform/OptionalModDialog.ui | 148 +++++++++--------- 3 files changed, 113 insertions(+), 222 deletions(-) diff --git a/launcher/ui/pages/modplatform/OptionalModDialog.cpp b/launcher/ui/pages/modplatform/OptionalModDialog.cpp index 620de4f74..fc1c8b3cb 100644 --- a/launcher/ui/pages/modplatform/OptionalModDialog.cpp +++ b/launcher/ui/pages/modplatform/OptionalModDialog.cpp @@ -19,125 +19,45 @@ #include "OptionalModDialog.h" #include "ui_OptionalModDialog.h" -OptionalModListModel::OptionalModListModel(QWidget* parent, QStringList mods) : QAbstractListModel(parent), m_mods(mods) {} - -QStringList OptionalModListModel::getResult() -{ - QStringList result; - for (const auto& mod : m_mods) { - if (m_selected.value(mod, false)) { - result << mod; - } - } - return result; -} - -int OptionalModListModel::rowCount(const QModelIndex& parent) const -{ - return parent.isValid() ? 0 : m_mods.size(); -} - -int OptionalModListModel::columnCount(const QModelIndex& parent) const -{ - // Enabled, Name - return parent.isValid() ? 0 : 2; -} - -QVariant OptionalModListModel::data(const QModelIndex& index, int role) const -{ - auto row = index.row(); - auto mod = m_mods.at(row); - - if (role == Qt::DisplayRole && index.column() == NameColumn) { - return mod; - } else if (role == Qt::CheckStateRole && index.column() == EnabledColumn) { - return m_selected.value(mod, false) ? Qt::Checked : Qt::Unchecked; - } - - return {}; -} - -bool OptionalModListModel::setData(const QModelIndex& index, [[maybe_unused]] const QVariant& value, int role) -{ - if (role == Qt::CheckStateRole) { - auto row = index.row(); - auto mod = m_mods.at(row); - - toggleMod(mod, row); - return true; - } - - return false; -} - -QVariant OptionalModListModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { - switch (section) { - case EnabledColumn: - return QString(); - case NameColumn: - return QString("Name"); - } - } - - return {}; -} - -Qt::ItemFlags OptionalModListModel::flags(const QModelIndex& index) const -{ - auto flags = QAbstractListModel::flags(index); - if (index.isValid() && index.column() == EnabledColumn) { - flags |= Qt::ItemIsUserCheckable; - } - return flags; -} - -void OptionalModListModel::toggleAll(bool enabled) -{ - for (const auto& mod : m_mods) { - m_selected[mod] = enabled; - } - - emit dataChanged(OptionalModListModel::index(0, EnabledColumn), OptionalModListModel::index(m_mods.size() - 1, EnabledColumn)); -} - -void OptionalModListModel::toggleMod(QString mod, int index) -{ - auto enable = !m_selected.value(mod, false); - - setMod(mod, index, enable); -} - -void OptionalModListModel::setMod(QString mod, int index, bool enable, bool shouldEmit) -{ - if (m_selected.value(mod, false) == enable) - return; - - m_selected[mod] = enable; - - if (shouldEmit) { - emit dataChanged(OptionalModListModel::index(index, EnabledColumn), OptionalModListModel::index(index, EnabledColumn)); - } -} - -OptionalModDialog::OptionalModDialog(QWidget* parent, QStringList mods) : QDialog(parent), ui(new Ui::OptionalModDialog) +OptionalModDialog::OptionalModDialog(QWidget* parent, const QStringList& mods) : QDialog(parent), ui(new Ui::OptionalModDialog) { ui->setupUi(this); + for (const QString& mod : mods) { + auto item = new QListWidgetItem(mod, ui->list); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + item->setCheckState(Qt::Unchecked); + item->setData(Qt::UserRole, mod); + } - listModel = new OptionalModListModel(this, mods); - ui->treeView->setModel(listModel); - - ui->treeView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - ui->treeView->header()->setSectionResizeMode(OptionalModListModel::NameColumn, QHeaderView::Stretch); - - connect(ui->selectAllButton, &QPushButton::clicked, listModel, &OptionalModListModel::selectAll); - connect(ui->clearAllButton, &QPushButton::clicked, listModel, &OptionalModListModel::clearAll); - connect(ui->installButton, &QPushButton::clicked, this, &QDialog::accept); - connect(ui->cancelButton, &QPushButton::clicked, this, &QDialog::reject); + connect(ui->selectAllButton, &QPushButton::clicked, ui->list, [this] { + for (int i = 0; i < ui->list->count(); i++) + ui->list->item(i)->setCheckState(Qt::Checked); + }); + connect(ui->clearAllButton, &QPushButton::clicked, ui->list, [this] { + for (int i = 0; i < ui->list->count(); i++) + ui->list->item(i)->setCheckState(Qt::Unchecked); + }); + connect(ui->list, &QListWidget::itemActivated, [](QListWidgetItem* item) { + if (item->checkState() == Qt::Checked) + item->setCheckState(Qt::Unchecked); + else + item->setCheckState(Qt::Checked); + }); } OptionalModDialog::~OptionalModDialog() { delete ui; } + +QStringList OptionalModDialog::getResult() +{ + QStringList result; + result.reserve(ui->list->count()); + for (int i = 0; i < ui->list->count(); i++) { + auto item = ui->list->item(i); + if (item->checkState() == Qt::Checked) + result.append(item->data(Qt::UserRole).toString()); + } + return result; +} diff --git a/launcher/ui/pages/modplatform/OptionalModDialog.h b/launcher/ui/pages/modplatform/OptionalModDialog.h index 4e22d0e46..1897c1fca 100644 --- a/launcher/ui/pages/modplatform/OptionalModDialog.h +++ b/launcher/ui/pages/modplatform/OptionalModDialog.h @@ -25,52 +25,15 @@ namespace Ui { class OptionalModDialog; } -class OptionalModListModel : public QAbstractListModel { - Q_OBJECT - public: - enum Columns { - EnabledColumn = 0, - NameColumn, - }; - - OptionalModListModel(QWidget* parent, QStringList mods); - - QStringList getResult(); - - int rowCount(const QModelIndex& parent) const override; - int columnCount(const QModelIndex& parent) const override; - - QVariant data(const QModelIndex& index, int role) const override; - bool setData(const QModelIndex& index, const QVariant& value, int role) override; - QVariant headerData(int section, Qt::Orientation orientation, int role) const override; - - Qt::ItemFlags flags(const QModelIndex& index) const override; - - public slots: - void selectAll() { toggleAll(true); } - void clearAll() { toggleAll(false); }; - void toggleAll(bool enabled); - - private: - void toggleMod(QString mod, int index); - void setMod(QString mod, int index, bool enable, bool shouldEmit = true); - - private: - QStringList m_mods; - QHash m_selected; -}; - class OptionalModDialog : public QDialog { Q_OBJECT public: - OptionalModDialog(QWidget* parent, QStringList mods); + OptionalModDialog(QWidget* parent, const QStringList& mods); ~OptionalModDialog() override; - QStringList getResult() { return listModel->getResult(); } + QStringList getResult(); private: Ui::OptionalModDialog* ui; - - OptionalModListModel* listModel; }; diff --git a/launcher/ui/pages/modplatform/OptionalModDialog.ui b/launcher/ui/pages/modplatform/OptionalModDialog.ui index 76d1da89d..0b809d2cb 100644 --- a/launcher/ui/pages/modplatform/OptionalModDialog.ui +++ b/launcher/ui/pages/modplatform/OptionalModDialog.ui @@ -11,81 +11,63 @@
- Select Mods To Install + Select Optional Mods - - - - 11 - 75 - true - + + + Qt::IgnoreAction - - Select optional mods to install. - - - Qt::AlignCenter - - - - - - - - true - - - - Note: All files will be downloaded but the unselected mods will be disabled. - - - Qt::AlignCenter - - - - - - - - - - - - - + true - - Cancel - - - - Clear All - - + + + + + Select All + + + + + + + Deselect All + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Unchecked mods will be disabled. + + + + - - - Select All - - - - - - - Install - - - true + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok @@ -93,13 +75,39 @@
- - - ModListView - QTreeView -
ui/widgets/ModListView.h
-
-
- + + + buttonBox + accepted() + OptionalModDialog + accept() + + + 274 + 284 + + + 274 + 154 + + + + + buttonBox + rejected() + OptionalModDialog + reject() + + + 274 + 284 + + + 274 + 154 + + + + From 9735e46a7eb4aa02eec5e08dbf784005d43d37d5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 27 Aug 2023 00:17:22 +0000 Subject: [PATCH 188/268] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:nixos/nixpkgs/ca3c9ac9f4cdd4bea19f592b32bb59b74ab7d783' (2023-08-19) → 'github:nixos/nixpkgs/c66ccfa00c643751da2fd9290e096ceaa30493fc' (2023-08-26) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 28ef60f30..5599efe16 100644 --- a/flake.lock +++ b/flake.lock @@ -91,11 +91,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1692463654, - "narHash": "sha256-F8hZmsQINI+S6UROM4jyxAMbQLtzE44pI8Nk6NtMdao=", + "lastModified": 1693060755, + "narHash": "sha256-KNsbfqewEziFJEpPR0qvVz4rx0x6QXxw1CcunRhlFdk=", "owner": "nixos", "repo": "nixpkgs", - "rev": "ca3c9ac9f4cdd4bea19f592b32bb59b74ab7d783", + "rev": "c66ccfa00c643751da2fd9290e096ceaa30493fc", "type": "github" }, "original": { From 4036cecfc02142c403a45e18474ad7aab6fc8834 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 27 Aug 2023 15:04:42 +0300 Subject: [PATCH 189/268] Added progress widget to some modpack providers Signed-off-by: Trial97 --- launcher/ui/pages/modplatform/flame/FlameModel.h | 1 + launcher/ui/pages/modplatform/flame/FlamePage.cpp | 10 +++++++++- launcher/ui/pages/modplatform/flame/FlamePage.h | 3 +++ launcher/ui/pages/modplatform/flame/FlamePage.ui | 4 ++-- launcher/ui/pages/modplatform/modrinth/ModrinthModel.h | 1 + .../ui/pages/modplatform/modrinth/ModrinthPage.cpp | 10 +++++++++- launcher/ui/pages/modplatform/modrinth/ModrinthPage.h | 3 +++ launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui | 10 +++++----- launcher/ui/pages/modplatform/technic/TechnicModel.h | 1 + launcher/ui/pages/modplatform/technic/TechnicPage.cpp | 10 +++++++++- launcher/ui/pages/modplatform/technic/TechnicPage.h | 3 +++ launcher/ui/pages/modplatform/technic/TechnicPage.ui | 4 ++-- 12 files changed, 48 insertions(+), 12 deletions(-) diff --git a/launcher/ui/pages/modplatform/flame/FlameModel.h b/launcher/ui/pages/modplatform/flame/FlameModel.h index cd73fce30..fd8496dfb 100644 --- a/launcher/ui/pages/modplatform/flame/FlameModel.h +++ b/launcher/ui/pages/modplatform/flame/FlameModel.h @@ -41,6 +41,7 @@ class ListModel : public QAbstractListModel { void searchWithTerm(const QString& term, const int sort); [[nodiscard]] bool hasActiveSearchJob() const { return jobPtr && jobPtr->isRunning(); } + [[nodiscard]] Task::Ptr activeSearchJob() { return hasActiveSearchJob() ? jobPtr : nullptr; } private slots: void performPaginatedSearch(); diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.cpp b/launcher/ui/pages/modplatform/flame/FlamePage.cpp index 79fcc8211..50656f427 100644 --- a/launcher/ui/pages/modplatform/flame/FlamePage.cpp +++ b/launcher/ui/pages/modplatform/flame/FlamePage.cpp @@ -50,7 +50,8 @@ static FlameAPI api; -FlamePage::FlamePage(NewInstanceDialog* dialog, QWidget* parent) : QWidget(parent), ui(new Ui::FlamePage), dialog(dialog) +FlamePage::FlamePage(NewInstanceDialog* dialog, QWidget* parent) + : QWidget(parent), ui(new Ui::FlamePage), dialog(dialog), m_fetch_progress(this, false) { ui->setupUi(this); connect(ui->searchButton, &QPushButton::clicked, this, &FlamePage::triggerSearch); @@ -66,6 +67,12 @@ FlamePage::FlamePage(NewInstanceDialog* dialog, QWidget* parent) : QWidget(paren connect(&m_search_timer, &QTimer::timeout, this, &FlamePage::triggerSearch); + m_fetch_progress.hideIfInactive(true); + m_fetch_progress.setFixedHeight(24); + m_fetch_progress.progressFormat(""); + + ui->gridLayout->addWidget(&m_fetch_progress, 2, 0, 1, ui->gridLayout->columnCount()); + // index is used to set the sorting with the curseforge api ui->sortByBox->addItem(tr("Sort by Featured")); ui->sortByBox->addItem(tr("Sort by Popularity")); @@ -124,6 +131,7 @@ void FlamePage::openedImpl() void FlamePage::triggerSearch() { listModel->searchWithTerm(ui->searchEdit->text(), ui->sortByBox->currentIndex()); + m_fetch_progress.watch(listModel->activeSearchJob().get()); } void FlamePage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelIndex prev) diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.h b/launcher/ui/pages/modplatform/flame/FlamePage.h index a45c9e404..d35858fbc 100644 --- a/launcher/ui/pages/modplatform/flame/FlamePage.h +++ b/launcher/ui/pages/modplatform/flame/FlamePage.h @@ -41,6 +41,7 @@ #include #include #include "ui/pages/BasePage.h" +#include "ui/widgets/ProgressWidget.h" namespace Ui { class FlamePage; @@ -87,6 +88,8 @@ class FlamePage : public QWidget, public BasePage { int m_selected_version_index = -1; + ProgressWidget m_fetch_progress; + // Used to do instant searching with a delay to cache quick changes QTimer m_search_timer; }; diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.ui b/launcher/ui/pages/modplatform/flame/FlamePage.ui index 71d195135..f9e1fe67f 100644 --- a/launcher/ui/pages/modplatform/flame/FlamePage.ui +++ b/launcher/ui/pages/modplatform/flame/FlamePage.ui @@ -47,7 +47,7 @@
- + @@ -77,7 +77,7 @@ - + diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h index f5e686a80..2a9d62261 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h @@ -74,6 +74,7 @@ class ModpackListModel : public QAbstractListModel { void searchWithTerm(const QString& term, const int sort); [[nodiscard]] bool hasActiveSearchJob() const { return jobPtr && jobPtr->isRunning(); } + [[nodiscard]] Task::Ptr activeSearchJob() { return hasActiveSearchJob() ? jobPtr : nullptr; } void getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback); diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index 72c9da358..f7fa8fd78 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -52,7 +52,8 @@ #include #include -ModrinthPage::ModrinthPage(NewInstanceDialog* dialog, QWidget* parent) : QWidget(parent), ui(new Ui::ModrinthPage), dialog(dialog) +ModrinthPage::ModrinthPage(NewInstanceDialog* dialog, QWidget* parent) + : QWidget(parent), ui(new Ui::ModrinthPage), dialog(dialog), m_fetch_progress(this, false) { ui->setupUi(this); @@ -69,6 +70,12 @@ ModrinthPage::ModrinthPage(NewInstanceDialog* dialog, QWidget* parent) : QWidget connect(&m_search_timer, &QTimer::timeout, this, &ModrinthPage::triggerSearch); + m_fetch_progress.hideIfInactive(true); + m_fetch_progress.setFixedHeight(24); + m_fetch_progress.progressFormat(""); + + ui->gridLayout->addWidget(&m_fetch_progress, 2, 0, 1, ui->gridLayout->columnCount()); + ui->sortByBox->addItem(tr("Sort by Relevance")); ui->sortByBox->addItem(tr("Sort by Total Downloads")); ui->sortByBox->addItem(tr("Sort by Follows")); @@ -319,6 +326,7 @@ void ModrinthPage::suggestCurrent() void ModrinthPage::triggerSearch() { m_model->searchWithTerm(ui->searchEdit->text(), ui->sortByBox->currentIndex()); + m_fetch_progress.watch(m_model->activeSearchJob().get()); } void ModrinthPage::onVersionSelectionChanged(QString version) diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h index 0705ca99b..4240dcafb 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.h @@ -41,6 +41,7 @@ #include "ui/pages/BasePage.h" #include "modplatform/modrinth/ModrinthPackManifest.h" +#include "ui/widgets/ProgressWidget.h" #include #include @@ -90,6 +91,8 @@ class ModrinthPage : public QWidget, public BasePage { Modrinth::Modpack current; QString selectedVersion; + ProgressWidget m_fetch_progress; + // Used to do instant searching with a delay to cache quick changes QTimer m_search_timer; }; diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui index 6d8b2b675..78a25feae 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui @@ -10,8 +10,8 @@ 600 - - + + @@ -29,7 +29,7 @@ - + @@ -47,7 +47,7 @@ - + @@ -77,7 +77,7 @@ - + diff --git a/launcher/ui/pages/modplatform/technic/TechnicModel.h b/launcher/ui/pages/modplatform/technic/TechnicModel.h index c0d13ae82..aeb4f3084 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicModel.h +++ b/launcher/ui/pages/modplatform/technic/TechnicModel.h @@ -59,6 +59,7 @@ class ListModel : public QAbstractListModel { void searchWithTerm(const QString& term); [[nodiscard]] bool hasActiveSearchJob() const { return jobPtr && jobPtr->isRunning(); } + [[nodiscard]] Task::Ptr activeSearchJob() { return hasActiveSearchJob() ? jobPtr : nullptr; } private slots: void searchRequestFinished(); diff --git a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp index 518d049e8..190b7c68f 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp +++ b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp @@ -52,7 +52,8 @@ #include "net/ApiDownload.h" -TechnicPage::TechnicPage(NewInstanceDialog* dialog, QWidget* parent) : QWidget(parent), ui(new Ui::TechnicPage), dialog(dialog) +TechnicPage::TechnicPage(NewInstanceDialog* dialog, QWidget* parent) + : QWidget(parent), ui(new Ui::TechnicPage), dialog(dialog), m_fetch_progress(this, false) { ui->setupUi(this); connect(ui->searchButton, &QPushButton::clicked, this, &TechnicPage::triggerSearch); @@ -65,6 +66,12 @@ TechnicPage::TechnicPage(NewInstanceDialog* dialog, QWidget* parent) : QWidget(p connect(&m_search_timer, &QTimer::timeout, this, &TechnicPage::triggerSearch); + m_fetch_progress.hideIfInactive(true); + m_fetch_progress.setFixedHeight(24); + m_fetch_progress.progressFormat(""); + + ui->gridLayout->addWidget(&m_fetch_progress, 2, 0, 1, ui->gridLayout->columnCount()); + connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &TechnicPage::onSelectionChanged); connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &TechnicPage::onVersionSelectionChanged); @@ -113,6 +120,7 @@ void TechnicPage::openedImpl() void TechnicPage::triggerSearch() { model->searchWithTerm(ui->searchEdit->text()); + m_fetch_progress.watch(model->activeSearchJob().get()); } void TechnicPage::onSelectionChanged(QModelIndex first, [[maybe_unused]] QModelIndex second) diff --git a/launcher/ui/pages/modplatform/technic/TechnicPage.h b/launcher/ui/pages/modplatform/technic/TechnicPage.h index 1e36fbd31..01439337d 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicPage.h +++ b/launcher/ui/pages/modplatform/technic/TechnicPage.h @@ -42,6 +42,7 @@ #include "TechnicData.h" #include "net/NetJob.h" #include "ui/pages/BasePage.h" +#include "ui/widgets/ProgressWidget.h" namespace Ui { class TechnicPage; @@ -92,6 +93,8 @@ class TechnicPage : public QWidget, public BasePage { NetJob::Ptr jobPtr; std::shared_ptr response = std::make_shared(); + ProgressWidget m_fetch_progress; + // Used to do instant searching with a delay to cache quick changes QTimer m_search_timer; }; diff --git a/launcher/ui/pages/modplatform/technic/TechnicPage.ui b/launcher/ui/pages/modplatform/technic/TechnicPage.ui index 15bf645fb..b988eda2b 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicPage.ui +++ b/launcher/ui/pages/modplatform/technic/TechnicPage.ui @@ -11,7 +11,7 @@ - + @@ -44,7 +44,7 @@ - + From 8c607ae7348206c29a8a8ce5e2db421138bd89ff Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 27 Aug 2023 20:02:12 +0300 Subject: [PATCH 190/268] removed extra if Signed-off-by: Trial97 --- .../ui/pages/modplatform/ResourceModel.cpp | 34 ++++++++----------- .../ui/pages/modplatform/flame/FlameModel.cpp | 8 ++--- .../modplatform/modrinth/ModrinthModel.cpp | 8 ++--- 3 files changed, 19 insertions(+), 31 deletions(-) diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp index 018721f94..cb8f1920f 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.cpp +++ b/launcher/ui/pages/modplatform/ResourceModel.cpp @@ -137,26 +137,22 @@ void ResourceModel::search() if (!projectId.isEmpty()) { ResourceAPI::ProjectInfoCallbacks callbacks; - // Use defaults if no callbacks are set - if (!callbacks.on_fail) - callbacks.on_fail = [this](QString reason) { - if (!s_running_models.constFind(this).value()) - return; - searchRequestFailed(reason, -1); - }; - if (!callbacks.on_abort) - callbacks.on_abort = [this] { - if (!s_running_models.constFind(this).value()) - return; - searchRequestAborted(); - }; + callbacks.on_fail = [this](QString reason) { + if (!s_running_models.constFind(this).value()) + return; + searchRequestFailed(reason, -1); + }; + callbacks.on_abort = [this] { + if (!s_running_models.constFind(this).value()) + return; + searchRequestAborted(); + }; - if (!callbacks.on_succeed) - callbacks.on_succeed = [this](auto& doc, auto& pack) { - if (!s_running_models.constFind(this).value()) - return; - searchRequestForOneSucceeded(doc); - }; + callbacks.on_succeed = [this](auto& doc, auto& pack) { + if (!s_running_models.constFind(this).value()) + return; + searchRequestForOneSucceeded(doc); + }; if (auto job = m_api->getProjectInfo({ projectId }, std::move(callbacks)); job) runSearchJob(job); return; diff --git a/launcher/ui/pages/modplatform/flame/FlameModel.cpp b/launcher/ui/pages/modplatform/flame/FlameModel.cpp index 17875a604..8875a9452 100644 --- a/launcher/ui/pages/modplatform/flame/FlameModel.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameModel.cpp @@ -168,12 +168,8 @@ void ListModel::performPaginatedSearch() if (!projectId.isEmpty()) { ResourceAPI::ProjectInfoCallbacks callbacks; - // Use defaults if no callbacks are set - if (!callbacks.on_fail) - callbacks.on_fail = [this](QString reason) { searchRequestFailed(reason); }; - - if (!callbacks.on_succeed) - callbacks.on_succeed = [this](auto& doc, auto& pack) { searchRequestForOneSucceeded(doc); }; + callbacks.on_fail = [this](QString reason) { searchRequestFailed(reason); }; + callbacks.on_succeed = [this](auto& doc, auto& pack) { searchRequestForOneSucceeded(doc); }; static const FlameAPI api; if (auto job = api.getProjectInfo({ projectId }, std::move(callbacks)); job) { jobPtr = job; diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp index efb1fe44e..f691a185d 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -138,12 +138,8 @@ void ModpackListModel::performPaginatedSearch() if (!projectId.isEmpty()) { ResourceAPI::ProjectInfoCallbacks callbacks; - // Use defaults if no callbacks are set - if (!callbacks.on_fail) - callbacks.on_fail = [this](QString reason) { searchRequestFailed(reason); }; - - if (!callbacks.on_succeed) - callbacks.on_succeed = [this](auto& doc, auto& pack) { searchRequestForOneSucceeded(doc); }; + callbacks.on_fail = [this](QString reason) { searchRequestFailed(reason); }; + callbacks.on_succeed = [this](auto& doc, auto& pack) { searchRequestForOneSucceeded(doc); }; static const ModrinthAPI api; if (auto job = api.getProjectInfo({ projectId }, std::move(callbacks)); job) { jobPtr = job; From 07d8598638247d8dfb2fcd2e38c4498e90570f6e Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 27 Aug 2023 23:40:32 +0300 Subject: [PATCH 191/268] added catpacks tests Signed-off-by: Trial97 --- launcher/ui/themes/CatPack.cpp | 10 +++-- launcher/ui/themes/CatPack.h | 9 ++-- tests/CMakeLists.txt | 3 ++ tests/CatPack_test.cpp | 77 ++++++++++++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 7 deletions(-) create mode 100644 tests/CatPack_test.cpp diff --git a/launcher/ui/themes/CatPack.cpp b/launcher/ui/themes/CatPack.cpp index f0d8ddd55..bbcb58bc8 100644 --- a/launcher/ui/themes/CatPack.cpp +++ b/launcher/ui/themes/CatPack.cpp @@ -99,18 +99,22 @@ QDate ensureDay(int year, int month, int day) QString JsonCatPack::path() { - const QDate now = QDate::currentDate(); + return path(QDate::currentDate()); +} + +QString JsonCatPack::path(QDate now) +{ for (auto var : m_variants) { QDate startDate = ensureDay(now.year(), var.startTime.month, var.startTime.day); QDate endDate = ensureDay(now.year(), var.endTime.month, var.endTime.day); if (startDate > endDate) { // it's spans over multiple years - if (endDate <= now) // end date is in the past so jump one year into the future for endDate + if (endDate < now) // end date is in the past so jump one year into the future for endDate endDate = endDate.addYears(1); else // end date is in the future so jump one year into the past for startDate startDate = startDate.addYears(-1); } - if (startDate >= now && now >= endDate) + if (startDate <= now && now <= endDate) return var.path; } return m_defaultPath; diff --git a/launcher/ui/themes/CatPack.h b/launcher/ui/themes/CatPack.h index fdd117a7f..1d310e796 100644 --- a/launcher/ui/themes/CatPack.h +++ b/launcher/ui/themes/CatPack.h @@ -52,9 +52,9 @@ class BasicCatPack : public CatPack { public: BasicCatPack(QString id, QString name) : m_id(id), m_name(name) {} BasicCatPack(QString id) : BasicCatPack(id, id) {} - virtual QString id() { return m_id; } - virtual QString name() { return m_name; } - virtual QString path(); + virtual QString id() override { return m_id; } + virtual QString name() override { return m_name; } + virtual QString path() override; protected: QString m_id; @@ -83,7 +83,8 @@ class JsonCatPack : public BasicCatPack { PartialDate endTime; }; JsonCatPack(QFileInfo& manifestInfo); - virtual QString path(); + virtual QString path() override; + QString path(QDate now); private: QString m_defaultPath; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a26a49fec..1a49fd700 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -56,3 +56,6 @@ ecm_add_test(Index_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}: ecm_add_test(Version_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test TEST_NAME Version) + +ecm_add_test(CatPack_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test + TEST_NAME CatPack) \ No newline at end of file diff --git a/tests/CatPack_test.cpp b/tests/CatPack_test.cpp new file mode 100644 index 000000000..a84ba6511 --- /dev/null +++ b/tests/CatPack_test.cpp @@ -0,0 +1,77 @@ +#include + +#include +#include +#include +#include +#include "FileSystem.h" +#include "ui/themes/CatPack.h" + +class CatPackTest : public QObject { + Q_OBJECT + private slots: + void test_catPack() + { + QString fileContent = R"({ + "name": "My Cute Cat", + "default": "maxwell.png", + "variants": [ + { + "startTime": { "day": 12, "month": 4 }, + "endTime": { "day": 12, "month": 4 }, + "path": "oneDay.png" + }, + { + "startTime": { "day": 20, "month": 12 }, + "endTime": { "day": 28, "month": 12 }, + "path": "christmas.png" + }, + { + "startTime": { "day": 30, "month": 12 }, + "endTime": { "day": 1, "month": 1 }, + "path": "newyear2.png" + }, + { + "startTime": { "day": 28, "month": 12 }, + "endTime": { "day": 3, "month": 1 }, + "path": "newyear.png" + } + ] +})"; +#if defined(Q_OS_WIN) + QString fileName = "test_SaveAlreadyExistingFile.ini"; + QFile file(fileName); + QCOMPARE(file.open(QFile::WriteOnly | QFile::Text), true); +#else + QTemporaryFile file; + QCOMPARE(file.open(), true); + QCOMPARE(file.fileName().isEmpty(), false); + QString fileName = file.fileName(); +#endif + QTextStream stream(&file); + stream << fileContent; + file.close(); + auto fileinfo = QFileInfo(fileName); + try { + auto cat = JsonCatPack(fileinfo); + QCOMPARE(cat.path(QDate(2023, 4, 12)), FS::PathCombine(fileinfo.path(), "oneDay.png")); + QCOMPARE(cat.path(QDate(2023, 4, 11)), FS::PathCombine(fileinfo.path(), "maxwell.png")); + QCOMPARE(cat.path(QDate(2023, 4, 13)), FS::PathCombine(fileinfo.path(), "maxwell.png")); + QCOMPARE(cat.path(QDate(2023, 12, 21)), FS::PathCombine(fileinfo.path(), "christmas.png")); + QCOMPARE(cat.path(QDate(2023, 12, 28)), FS::PathCombine(fileinfo.path(), "christmas.png")); + QCOMPARE(cat.path(QDate(2023, 12, 29)), FS::PathCombine(fileinfo.path(), "newyear.png")); + QCOMPARE(cat.path(QDate(2023, 12, 30)), FS::PathCombine(fileinfo.path(), "newyear2.png")); + QCOMPARE(cat.path(QDate(2023, 12, 31)), FS::PathCombine(fileinfo.path(), "newyear2.png")); + QCOMPARE(cat.path(QDate(2024, 1, 1)), FS::PathCombine(fileinfo.path(), "newyear2.png")); + QCOMPARE(cat.path(QDate(2024, 1, 2)), FS::PathCombine(fileinfo.path(), "newyear.png")); + QCOMPARE(cat.path(QDate(2024, 1, 3)), FS::PathCombine(fileinfo.path(), "newyear.png")); + QCOMPARE(cat.path(QDate(2024, 1, 4)), FS::PathCombine(fileinfo.path(), "maxwell.png")); + } catch (const Exception& e) { + QFAIL(e.cause().toLatin1()); + } + } +}; + +QTEST_GUILESS_MAIN(CatPackTest) + +#include "CatPack_test.moc" From 66cbbfec0c992408e3d507496ccf73e25171891f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 00:23:14 +0000 Subject: [PATCH 192/268] chore(deps): lock file maintenance --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 5599efe16..f27ca5207 100644 --- a/flake.lock +++ b/flake.lock @@ -91,11 +91,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1693060755, - "narHash": "sha256-KNsbfqewEziFJEpPR0qvVz4rx0x6QXxw1CcunRhlFdk=", + "lastModified": 1693145325, + "narHash": "sha256-Gat9xskErH1zOcLjYMhSDBo0JTBZKfGS0xJlIRnj6Rc=", "owner": "nixos", "repo": "nixpkgs", - "rev": "c66ccfa00c643751da2fd9290e096ceaa30493fc", + "rev": "cddebdb60de376c1bdb7a4e6ee3d98355453fe56", "type": "github" }, "original": { From 5d70f4dbca4405704e03e8428a7829b6a7cc32eb Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 28 Aug 2023 08:50:10 +0300 Subject: [PATCH 193/268] removed if Signed-off-by: Trial97 --- launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp index 1be58c2b8..e732ad39c 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp @@ -394,7 +394,7 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path, for (auto file : optionalFiles) { if (selectedMods.contains(file.path)) { file.required = true; - } else if (file.path.endsWith(".jar")) { + } else { file.path += ".disabled"; } files.push_back(file); From 97d932db620b478742583cbd151f875e89999100 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Mon, 28 Aug 2023 10:13:44 +0200 Subject: [PATCH 194/268] Revert "chore(deps): enable nix lockfile maintenance for renovate" This reverts commit 5d14724e66a1911b04dd5091e520751fd7f5ee90. Signed-off-by: Sefa Eyeoglu --- renovate.json | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/renovate.json b/renovate.json index 71581e3b0..f9c2c3270 100644 --- a/renovate.json +++ b/renovate.json @@ -2,11 +2,5 @@ "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ "config:base" - ], - "nix": { - "enabled": true - }, - "lockFileMaintenance": { - "enabled": true - } + ] } From 311e36b5d6dfaed449a3bbdada3d2e5b87f2b6d9 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 28 Aug 2023 11:52:26 +0300 Subject: [PATCH 195/268] added new line Signed-off-by: Trial97 --- tests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1a49fd700..59e0e3144 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -58,4 +58,4 @@ ecm_add_test(Version_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR TEST_NAME Version) ecm_add_test(CatPack_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test - TEST_NAME CatPack) \ No newline at end of file + TEST_NAME CatPack) From bdc2fca711d3ad110d7ef2a2c1ae99537144a00e Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Mon, 28 Aug 2023 10:26:03 +0200 Subject: [PATCH 196/268] refactor(nix): don't instantiate nixpkgs See https://zimbatm.com/notes/1000-instances-of-nixpkgs Signed-off-by: Sefa Eyeoglu --- flake.nix | 14 ++++- nix/default.nix | 31 ---------- nix/distribution.nix | 36 ++++++------ nix/{package.nix => pkg/default.nix} | 0 nix/pkg/wrapper.nix | 86 ++++++++++++++++++++++++++++ 5 files changed, 118 insertions(+), 49 deletions(-) delete mode 100644 nix/default.nix rename nix/{package.nix => pkg/default.nix} (100%) create mode 100644 nix/pkg/wrapper.nix diff --git a/flake.nix b/flake.nix index c3148fe03..8f31ccec5 100644 --- a/flake.nix +++ b/flake.nix @@ -23,5 +23,17 @@ outputs = inputs: inputs.flake-parts.lib.mkFlake {inherit inputs;} - {imports = [./nix];}; + { + imports = [ + ./nix/dev.nix + ./nix/distribution.nix + ]; + + systems = [ + "x86_64-linux" + "aarch64-linux" + "x86_64-darwin" + "aarch64-darwin" + ]; + }; } diff --git a/nix/default.nix b/nix/default.nix deleted file mode 100644 index 71c95c2cf..000000000 --- a/nix/default.nix +++ /dev/null @@ -1,31 +0,0 @@ -{ - inputs, - self, - ... -}: { - imports = [ - ./dev.nix - ./distribution.nix - ]; - - _module.args = { - # User-friendly version number. - version = builtins.substring 0 8 self.lastModifiedDate; - }; - - perSystem = {system, ...}: { - # Nixpkgs instantiated for supported systems with our overlay. - _module.args.pkgs = import inputs.nixpkgs { - inherit system; - overlays = [self.overlays.default]; - }; - }; - - # Supported systems. - systems = [ - "x86_64-linux" - "aarch64-linux" - "x86_64-darwin" - "aarch64-darwin" - ]; -} diff --git a/nix/distribution.nix b/nix/distribution.nix index d0904d41d..0dde1e4f0 100644 --- a/nix/distribution.nix +++ b/nix/distribution.nix @@ -1,30 +1,32 @@ { inputs, self, - version, ... }: { perSystem = {pkgs, ...}: { - packages = { - inherit (pkgs) prismlauncher-qt5-unwrapped prismlauncher-qt5 prismlauncher-unwrapped prismlauncher; - default = pkgs.prismlauncher; + packages = let + ourPackages = self.overlays.default pkgs null; + in { + inherit (ourPackages) prismlauncher-qt5-unwrapped prismlauncher-qt5 prismlauncher-unwrapped prismlauncher; + default = ourPackages.prismlauncher; }; }; flake = { - overlays.default = final: prev: let - # Helper function to build prism against different versions of Qt. - mkPrism = qt: - qt.callPackage ./package.nix { - inherit (inputs) libnbtplusplus; - inherit (prev.darwin.apple_sdk.frameworks) Cocoa; - inherit self version; - }; - in { - prismlauncher-qt5-unwrapped = mkPrism final.libsForQt5; - prismlauncher-qt5 = prev.prismlauncher-qt5.override {prismlauncher-unwrapped = final.prismlauncher-qt5-unwrapped;}; - prismlauncher-unwrapped = mkPrism final.qt6Packages; - prismlauncher = prev.prismlauncher.override {inherit (final) prismlauncher-unwrapped;}; + overlays.default = final: _: let + version = builtins.substring 0 8 self.lastModifiedDate; + + # common args for prismlauncher evaluations + unwrappedArgs = { + inherit (inputs) libnbtplusplus; + inherit (final.darwin.apple_sdk.frameworks) Cocoa; + inherit self version; + }; + in rec { + prismlauncher-qt5-unwrapped = final.libsForQt5.callPackage ./pkg unwrappedArgs; + prismlauncher-qt5 = final.libsForQt5.callPackage ./pkg/wrapper.nix {prismlauncher-unwrapped = prismlauncher-qt5-unwrapped;}; + prismlauncher-unwrapped = final.qt6Packages.callPackage ./pkg unwrappedArgs; + prismlauncher = final.qt6Packages.callPackage ./pkg/wrapper.nix {inherit prismlauncher-unwrapped;}; }; }; } diff --git a/nix/package.nix b/nix/pkg/default.nix similarity index 100% rename from nix/package.nix rename to nix/pkg/default.nix diff --git a/nix/pkg/wrapper.nix b/nix/pkg/wrapper.nix new file mode 100644 index 000000000..3bf5b3f85 --- /dev/null +++ b/nix/pkg/wrapper.nix @@ -0,0 +1,86 @@ +{ + lib, + stdenv, + symlinkJoin, + prismlauncher-unwrapped, + wrapQtAppsHook, + qtbase, # needed for wrapQtAppsHook + qtsvg, + qtwayland, + xorg, + libpulseaudio, + libGL, + glfw, + openal, + jdk8, + jdk17, + gamemode, + flite, + mesa-demos, + msaClientID ? null, + gamemodeSupport ? stdenv.isLinux, + textToSpeechSupport ? stdenv.isLinux, + jdks ? [jdk17 jdk8], + additionalLibs ? [], + additionalPrograms ? [], +}: let + prismlauncherFinal = prismlauncher-unwrapped.override { + inherit msaClientID gamemodeSupport; + }; +in + symlinkJoin { + name = "prismlauncher-${prismlauncherFinal.version}"; + + paths = [prismlauncherFinal]; + + nativeBuildInputs = [ + wrapQtAppsHook + ]; + + buildInputs = + [ + qtbase + qtsvg + ] + ++ lib.optional (lib.versionAtLeast qtbase.version "6" && stdenv.isLinux) qtwayland; + + postBuild = '' + wrapQtAppsHook + ''; + + qtWrapperArgs = let + runtimeLibs = + (with xorg; [ + libX11 + libXext + libXcursor + libXrandr + libXxf86vm + ]) + ++ [ + libpulseaudio + libGL + glfw + openal + stdenv.cc.cc.lib + ] + ++ lib.optional gamemodeSupport gamemode.lib + ++ lib.optional textToSpeechSupport flite + ++ additionalLibs; + + runtimePrograms = + [ + xorg.xrandr + mesa-demos # need glxinfo + ] + ++ additionalPrograms; + in + ["--prefix PRISMLAUNCHER_JAVA_PATHS : ${lib.makeSearchPath "bin/java" jdks}"] + ++ lib.optionals stdenv.isLinux [ + "--set LD_LIBRARY_PATH /run/opengl-driver/lib:${lib.makeLibraryPath runtimeLibs}" + # xorg.xrandr needed for LWJGL [2.9.2, 3) https://github.com/LWJGL/lwjgl/issues/128 + "--prefix PATH : ${lib.makeBinPath runtimePrograms}" + ]; + + inherit (prismlauncherFinal) meta; + } From e98bca47493f53b324d0ac7fd9eac9386c30be23 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Mon, 28 Aug 2023 17:09:40 +0100 Subject: [PATCH 197/268] Fix cancelling pack save Signed-off-by: TheKodeToad --- launcher/ui/dialogs/ExportPackDialog.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/launcher/ui/dialogs/ExportPackDialog.cpp b/launcher/ui/dialogs/ExportPackDialog.cpp index 0a97ee13c..b0558df7d 100644 --- a/launcher/ui/dialogs/ExportPackDialog.cpp +++ b/launcher/ui/dialogs/ExportPackDialog.cpp @@ -120,18 +120,19 @@ void ExportPackDialog::done(int result) if (m_provider == ModPlatform::ResourceProvider::MODRINTH) { output = QFileDialog::getSaveFileName(this, tr("Export %1").arg(name), FS::PathCombine(QDir::homePath(), filename + ".mrpack"), "Modrinth pack (*.mrpack *.zip)", nullptr); + if (output.isEmpty()) + return; if (!(output.endsWith(".zip") || output.endsWith(".mrpack"))) output.append(".mrpack"); } else { output = QFileDialog::getSaveFileName(this, tr("Export %1").arg(name), FS::PathCombine(QDir::homePath(), filename + ".zip"), "CurseForge pack (*.zip)", nullptr); + if (output.isEmpty()) + return; if (!output.endsWith(".zip")) output.append(".zip"); } - if (output.isEmpty()) - return; - Task* task; if (m_provider == ModPlatform::ResourceProvider::MODRINTH) { task = new ModrinthPackExportTask(name, ui->version->text(), ui->summary->text(), ui->optionalFiles->isChecked(), instance, From 0e67686295e25a3dda587e4955837e6201ae23e8 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Mon, 28 Aug 2023 17:25:07 +0100 Subject: [PATCH 198/268] Hide index folder in pack export dialog Signed-off-by: TheKodeToad --- launcher/FileIgnoreProxy.cpp | 5 +---- launcher/ui/dialogs/ExportPackDialog.cpp | 3 +-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/launcher/FileIgnoreProxy.cpp b/launcher/FileIgnoreProxy.cpp index 4c8c64c72..df06c3c75 100644 --- a/launcher/FileIgnoreProxy.cpp +++ b/launcher/FileIgnoreProxy.cpp @@ -267,10 +267,7 @@ bool FileIgnoreProxy::filterAcceptsRow(int sourceRow, const QModelIndex& sourceP bool FileIgnoreProxy::ignoreFile(QFileInfo fileInfo) const { - auto fileName = fileInfo.fileName(); - auto path = relPath(fileInfo.absoluteFilePath()); - return std::any_of(m_ignoreFiles.cbegin(), m_ignoreFiles.cend(), [fileName](auto iFileName) { return fileName == iFileName; }) || - m_ignoreFilePaths.covers(path); + return m_ignoreFiles.contains(fileInfo.fileName()) || m_ignoreFilePaths.covers(relPath(fileInfo.absoluteFilePath())); } bool FileIgnoreProxy::filterFile(const QString& fileName) const diff --git a/launcher/ui/dialogs/ExportPackDialog.cpp b/launcher/ui/dialogs/ExportPackDialog.cpp index b0558df7d..5af24b1b7 100644 --- a/launcher/ui/dialogs/ExportPackDialog.cpp +++ b/launcher/ui/dialogs/ExportPackDialog.cpp @@ -81,10 +81,9 @@ ExportPackDialog::ExportPackDialog(InstancePtr instance, QWidget* parent, ModPla MinecraftInstance* mcInstance = dynamic_cast(instance.get()); if (mcInstance) { - mcInstance->loaderModList()->update(); const QDir index = mcInstance->loaderModList()->indexDir(); if (index.exists()) - proxy->blockedPaths().insert(root.relativeFilePath(index.absolutePath())); + proxy->ignoreFilesWithPath().insert(root.relativeFilePath(index.absolutePath())); } ui->files->setModel(proxy); From 8c30cb370676970f62a56e9f5615b22f646eb2e3 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Tue, 29 Aug 2023 13:27:06 +0100 Subject: [PATCH 199/268] Fix CI Signed-off-by: TheKodeToad --- launcher/InstanceList.cpp | 1 - launcher/InstanceList.h | 2 +- launcher/ui/MainWindow.cpp | 5 +++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/launcher/InstanceList.cpp b/launcher/InstanceList.cpp index ef3707ec9..756ff93dd 100644 --- a/launcher/InstanceList.cpp +++ b/launcher/InstanceList.cpp @@ -645,7 +645,6 @@ InstancePtr InstanceList::loadInstance(const InstanceId& id) return inst; } - void InstanceList::increaseGroupCount(const QString& group) { if (group.isEmpty()) diff --git a/launcher/InstanceList.h b/launcher/InstanceList.h index 26aea6556..6b0bcd810 100644 --- a/launcher/InstanceList.h +++ b/launcher/InstanceList.h @@ -106,7 +106,7 @@ class InstanceList : public QAbstractListModel { bool isGroupCollapsed(const QString& groupName); GroupId getInstanceGroup(const InstanceId& id) const; - void setInstanceGroup(const InstanceId& id, const GroupId& name); + void setInstanceGroup(const InstanceId& id, GroupId name); void deleteGroup(const GroupId& name); void renameGroup(const GroupId& src, const GroupId& dst); diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index eabfe7a49..3f0e4fb2a 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -1149,12 +1149,13 @@ void MainWindow::deleteGroup(QString group) Q_ASSERT(!group.isEmpty()); const int reply = QMessageBox::question(this, tr("Delete group"), tr("Are you sure you want to delete the group '%1'?").arg(group), - QMessageBox::Yes | QMessageBox::No); + QMessageBox::Yes | QMessageBox::No); if (reply == QMessageBox::Yes) APPLICATION->instances()->deleteGroup(group); } -void MainWindow::renameGroup(QString group) { +void MainWindow::renameGroup(QString group) +{ Q_ASSERT(!group.isEmpty()); QString name = QInputDialog::getText(this, tr("Rename group"), tr("Enter a new group name."), QLineEdit::Normal, group); From f23a8e4b4b80cff4b2e15fa1783dbf046d2dfc18 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Tue, 29 Aug 2023 23:30:10 +0100 Subject: [PATCH 200/268] Enable antialiasing for mod and pack icons Signed-off-by: TheKodeToad --- launcher/minecraft/mod/Mod.cpp | 5 +++-- launcher/minecraft/mod/ResourcePack.cpp | 5 +++-- launcher/minecraft/mod/TexturePack.cpp | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index ae3dea8d8..b59c6d37c 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -246,7 +246,8 @@ void Mod::setIcon(QImage new_image) const PixmapCache::remove(m_pack_image_cache_key.key); // scale the image to avoid flooding the pixmapcache - auto pixmap = QPixmap::fromImage(new_image.scaled({ 64, 64 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding)); + auto pixmap = + QPixmap::fromImage(new_image.scaled({ 64, 64 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding, Qt::SmoothTransformation)); m_pack_image_cache_key.key = PixmapCache::insert(pixmap); m_pack_image_cache_key.was_ever_used = true; @@ -259,7 +260,7 @@ QPixmap Mod::icon(QSize size, Qt::AspectRatioMode mode) const if (PixmapCache::find(m_pack_image_cache_key.key, &cached_image)) { if (size.isNull()) return cached_image; - return cached_image.scaled(size, mode); + return cached_image.scaled(size, mode, Qt::SmoothTransformation); } // No valid image we can get diff --git a/launcher/minecraft/mod/ResourcePack.cpp b/launcher/minecraft/mod/ResourcePack.cpp index dab0f6d67..2bb51dc5b 100644 --- a/launcher/minecraft/mod/ResourcePack.cpp +++ b/launcher/minecraft/mod/ResourcePack.cpp @@ -50,7 +50,8 @@ void ResourcePack::setImage(QImage new_image) const PixmapCache::instance().remove(m_pack_image_cache_key.key); // scale the image to avoid flooding the pixmapcache - auto pixmap = QPixmap::fromImage(new_image.scaled({ 64, 64 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding)); + auto pixmap = + QPixmap::fromImage(new_image.scaled({ 64, 64 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding, Qt::SmoothTransformation)); m_pack_image_cache_key.key = PixmapCache::instance().insert(pixmap); m_pack_image_cache_key.was_ever_used = true; @@ -68,7 +69,7 @@ QPixmap ResourcePack::image(QSize size, Qt::AspectRatioMode mode) const if (PixmapCache::instance().find(m_pack_image_cache_key.key, &cached_image)) { if (size.isNull()) return cached_image; - return cached_image.scaled(size, mode); + return cached_image.scaled(size, mode, Qt::SmoothTransformation); } // No valid image we can get diff --git a/launcher/minecraft/mod/TexturePack.cpp b/launcher/minecraft/mod/TexturePack.cpp index 7d8c67137..04cc36310 100644 --- a/launcher/minecraft/mod/TexturePack.cpp +++ b/launcher/minecraft/mod/TexturePack.cpp @@ -44,7 +44,8 @@ void TexturePack::setImage(QImage new_image) const PixmapCache::remove(m_pack_image_cache_key.key); // scale the image to avoid flooding the pixmapcache - auto pixmap = QPixmap::fromImage(new_image.scaled({ 64, 64 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding)); + auto pixmap = + QPixmap::fromImage(new_image.scaled({ 64, 64 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding, Qt::SmoothTransformation)); m_pack_image_cache_key.key = PixmapCache::insert(pixmap); m_pack_image_cache_key.was_ever_used = true; @@ -56,7 +57,7 @@ QPixmap TexturePack::image(QSize size, Qt::AspectRatioMode mode) const if (PixmapCache::find(m_pack_image_cache_key.key, &cached_image)) { if (size.isNull()) return cached_image; - return cached_image.scaled(size, mode); + return cached_image.scaled(size, mode, Qt::SmoothTransformation); } // No valid image we can get From 584e800279351cdd592d1012ca885dd86b9f26f2 Mon Sep 17 00:00:00 2001 From: seth Date: Wed, 30 Aug 2023 11:13:39 -0400 Subject: [PATCH 201/268] fix: remove -Wextra-semi this flag is unavailable on gcc versions < 8. we could detect the version of the compiler here, but i don't think we lose much in this flags removal and this is a simpler option Signed-off-by: seth --- cmake/CompilerWarnings.cmake | 1 - 1 file changed, 1 deletion(-) diff --git a/cmake/CompilerWarnings.cmake b/cmake/CompilerWarnings.cmake index 635e54289..69e57b4d5 100644 --- a/cmake/CompilerWarnings.cmake +++ b/cmake/CompilerWarnings.cmake @@ -75,7 +75,6 @@ function( set(CLANG_WARNINGS -Wall -Wextra # reasonable and standard - -Wextra-semi # Warn about semicolon after in-class function definition. -Wshadow # warn the user if a variable declaration shadows one from a parent context -Wnon-virtual-dtor # warn the user if a class with virtual functions has a non-virtual destructor. This helps # catch hard to track down memory errors From 707da5a25a11a8ae6c60f619f37b9fdcb7970082 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Wed, 30 Aug 2023 21:59:41 +0200 Subject: [PATCH 202/268] fix(nix): include udev dependency See https://github.com/NixOS/nixpkgs/pull/252425 Signed-off-by: Sefa Eyeoglu --- nix/pkg/wrapper.nix | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/nix/pkg/wrapper.nix b/nix/pkg/wrapper.nix index 3bf5b3f85..8d160143d 100644 --- a/nix/pkg/wrapper.nix +++ b/nix/pkg/wrapper.nix @@ -17,6 +17,7 @@ gamemode, flite, mesa-demos, + udev, msaClientID ? null, gamemodeSupport ? stdenv.isLinux, textToSpeechSupport ? stdenv.isLinux, @@ -58,11 +59,15 @@ in libXxf86vm ]) ++ [ + # lwjgl libpulseaudio libGL glfw openal stdenv.cc.cc.lib + + # oshi + udev ] ++ lib.optional gamemodeSupport gamemode.lib ++ lib.optional textToSpeechSupport flite From 6cfe2dbc50d0dbe53c59012375fcd0cf3a8558a9 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 31 Aug 2023 17:34:05 +0300 Subject: [PATCH 203/268] moved catpack data to testdata Signed-off-by: Trial97 --- tests/CatPack_test.cpp | 41 ++---------------------------- tests/testdata/CatPacks/index.json | 26 +++++++++++++++++++ 2 files changed, 28 insertions(+), 39 deletions(-) create mode 100644 tests/testdata/CatPacks/index.json diff --git a/tests/CatPack_test.cpp b/tests/CatPack_test.cpp index a84ba6511..330d1a814 100644 --- a/tests/CatPack_test.cpp +++ b/tests/CatPack_test.cpp @@ -12,45 +12,8 @@ class CatPackTest : public QObject { private slots: void test_catPack() { - QString fileContent = R"({ - "name": "My Cute Cat", - "default": "maxwell.png", - "variants": [ - { - "startTime": { "day": 12, "month": 4 }, - "endTime": { "day": 12, "month": 4 }, - "path": "oneDay.png" - }, - { - "startTime": { "day": 20, "month": 12 }, - "endTime": { "day": 28, "month": 12 }, - "path": "christmas.png" - }, - { - "startTime": { "day": 30, "month": 12 }, - "endTime": { "day": 1, "month": 1 }, - "path": "newyear2.png" - }, - { - "startTime": { "day": 28, "month": 12 }, - "endTime": { "day": 3, "month": 1 }, - "path": "newyear.png" - } - ] -})"; -#if defined(Q_OS_WIN) - QString fileName = "test_SaveAlreadyExistingFile.ini"; - QFile file(fileName); - QCOMPARE(file.open(QFile::WriteOnly | QFile::Text), true); -#else - QTemporaryFile file; - QCOMPARE(file.open(), true); - QCOMPARE(file.fileName().isEmpty(), false); - QString fileName = file.fileName(); -#endif - QTextStream stream(&file); - stream << fileContent; - file.close(); + auto dataDir = QDir(QFINDTESTDATA("testdata/CatPacks")).absolutePath(); + auto fileName = FS::PathCombine(dataDir, "index.json"); auto fileinfo = QFileInfo(fileName); try { auto cat = JsonCatPack(fileinfo); diff --git a/tests/testdata/CatPacks/index.json b/tests/testdata/CatPacks/index.json new file mode 100644 index 000000000..c55813dbb --- /dev/null +++ b/tests/testdata/CatPacks/index.json @@ -0,0 +1,26 @@ +{ + "name": "My Cute Cat", + "default": "maxwell.png", + "variants": [ + { + "startTime": { "day": 12, "month": 4 }, + "endTime": { "day": 12, "month": 4 }, + "path": "oneDay.png" + }, + { + "startTime": { "day": 20, "month": 12 }, + "endTime": { "day": 28, "month": 12 }, + "path": "christmas.png" + }, + { + "startTime": { "day": 30, "month": 12 }, + "endTime": { "day": 1, "month": 1 }, + "path": "newyear2.png" + }, + { + "startTime": { "day": 28, "month": 12 }, + "endTime": { "day": 3, "month": 1 }, + "path": "newyear.png" + } + ] +} From 7e65aea2ef790d8f2e424e618d9ab9acd476f045 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 31 Aug 2023 19:04:41 +0300 Subject: [PATCH 204/268] format json Signed-off-by: Trial97 --- tests/testdata/CatPacks/index.json | 72 ++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 24 deletions(-) diff --git a/tests/testdata/CatPacks/index.json b/tests/testdata/CatPacks/index.json index c55813dbb..b5401d230 100644 --- a/tests/testdata/CatPacks/index.json +++ b/tests/testdata/CatPacks/index.json @@ -1,26 +1,50 @@ { - "name": "My Cute Cat", - "default": "maxwell.png", - "variants": [ - { - "startTime": { "day": 12, "month": 4 }, - "endTime": { "day": 12, "month": 4 }, - "path": "oneDay.png" - }, - { - "startTime": { "day": 20, "month": 12 }, - "endTime": { "day": 28, "month": 12 }, - "path": "christmas.png" - }, - { - "startTime": { "day": 30, "month": 12 }, - "endTime": { "day": 1, "month": 1 }, - "path": "newyear2.png" - }, - { - "startTime": { "day": 28, "month": 12 }, - "endTime": { "day": 3, "month": 1 }, - "path": "newyear.png" - } - ] + "name": "My Cute Cat", + "default": "maxwell.png", + "variants": [ + { + "startTime": { + "day": 12, + "month": 4 + }, + "endTime": { + "day": 12, + "month": 4 + }, + "path": "oneDay.png" + }, + { + "startTime": { + "day": 20, + "month": 12 + }, + "endTime": { + "day": 28, + "month": 12 + }, + "path": "christmas.png" + }, + { + "startTime": { + "day": 30, + "month": 12 + }, + "endTime": { + "day": 1, + "month": 1 + }, + "path": "newyear2.png" + }, + { + "startTime": { + "day": 28, + "month": 12 + }, + "endTime": { + "day": 3, + "month": 1 + }, + "path": "newyear.png" + } + ] } From 30ff417074a6a5f2200df9bac92a537425417103 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 1 Sep 2023 16:25:49 +0300 Subject: [PATCH 205/268] fix: make cached instead of file for ftb pack import Signed-off-by: Trial97 --- launcher/modplatform/legacy_ftb/PackInstallTask.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp index 761f622bb..091296751 100644 --- a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp +++ b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp @@ -70,16 +70,18 @@ void PackInstallTask::downloadPack() setProgress(1, 4); setAbortable(false); - archivePath = QString("%1/%2/%3").arg(m_pack.dir, m_version.replace(".", "_"), m_pack.file); - + auto path = QString("%1/%2/%3").arg(m_pack.dir, m_version.replace(".", "_"), m_pack.file); + auto entry = APPLICATION->metacache()->resolveEntry("FTBPacks", path); + entry->setStale(true); + archivePath = entry->getFullPath(); netJobContainer.reset(new NetJob("Download FTB Pack", m_network)); QString url; if (m_pack.type == PackType::Private) { - url = QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "privatepacks/%1").arg(archivePath); + url = QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "privatepacks/%1").arg(path); } else { - url = QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "modpacks/%1").arg(archivePath); + url = QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "modpacks/%1").arg(path); } - netJobContainer->addNetAction(Net::ApiDownload::makeFile(url, archivePath)); + netJobContainer->addNetAction(Net::ApiDownload::makeCached(url, entry)); connect(netJobContainer.get(), &NetJob::succeeded, this, &PackInstallTask::unzip); connect(netJobContainer.get(), &NetJob::failed, this, &PackInstallTask::emitFailed); From eb66e37b8311fb830727e2632c6a00301b36b1f1 Mon Sep 17 00:00:00 2001 From: kumquat-ir <66188216+kumquat-ir@users.noreply.github.com> Date: Fri, 1 Sep 2023 10:29:16 -0400 Subject: [PATCH 206/268] fix opening files from curseforge: links on linux Signed-off-by: kumquat-ir <66188216+kumquat-ir@users.noreply.github.com> --- program_info/org.prismlauncher.PrismLauncher.desktop.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program_info/org.prismlauncher.PrismLauncher.desktop.in b/program_info/org.prismlauncher.PrismLauncher.desktop.in index 816c00595..98ac92fe5 100644 --- a/program_info/org.prismlauncher.PrismLauncher.desktop.in +++ b/program_info/org.prismlauncher.PrismLauncher.desktop.in @@ -4,7 +4,7 @@ Name=Prism Launcher Comment=A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once. Type=Application Terminal=false -Exec=@Launcher_APP_BINARY_NAME@ +Exec=@Launcher_APP_BINARY_NAME@ %u StartupNotify=true Icon=org.prismlauncher.PrismLauncher Categories=Game;ActionGame;AdventureGame;Simulation; From 984e870bca6ac4e8264ba7221bd99b54c0bb0fc9 Mon Sep 17 00:00:00 2001 From: kumquat-ir <66188216+kumquat-ir@users.noreply.github.com> Date: Fri, 1 Sep 2023 10:32:35 -0400 Subject: [PATCH 207/268] actually it can handle multiple files just fine Signed-off-by: kumquat-ir <66188216+kumquat-ir@users.noreply.github.com> --- program_info/org.prismlauncher.PrismLauncher.desktop.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program_info/org.prismlauncher.PrismLauncher.desktop.in b/program_info/org.prismlauncher.PrismLauncher.desktop.in index 98ac92fe5..76f4b19c0 100644 --- a/program_info/org.prismlauncher.PrismLauncher.desktop.in +++ b/program_info/org.prismlauncher.PrismLauncher.desktop.in @@ -4,7 +4,7 @@ Name=Prism Launcher Comment=A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once. Type=Application Terminal=false -Exec=@Launcher_APP_BINARY_NAME@ %u +Exec=@Launcher_APP_BINARY_NAME@ %U StartupNotify=true Icon=org.prismlauncher.PrismLauncher Categories=Game;ActionGame;AdventureGame;Simulation; From 17f696bffcf83ff9d40964137c9743b590161e77 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 1 Sep 2023 22:25:15 +0300 Subject: [PATCH 208/268] small tweaks to atl icons Signed-off-by: Trial97 --- launcher/modplatform/atlauncher/ATLPackIndex.cpp | 2 +- launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/modplatform/atlauncher/ATLPackIndex.cpp b/launcher/modplatform/atlauncher/ATLPackIndex.cpp index 84b0cc9e2..678db63cc 100644 --- a/launcher/modplatform/atlauncher/ATLPackIndex.cpp +++ b/launcher/modplatform/atlauncher/ATLPackIndex.cpp @@ -43,5 +43,5 @@ void ATLauncher::loadIndexedPack(ATLauncher::IndexedPack& m, QJsonObject& obj) m.system = Json::ensureBoolean(obj, QString("system"), false); m.description = Json::ensureString(obj, "description", ""); - m.safeName = "atl_" + Json::requireString(obj, "name").replace(QRegularExpression("[^A-Za-z0-9]"), "").toLower() + ".png"; + m.safeName = Json::requireString(obj, "name").replace(QRegularExpression("[^A-Za-z0-9]"), "").toLower() + ".png"; } diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp index 6298cd0b4..e492830c6 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp @@ -114,7 +114,7 @@ void AtlPage::suggestCurrent() auto uiSupport = new AtlUserInteractionSupportImpl(this); dialog->setSuggestedPack(selected.name, selectedVersion, new ATLauncher::PackInstallTask(uiSupport, selected.name, selectedVersion)); - auto editedLogoName = selected.safeName; + auto editedLogoName = "atl_" + selected.safeName; auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/images/%1").arg(selected.safeName); listModel->getLogo(selected.safeName, url, [this, editedLogoName](QString logo) { dialog->setSuggestedIconFromFile(logo, editedLogoName); }); From 347228a24613f5ec7ce63341a929a8a88bb2a6b4 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sat, 2 Sep 2023 22:29:26 +0100 Subject: [PATCH 209/268] Legacy settings override default -> false Signed-off-by: TheKodeToad --- launcher/minecraft/MinecraftInstance.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index bc8d2db58..52416fe15 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -185,7 +185,7 @@ void MinecraftInstance::loadSpecificSettings() m_settings->registerOverride(global_settings->getSetting("QuitAfterGameStop"), miscellaneousOverride); // Legacy-related options - auto legacySettings = m_settings->registerSetting("OverrideLegacySettings", true); + auto legacySettings = m_settings->registerSetting("OverrideLegacySettings", false); m_settings->registerOverride(global_settings->getSetting("OnlineFixes"), legacySettings); m_settings->set("InstanceType", "OneSix"); From 19b5a5e00badad2630e2e4ae431d792a06bfe6b6 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sat, 2 Sep 2023 22:38:14 +0100 Subject: [PATCH 210/268] Remove final conflict - kept by mistake Signed-off-by: TheKodeToad --- .../legacy/org/prismlauncher/legacy/LegacyLauncher.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libraries/launcher/legacy/org/prismlauncher/legacy/LegacyLauncher.java b/libraries/launcher/legacy/org/prismlauncher/legacy/LegacyLauncher.java index 90244e1ae..02f77e039 100644 --- a/libraries/launcher/legacy/org/prismlauncher/legacy/LegacyLauncher.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/LegacyLauncher.java @@ -122,7 +122,6 @@ final class LegacyLauncher extends AbstractLauncher { MethodHandle method = ReflectionUtils.findMainMethod(main); method.invokeExact(gameArgs.toArray(new String[0])); } -<<<<<<< HEAD:libraries/launcher/legacy/org/prismlauncher/legacy/LegacyLauncher.java private static Applet createAppletClass(String clazz) throws Throwable { Class appletClass = ClassLoader.getSystemClassLoader().loadClass(clazz); @@ -153,7 +152,4 @@ final class LegacyLauncher extends AbstractLauncher { return null; } - -======= ->>>>>>> upstream/develop:libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java } From 88f3e19f815e67f6cb38c2b9df2dbf6008d4d60a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 3 Sep 2023 00:17:45 +0000 Subject: [PATCH 211/268] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'flake-parts': 'github:hercules-ci/flake-parts/59cf3f1447cfc75087e7273b04b31e689a8599fb' (2023-08-01) → 'github:hercules-ci/flake-parts/7f53fdb7bdc5bb237da7fefef12d099e4fd611ca' (2023-09-01) • Updated input 'flake-parts/nixpkgs-lib': 'github:NixOS/nixpkgs/9e1960bc196baf6881340d53dccb203a951745a2?dir=lib' (2023-08-01) → 'github:NixOS/nixpkgs/3e52e76b70d5508f3cec70b882a29199f4d1ee85?dir=lib' (2023-08-31) • Updated input 'nixpkgs': 'github:nixos/nixpkgs/cddebdb60de376c1bdb7a4e6ee3d98355453fe56' (2023-08-27) → 'github:nixos/nixpkgs/bfb7dfec93f3b5d7274db109f2990bc889861caf' (2023-09-02) --- flake.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/flake.lock b/flake.lock index f27ca5207..b1486ea69 100644 --- a/flake.lock +++ b/flake.lock @@ -21,11 +21,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1690933134, - "narHash": "sha256-ab989mN63fQZBFrkk4Q8bYxQCktuHmBIBqUG1jl6/FQ=", + "lastModified": 1693611461, + "narHash": "sha256-aPODl8vAgGQ0ZYFIRisxYG5MOGSkIczvu2Cd8Gb9+1Y=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "59cf3f1447cfc75087e7273b04b31e689a8599fb", + "rev": "7f53fdb7bdc5bb237da7fefef12d099e4fd611ca", "type": "github" }, "original": { @@ -91,11 +91,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1693145325, - "narHash": "sha256-Gat9xskErH1zOcLjYMhSDBo0JTBZKfGS0xJlIRnj6Rc=", + "lastModified": 1693626178, + "narHash": "sha256-Rpiy6lIOu4zny8tfGuIeN1ji9eSz9nPmm9yBhh/4IOM=", "owner": "nixos", "repo": "nixpkgs", - "rev": "cddebdb60de376c1bdb7a4e6ee3d98355453fe56", + "rev": "bfb7dfec93f3b5d7274db109f2990bc889861caf", "type": "github" }, "original": { @@ -108,11 +108,11 @@ "nixpkgs-lib": { "locked": { "dir": "lib", - "lastModified": 1690881714, - "narHash": "sha256-h/nXluEqdiQHs1oSgkOOWF+j8gcJMWhwnZ9PFabN6q0=", + "lastModified": 1693471703, + "narHash": "sha256-0l03ZBL8P1P6z8MaSDS/MvuU8E75rVxe5eE1N6gxeTo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "9e1960bc196baf6881340d53dccb203a951745a2", + "rev": "3e52e76b70d5508f3cec70b882a29199f4d1ee85", "type": "github" }, "original": { From bbf4e3b04d98eac840134b1687aba63ec180ead0 Mon Sep 17 00:00:00 2001 From: seth Date: Sun, 3 Sep 2023 18:11:23 -0400 Subject: [PATCH 212/268] refactor(nix): use fixed points over rec Signed-off-by: seth --- nix/distribution.nix | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/nix/distribution.nix b/nix/distribution.nix index 0dde1e4f0..6b93d355f 100644 --- a/nix/distribution.nix +++ b/nix/distribution.nix @@ -3,18 +3,28 @@ self, ... }: { - perSystem = {pkgs, ...}: { + perSystem = { + lib, + pkgs, + ... + }: { packages = let - ourPackages = self.overlays.default pkgs null; + ourPackages = lib.fix (final: self.overlays.default ({inherit (pkgs) darwin;} // final) pkgs); in { - inherit (ourPackages) prismlauncher-qt5-unwrapped prismlauncher-qt5 prismlauncher-unwrapped prismlauncher; + inherit + (ourPackages) + prismlauncher-qt5-unwrapped + prismlauncher-qt5 + prismlauncher-unwrapped + prismlauncher + ; default = ourPackages.prismlauncher; }; }; flake = { - overlays.default = final: _: let - version = builtins.substring 0 8 self.lastModifiedDate; + overlays.default = final: prev: let + version = builtins.substring 0 8 self.lastModifiedDate or "dirty"; # common args for prismlauncher evaluations unwrappedArgs = { @@ -22,11 +32,13 @@ inherit (final.darwin.apple_sdk.frameworks) Cocoa; inherit self version; }; - in rec { - prismlauncher-qt5-unwrapped = final.libsForQt5.callPackage ./pkg unwrappedArgs; - prismlauncher-qt5 = final.libsForQt5.callPackage ./pkg/wrapper.nix {prismlauncher-unwrapped = prismlauncher-qt5-unwrapped;}; - prismlauncher-unwrapped = final.qt6Packages.callPackage ./pkg unwrappedArgs; - prismlauncher = final.qt6Packages.callPackage ./pkg/wrapper.nix {inherit prismlauncher-unwrapped;}; + in { + prismlauncher-qt5-unwrapped = prev.libsForQt5.callPackage ./pkg unwrappedArgs; + prismlauncher-qt5 = prev.libsForQt5.callPackage ./pkg/wrapper.nix { + prismlauncher-unwrapped = final.prismlauncher-qt5-unwrapped; + }; + prismlauncher-unwrapped = prev.qt6Packages.callPackage ./pkg unwrappedArgs; + prismlauncher = prev.qt6Packages.callPackage ./pkg/wrapper.nix {inherit (final) prismlauncher-unwrapped;}; }; }; } From 2918d61b16934980ed43fa52ead291e37a66732c Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Mon, 4 Sep 2023 08:52:56 +0200 Subject: [PATCH 213/268] refactor(nix): use pre-commit flake module Signed-off-by: Sefa Eyeoglu --- flake.nix | 2 ++ nix/dev.nix | 38 +++++++++++++++++--------------------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/flake.nix b/flake.nix index 8f31ccec5..d45282aa6 100644 --- a/flake.nix +++ b/flake.nix @@ -25,6 +25,8 @@ {inherit inputs;} { imports = [ + inputs.pre-commit-hooks.flakeModule + ./nix/dev.nix ./nix/distribution.nix ]; diff --git a/nix/dev.nix b/nix/dev.nix index a9c1dc65d..c476ed10f 100644 --- a/nix/dev.nix +++ b/nix/dev.nix @@ -1,37 +1,33 @@ { - inputs, - self, - ... -}: { perSystem = { - system, + config, + lib, pkgs, ... }: { - checks = { - pre-commit-check = inputs.pre-commit-hooks.lib.${system}.run { - src = self; - hooks = { - markdownlint.enable = true; + pre-commit.settings = { + hooks = { + markdownlint.enable = true; - alejandra.enable = true; - deadnix.enable = true; - nil.enable = true; + alejandra.enable = true; + deadnix.enable = true; + nil.enable = true; - clang-format = { - enable = true; - types_or = ["c" "c++" "java" "json" "objective-c"]; - }; + clang-format = { + enable = true; + types_or = ["c" "c++" "java" "json" "objective-c"]; }; - - tools.clang-tools = pkgs.clang-tools_16; }; + + tools.clang-tools = lib.mkForce pkgs.clang-tools_16; }; devShells.default = pkgs.mkShell { - inherit (self.checks.${system}.pre-commit-check) shellHook; + shellHook = '' + ${config.pre-commit.installationScript} + ''; - inputsFrom = [self.packages.${system}.prismlauncher-unwrapped]; + inputsFrom = [config.packages.prismlauncher-unwrapped]; buildInputs = with pkgs; [ccache ninja]; }; From e1558446717c83f14c575e0b8759f6a370633477 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 10:22:11 +0000 Subject: [PATCH 214/268] chore(deps): update cachix/install-nix-action action to v23 --- .github/workflows/update-flake.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-flake.yml b/.github/workflows/update-flake.yml index ad4016ff4..16dbd7240 100644 --- a/.github/workflows/update-flake.yml +++ b/.github/workflows/update-flake.yml @@ -17,7 +17,7 @@ jobs: steps: - uses: actions/checkout@v3 - - uses: cachix/install-nix-action@v22 + - uses: cachix/install-nix-action@6a9a9e84a173d90b3ffb42c5ddaf9ea033fad011 # v23 - uses: DeterminateSystems/update-flake-lock@v20 with: From 94c1cd6bcfbc7a66286c18bf1d1c3854f49deb64 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Mon, 4 Sep 2023 14:13:44 +0100 Subject: [PATCH 215/268] CurseForge shader downloading Signed-off-by: TheKodeToad --- launcher/modplatform/flame/FlameAPI.h | 2 + .../ui/dialogs/ResourceDownloadDialog.cpp | 2 + .../modplatform/flame/FlameResourceModels.cpp | 23 ++++++++++ .../modplatform/flame/FlameResourceModels.h | 17 ++++++++ .../modplatform/flame/FlameResourcePages.cpp | 43 +++++++++++++++++++ .../modplatform/flame/FlameResourcePages.h | 28 ++++++++++++ 6 files changed, 115 insertions(+) diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h index 47350c33e..e22d8f0d8 100644 --- a/launcher/modplatform/flame/FlameAPI.h +++ b/launcher/modplatform/flame/FlameAPI.h @@ -38,6 +38,8 @@ class FlameAPI : public NetworkResourceAPI { return 6; case ModPlatform::ResourceType::RESOURCE_PACK: return 12; + case ModPlatform::ResourceType::SHADER_PACK: + return 6552; } } diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp index 9e121bb61..bf76b01e3 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp +++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp @@ -370,6 +370,8 @@ QList ShaderPackDownloadDialog::getPages() { QList pages; pages.append(ModrinthShaderPackPage::create(this, *m_instance)); + if (APPLICATION->capabilities() & Application::SupportsFlame) + pages.append(FlameShaderPackPage::create(this, *m_instance)); return pages; } diff --git a/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp index c80e4f999..7d18e72a6 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp @@ -121,4 +121,27 @@ auto FlameTexturePackModel::documentToArray(QJsonDocument& obj) const -> QJsonAr return Json::ensureArray(obj.object(), "data"); } +FlameShaderPackModel::FlameShaderPackModel(const BaseInstance& base) : ShaderPackResourceModel(base, new FlameAPI) {} + +void FlameShaderPackModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) +{ + FlameMod::loadIndexedPack(m, obj); +} + +// We already deal with the URLs when initializing the pack, due to the API response's structure +void FlameShaderPackModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) +{ + FlameMod::loadBody(m, obj); +} + +void FlameShaderPackModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) +{ + FlameMod::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_base_instance); +} + +auto FlameShaderPackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray +{ + return Json::ensureArray(obj.object(), "data"); +} + } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/flame/FlameResourceModels.h b/launcher/ui/pages/modplatform/flame/FlameResourceModels.h index 6cfd6a6f8..76dbd7b3d 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourceModels.h +++ b/launcher/ui/pages/modplatform/flame/FlameResourceModels.h @@ -68,4 +68,21 @@ class FlameTexturePackModel : public TexturePackResourceModel { auto documentToArray(QJsonDocument& obj) const -> QJsonArray override; }; +class FlameShaderPackModel : public ShaderPackResourceModel { + Q_OBJECT + + public: + FlameShaderPackModel(const BaseInstance&); + ~FlameShaderPackModel() override = default; + + private: + [[nodiscard]] QString debugName() const override { return Flame::debugName() + " (Model)"; } + [[nodiscard]] QString metaEntryBase() const override { return Flame::metaEntryBase(); } + + void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override; + void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) override; + void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override; + auto documentToArray(QJsonDocument& obj) const -> QJsonArray override; +}; + } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp index 1403e98f9..23373ec9d 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp @@ -173,6 +173,45 @@ void FlameTexturePackPage::openUrl(const QUrl& url) TexturePackResourcePage::openUrl(url); } +FlameShaderPackPage::FlameShaderPackPage(ShaderPackDownloadDialog* dialog, BaseInstance& instance) + : ShaderPackResourcePage(dialog, instance) +{ + m_model = new FlameShaderPackModel(instance); + m_ui->packView->setModel(m_model); + + addSortings(); + + // sometimes Qt just ignores virtual slots and doesn't work as intended it seems, + // so it's best not to connect them in the parent's constructor... + connect(m_ui->sortByBox, SIGNAL(currentIndexChanged(int)), this, SLOT(triggerSearch())); + connect(m_ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &FlameShaderPackPage::onSelectionChanged); + connect(m_ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &FlameShaderPackPage::onVersionSelectionChanged); + connect(m_ui->resourceSelectionButton, &QPushButton::clicked, this, &FlameShaderPackPage::onResourceSelected); + + m_ui->packDescription->setMetaEntry(metaEntryBase()); +} + +bool FlameShaderPackPage::optedOut(ModPlatform::IndexedVersion& ver) const +{ + return isOptedOut(ver); +} + +void FlameShaderPackPage::openUrl(const QUrl& url) +{ + if (url.scheme().isEmpty()) { + QString query = url.query(QUrl::FullyDecoded); + + if (query.startsWith("remoteUrl=")) { + // attempt to resolve url from warning page + query.remove(0, 10); + ShaderPackResourcePage::openUrl({ QUrl::fromPercentEncoding(query.toUtf8()) }); // double decoding is necessary + return; + } + } + + ShaderPackResourcePage::openUrl(url); +} + // I don't know why, but doing this on the parent class makes it so that // other mod providers start loading before being selected, at least with // my Qt, so we need to implement this in every derived class... @@ -188,5 +227,9 @@ auto FlameTexturePackPage::shouldDisplay() const -> bool { return true; } +auto FlameShaderPackPage::shouldDisplay() const -> bool +{ + return true; +} } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.h b/launcher/ui/pages/modplatform/flame/FlameResourcePages.h index 035da2d5f..f2f5cecad 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourcePages.h +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.h @@ -44,6 +44,7 @@ #include "ui/pages/modplatform/ModPage.h" #include "ui/pages/modplatform/ResourcePackPage.h" +#include "ui/pages/modplatform/ShaderPackPage.h" #include "ui/pages/modplatform/TexturePackPage.h" namespace ResourceDownload { @@ -155,4 +156,31 @@ class FlameTexturePackPage : public TexturePackResourcePage { void openUrl(const QUrl& url) override; }; +class FlameShaderPackPage : public ShaderPackResourcePage { + Q_OBJECT + + public: + static FlameShaderPackPage* create(ShaderPackDownloadDialog* dialog, BaseInstance& instance) + { + return ShaderPackResourcePage::create(dialog, instance); + } + + FlameShaderPackPage(ShaderPackDownloadDialog* dialog, BaseInstance& instance); + ~FlameShaderPackPage() override = default; + + [[nodiscard]] bool shouldDisplay() const override; + + [[nodiscard]] inline auto displayName() const -> QString override { return Flame::displayName(); } + [[nodiscard]] inline auto icon() const -> QIcon override { return Flame::icon(); } + [[nodiscard]] inline auto id() const -> QString override { return Flame::id(); } + [[nodiscard]] inline auto debugName() const -> QString override { return Flame::debugName(); } + [[nodiscard]] inline auto metaEntryBase() const -> QString override { return Flame::metaEntryBase(); } + + [[nodiscard]] inline auto helpPage() const -> QString override { return ""; } + + bool optedOut(ModPlatform::IndexedVersion& ver) const override; + + void openUrl(const QUrl& url) override; +}; + } // namespace ResourceDownload From 7dd2530027fb17997283e1ec812f036328057d39 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 15:02:01 +0000 Subject: [PATCH 216/268] chore(deps): update actions/checkout action to v4 --- .github/workflows/backport.yml | 2 +- .github/workflows/build.yml | 4 ++-- .github/workflows/codeql.yml | 2 +- .github/workflows/trigger_release.yml | 2 +- .github/workflows/update-flake.yml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 08cfb56dd..c705ff7b0 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -20,7 +20,7 @@ jobs: if: github.repository_owner == 'PrismLauncher' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name)) runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} - name: Create backport PRs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d044f4faf..e0434ce68 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -125,7 +125,7 @@ jobs: # PREPARE ## - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: 'true' @@ -620,7 +620,7 @@ jobs: options: --privileged steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 if: inputs.build_type == 'Debug' with: submodules: 'true' diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 0cd1f6e40..a77b4ae1e 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -8,7 +8,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: 'true' diff --git a/.github/workflows/trigger_release.yml b/.github/workflows/trigger_release.yml index 2a46ff5e7..bda75e354 100644 --- a/.github/workflows/trigger_release.yml +++ b/.github/workflows/trigger_release.yml @@ -26,7 +26,7 @@ jobs: upload_url: ${{ steps.create_release.outputs.upload_url }} steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: 'true' path: 'PrismLauncher-source' diff --git a/.github/workflows/update-flake.yml b/.github/workflows/update-flake.yml index 16dbd7240..6a16b0369 100644 --- a/.github/workflows/update-flake.yml +++ b/.github/workflows/update-flake.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: cachix/install-nix-action@6a9a9e84a173d90b3ffb42c5ddaf9ea033fad011 # v23 - uses: DeterminateSystems/update-flake-lock@v20 From e7d6be531f1e4101aa808818159cfeb0db394a67 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 8 Sep 2023 17:22:12 +0000 Subject: [PATCH 217/268] chore(deps): update actions/cache action to v3.3.2 --- .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 e0434ce68..059795a11 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -164,7 +164,7 @@ jobs: - name: Retrieve ccache cache (Windows MinGW-w64) if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug' - uses: actions/cache@v3.3.1 + uses: actions/cache@v3.3.2 with: path: '${{ github.workspace }}\.ccache' key: ${{ matrix.os }}-mingw-w64-ccache-${{ github.run_id }} From 89e434bd5bedcd92bfa99102011607027be20060 Mon Sep 17 00:00:00 2001 From: seth Date: Sat, 9 Sep 2023 14:32:50 -0400 Subject: [PATCH 218/268] refactor(nix): don't concat final attr in fixed point Signed-off-by: seth --- nix/distribution.nix | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/nix/distribution.nix b/nix/distribution.nix index 6b93d355f..c8fa01c59 100644 --- a/nix/distribution.nix +++ b/nix/distribution.nix @@ -9,7 +9,7 @@ ... }: { packages = let - ourPackages = lib.fix (final: self.overlays.default ({inherit (pkgs) darwin;} // final) pkgs); + ourPackages = lib.fix (final: self.overlays.default final pkgs); in { inherit (ourPackages) @@ -29,16 +29,21 @@ # common args for prismlauncher evaluations unwrappedArgs = { inherit (inputs) libnbtplusplus; - inherit (final.darwin.apple_sdk.frameworks) Cocoa; + inherit ((final.darwin or prev.darwin).apple_sdk.frameworks) Cocoa; inherit self version; }; in { prismlauncher-qt5-unwrapped = prev.libsForQt5.callPackage ./pkg unwrappedArgs; + prismlauncher-qt5 = prev.libsForQt5.callPackage ./pkg/wrapper.nix { prismlauncher-unwrapped = final.prismlauncher-qt5-unwrapped; }; + prismlauncher-unwrapped = prev.qt6Packages.callPackage ./pkg unwrappedArgs; - prismlauncher = prev.qt6Packages.callPackage ./pkg/wrapper.nix {inherit (final) prismlauncher-unwrapped;}; + + prismlauncher = prev.qt6Packages.callPackage ./pkg/wrapper.nix { + inherit (final) prismlauncher-unwrapped; + }; }; }; } From 6aa821df9c5cb2cd79e03ddad043ab49acfdd5a3 Mon Sep 17 00:00:00 2001 From: seth Date: Sat, 9 Sep 2023 14:34:38 -0400 Subject: [PATCH 219/268] refactor(nix): match inputs value in flake Signed-off-by: seth --- flake.nix | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/flake.nix b/flake.nix index d45282aa6..a58d66fb9 100644 --- a/flake.nix +++ b/flake.nix @@ -20,12 +20,14 @@ }; }; - outputs = inputs: - inputs.flake-parts.lib.mkFlake - {inherit inputs;} - { + outputs = { + flake-parts, + pre-commit-hooks, + ... + } @ inputs: + flake-parts.lib.mkFlake {inherit inputs;} { imports = [ - inputs.pre-commit-hooks.flakeModule + pre-commit-hooks.flakeModule ./nix/dev.nix ./nix/distribution.nix From 10192c540b85999c9a5cd075d695d1a4298ff9e5 Mon Sep 17 00:00:00 2001 From: seth Date: Sat, 9 Sep 2023 15:10:15 -0400 Subject: [PATCH 220/268] feat(nix): add source filtering this - along with garnix - should mostly eliminate unneeded (re)builds Signed-off-by: seth --- flake.lock | 16 ++++++++++++++++ flake.nix | 1 + nix/distribution.nix | 18 +++++++++++++++++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/flake.lock b/flake.lock index b1486ea69..df6039cf4 100644 --- a/flake.lock +++ b/flake.lock @@ -89,6 +89,21 @@ "type": "github" } }, + "nix-filter": { + "locked": { + "lastModified": 1693833173, + "narHash": "sha256-hlMABKrGbEiJD5dwUSfnw1CQ3bG7KKwDV+Nx3bEZd7U=", + "owner": "numtide", + "repo": "nix-filter", + "rev": "ac030bd9ba98e318e1f4c4328d60766ade8ebe8b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "nix-filter", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1693626178, @@ -156,6 +171,7 @@ "flake-compat": "flake-compat", "flake-parts": "flake-parts", "libnbtplusplus": "libnbtplusplus", + "nix-filter": "nix-filter", "nixpkgs": "nixpkgs", "pre-commit-hooks": "pre-commit-hooks" } diff --git a/flake.nix b/flake.nix index a58d66fb9..afb0ec63a 100644 --- a/flake.nix +++ b/flake.nix @@ -4,6 +4,7 @@ inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; flake-parts.url = "github:hercules-ci/flake-parts"; + nix-filter.url = "github:numtide/nix-filter"; pre-commit-hooks = { url = "github:cachix/pre-commit-hooks.nix"; inputs.nixpkgs.follows = "nixpkgs"; diff --git a/nix/distribution.nix b/nix/distribution.nix index c8fa01c59..ca9999dcb 100644 --- a/nix/distribution.nix +++ b/nix/distribution.nix @@ -26,11 +26,27 @@ overlays.default = final: prev: let version = builtins.substring 0 8 self.lastModifiedDate or "dirty"; + filteredSelf = inputs.nix-filter.lib.filter { + root = ../.; + include = [ + "buildconfig" + "cmake" + "launcher" + "libraries" + "program_info" + "tests" + ../COPYING.md + ../CMakeLists.txt + ]; + }; + # common args for prismlauncher evaluations unwrappedArgs = { + self = filteredSelf; + inherit (inputs) libnbtplusplus; inherit ((final.darwin or prev.darwin).apple_sdk.frameworks) Cocoa; - inherit self version; + inherit version; }; in { prismlauncher-qt5-unwrapped = prev.libsForQt5.callPackage ./pkg unwrappedArgs; From 82a0a5bca12851afb276851569af8716e85e66c1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 10 Sep 2023 00:17:57 +0000 Subject: [PATCH 221/268] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:nixos/nixpkgs/bfb7dfec93f3b5d7274db109f2990bc889861caf' (2023-09-02) → 'github:nixos/nixpkgs/b200e0df08f80c32974a6108ce431d8a8a5e6547' (2023-09-07) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index b1486ea69..9290736b3 100644 --- a/flake.lock +++ b/flake.lock @@ -91,11 +91,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1693626178, - "narHash": "sha256-Rpiy6lIOu4zny8tfGuIeN1ji9eSz9nPmm9yBhh/4IOM=", + "lastModified": 1694062546, + "narHash": "sha256-PiGI4f2BGnZcedP6slLjCLGLRLXPa9+ogGGgVPfGxys=", "owner": "nixos", "repo": "nixpkgs", - "rev": "bfb7dfec93f3b5d7274db109f2990bc889861caf", + "rev": "b200e0df08f80c32974a6108ce431d8a8a5e6547", "type": "github" }, "original": { From 47d1f23568a6ea72d16b95ea6481996c64e0a9fe Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 10 Sep 2023 16:22:57 +0300 Subject: [PATCH 222/268] added side for modrinth mods Signed-off-by: Trial97 --- launcher/minecraft/mod/MetadataHandler.h | 1 + launcher/modplatform/ModIndex.h | 2 +- .../modrinth/ModrinthPackExportTask.cpp | 25 ++++++++++++---- .../modrinth/ModrinthPackExportTask.h | 1 + .../modrinth/ModrinthPackIndex.cpp | 16 ++++++++++ launcher/modplatform/packwiz/Packwiz.cpp | 29 +++++++++++++++++-- launcher/modplatform/packwiz/Packwiz.h | 7 +++-- tests/Packwiz_test.cpp | 4 +-- 8 files changed, 73 insertions(+), 12 deletions(-) diff --git a/launcher/minecraft/mod/MetadataHandler.h b/launcher/minecraft/mod/MetadataHandler.h index 88e9ff2b6..3496da2a0 100644 --- a/launcher/minecraft/mod/MetadataHandler.h +++ b/launcher/minecraft/mod/MetadataHandler.h @@ -31,6 +31,7 @@ class Mod; class Metadata { public: using ModStruct = Packwiz::V1::Mod; + using ModSide = Packwiz::V1::Side; static auto create(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> ModStruct { diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index cad217034..7036241db 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -104,6 +104,7 @@ struct IndexedPack { QString logoName; QString logoUrl; QString websiteUrl; + QString side; bool versionsLoaded = false; QVector versions; @@ -128,7 +129,6 @@ struct IndexedPack { return std::any_of(versions.constBegin(), versions.constEnd(), [](auto const& v) { return v.is_currently_selected; }); } }; -QString getMetaURL(ResourceProvider provider, QVariant projectID); struct OverrideDep { QString quilt; diff --git a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp index ad8fefac1..72f0bd6c4 100644 --- a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp @@ -25,6 +25,7 @@ #include "Json.h" #include "MMCZip.h" #include "minecraft/PackProfile.h" +#include "minecraft/mod/MetadataHandler.h" #include "minecraft/mod/ModFolderModel.h" const QStringList ModrinthPackExportTask::PREFIXES({ "mods/", "coremods/", "resourcepacks/", "texturepacks/", "shaderpacks/" }); @@ -127,7 +128,8 @@ void ModrinthPackExportTask::collectHashes() QCryptographicHash sha1(QCryptographicHash::Algorithm::Sha1); sha1.addData(data); - ResolvedFile resolvedFile{ sha1.result().toHex(), sha512.result().toHex(), url.toEncoded(), openFile.size() }; + ResolvedFile resolvedFile{ sha1.result().toHex(), sha512.result().toHex(), url.toEncoded(), openFile.size(), + mod->metadata()->side }; resolvedFiles[relative] = resolvedFile; // nice! we've managed to resolve based on local metadata! @@ -272,18 +274,31 @@ QByteArray ModrinthPackExportTask::generateIndex() // detect disabled mod const QFileInfo pathInfo(path); + + QJsonObject env; if (pathInfo.suffix() == "disabled") { // rename it path = pathInfo.dir().filePath(pathInfo.completeBaseName()); - // ...and make it optional - QJsonObject env; env["client"] = "optional"; env["server"] = "optional"; - fileOut["env"] = env; + } else { + env["client"] = "required"; + env["server"] = "required"; } + switch (iterator->side) { + case Metadata::ModSide::ClientSide: + env["server"] = "unsupported"; + break; + case Metadata::ModSide::ServerSide: + env["client"] = "unsupported"; + break; + case Metadata::ModSide::UniversalSide: + break; + } + fileOut["env"] = env; fileOut["path"] = path; - fileOut["downloads"] = QJsonArray{ iterator.value().url }; + fileOut["downloads"] = QJsonArray{ iterator->url }; QJsonObject hashes; hashes["sha1"] = value.sha1; diff --git a/launcher/modplatform/modrinth/ModrinthPackExportTask.h b/launcher/modplatform/modrinth/ModrinthPackExportTask.h index 1f9e0eb77..388b733e5 100644 --- a/launcher/modplatform/modrinth/ModrinthPackExportTask.h +++ b/launcher/modplatform/modrinth/ModrinthPackExportTask.h @@ -43,6 +43,7 @@ class ModrinthPackExportTask : public Task { struct ResolvedFile { QString sha1, sha512, url; qint64 size; + Metadata::ModSide side; }; static const QStringList PREFIXES; diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index 85e66a91e..9cc59ac39 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -27,6 +27,11 @@ static ModrinthAPI api; static ModPlatform::ProviderCapabilities ProviderCaps; +bool shouldDownloadOnSide(QString side) +{ + return side == "required" || side == "optional"; +} + // https://docs.modrinth.com/api-spec/#tag/projects/operation/getProject void Modrinth::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj) { @@ -53,6 +58,17 @@ void Modrinth::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj) modAuthor.url = api.getAuthorURL(modAuthor.name); pack.authors.append(modAuthor); + auto client = shouldDownloadOnSide(Json::ensureString(obj, "client_side")); + auto server = shouldDownloadOnSide(Json::ensureString(obj, "server_side")); + + if (server && client) { + pack.side = "both"; + } else if (server) { + pack.side = "server"; + } else if (client) { + pack.side = "client"; + } + // Modrinth can have more data than what's provided by the basic search :) pack.extraDataLoaded = false; } diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 1757da3e0..e35567f24 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -113,6 +113,7 @@ auto V1::createModFormat([[maybe_unused]] QDir& index_dir, ModPlatform::IndexedP mod.provider = mod_pack.provider; mod.file_id = mod_version.fileId; mod.project_id = mod_pack.addonId; + mod.side = stringToSide(mod_pack.side); return mod; } @@ -190,7 +191,7 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod) { auto tbl = toml::table{ { "name", mod.name.toStdString() }, { "filename", mod.filename.toStdString() }, - { "side", mod.side.toStdString() }, + { "side", sideToString(mod.side).toStdString() }, { "download", toml::table{ { "mode", mod.mode.toStdString() }, @@ -274,7 +275,7 @@ auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod { // Basic info mod.name = stringEntry(table, "name"); mod.filename = stringEntry(table, "filename"); - mod.side = stringEntry(table, "side"); + mod.side = stringToSide(stringEntry(table, "side")); } { // [download] info @@ -329,4 +330,28 @@ auto V1::getIndexForMod(QDir& index_dir, QVariant& mod_id) -> Mod return {}; } +auto V1::sideToString(Side side) -> QString +{ + switch (side) { + case Side::ClientSide: + return "client"; + case Side::ServerSide: + return "server"; + case Side::UniversalSide: + return "both"; + } + return {}; +} + +auto V1::stringToSide(QString side) -> Side +{ + if (side == "client") + return Side::ClientSide; + if (side == "server") + return Side::ServerSide; + if (side == "both") + return Side::UniversalSide; + return Side::UniversalSide; +} + } // namespace Packwiz diff --git a/launcher/modplatform/packwiz/Packwiz.h b/launcher/modplatform/packwiz/Packwiz.h index 7edc18cde..dce198b0e 100644 --- a/launcher/modplatform/packwiz/Packwiz.h +++ b/launcher/modplatform/packwiz/Packwiz.h @@ -35,12 +35,12 @@ auto getRealIndexName(QDir& index_dir, QString normalized_index_name, bool shoul class V1 { public: + enum class Side { ClientSide = 1 << 0, ServerSide = 1 << 1, UniversalSide = ClientSide | ServerSide }; struct Mod { QString slug{}; QString name{}; QString filename{}; - // FIXME: make side an enum - QString side{ "both" }; + Side side{ Side::UniversalSide }; // [download] QString mode{}; @@ -93,6 +93,9 @@ class V1 { * If the mod doesn't have a metadata, it simply returns an empty Mod object. * */ static auto getIndexForMod(QDir& index_dir, QVariant& mod_id) -> Mod; + + static auto sideToString(Side side) -> QString; + static auto stringToSide(QString side) -> Side; }; } // namespace Packwiz diff --git a/tests/Packwiz_test.cpp b/tests/Packwiz_test.cpp index d1b274d12..e4abda9f9 100644 --- a/tests/Packwiz_test.cpp +++ b/tests/Packwiz_test.cpp @@ -42,7 +42,7 @@ class PackwizTest : public QObject { QCOMPARE(metadata.name, "Borderless Mining"); QCOMPARE(metadata.filename, "borderless-mining-1.1.1+1.18.jar"); - QCOMPARE(metadata.side, "client"); + QCOMPARE(metadata.side, Packwiz::V1::Side::ClientSide); QCOMPARE(metadata.url, QUrl("https://cdn.modrinth.com/data/kYq5qkSL/versions/1.1.1+1.18/borderless-mining-1.1.1+1.18.jar")); QCOMPARE(metadata.hash_format, "sha512"); @@ -72,7 +72,7 @@ class PackwizTest : public QObject { QCOMPARE(metadata.name, "Screenshot to Clipboard (Fabric)"); QCOMPARE(metadata.filename, "screenshot-to-clipboard-1.0.7-fabric.jar"); - QCOMPARE(metadata.side, "both"); + QCOMPARE(metadata.side, Packwiz::V1::Side::UniversalSide); QCOMPARE(metadata.url, QUrl("https://edge.forgecdn.net/files/3509/43/screenshot-to-clipboard-1.0.7-fabric.jar")); QCOMPARE(metadata.hash_format, "murmur2"); From 9afe80b0e00d9520abe4d91a3075b019e85c80d7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 17 Sep 2023 00:18:04 +0000 Subject: [PATCH 223/268] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:nixos/nixpkgs/b200e0df08f80c32974a6108ce431d8a8a5e6547' (2023-09-07) → 'github:nixos/nixpkgs/46688f8eb5cd6f1298d873d4d2b9cf245e09e88e' (2023-09-15) • Updated input 'pre-commit-hooks': 'github:cachix/pre-commit-hooks.nix/7e3517c03d46159fdbf8c0e5c97f82d5d4b0c8fa' (2023-08-17) → 'github:cachix/pre-commit-hooks.nix/4f883a76282bc28eb952570afc3d8a1bf6f481d7' (2023-09-10) --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index 9290736b3..6fbcf0526 100644 --- a/flake.lock +++ b/flake.lock @@ -91,11 +91,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1694062546, - "narHash": "sha256-PiGI4f2BGnZcedP6slLjCLGLRLXPa9+ogGGgVPfGxys=", + "lastModified": 1694760568, + "narHash": "sha256-3G07BiXrp2YQKxdcdms22MUx6spc6A++MSePtatCYuI=", "owner": "nixos", "repo": "nixpkgs", - "rev": "b200e0df08f80c32974a6108ce431d8a8a5e6547", + "rev": "46688f8eb5cd6f1298d873d4d2b9cf245e09e88e", "type": "github" }, "original": { @@ -138,11 +138,11 @@ ] }, "locked": { - "lastModified": 1692274144, - "narHash": "sha256-BxTQuRUANQ81u8DJznQyPmRsg63t4Yc+0kcyq6OLz8s=", + "lastModified": 1694364351, + "narHash": "sha256-oadhSCqopYXxURwIA6/Anpe5IAG11q2LhvTJNP5zE6o=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "7e3517c03d46159fdbf8c0e5c97f82d5d4b0c8fa", + "rev": "4f883a76282bc28eb952570afc3d8a1bf6f481d7", "type": "github" }, "original": { From a49851cb40a0156edf6011b7318c632fa7483612 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 19 Sep 2023 15:48:12 +0300 Subject: [PATCH 224/268] updated blocked mods with empty hash Signed-off-by: Trial97 --- launcher/ui/dialogs/BlockedModsDialog.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/launcher/ui/dialogs/BlockedModsDialog.cpp b/launcher/ui/dialogs/BlockedModsDialog.cpp index f105b4b5f..790d545b6 100644 --- a/launcher/ui/dialogs/BlockedModsDialog.cpp +++ b/launcher/ui/dialogs/BlockedModsDialog.cpp @@ -334,6 +334,13 @@ bool BlockedModsDialog::checkValidPath(QString path) for (auto& mod : m_mods) { if (compare(filename, mod.name)) { + // if the mod is not yet matched and doesn't have a hash then + // just match it with the file that has the exact same name + if (!mod.matched && mod.hash.isEmpty()) { + mod.matched = true; + mod.localPath = path; + return false; + } qDebug() << "[Blocked Mods Dialog] Name match found:" << mod.name << "| From path:" << path; return true; } From 97ced1f459664949943b3f0ef424ea792be1c716 Mon Sep 17 00:00:00 2001 From: seth Date: Tue, 19 Sep 2023 13:45:12 -0400 Subject: [PATCH 225/268] fix(nix): include libusb1 as a runtime dependency Signed-off-by: seth --- nix/pkg/wrapper.nix | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nix/pkg/wrapper.nix b/nix/pkg/wrapper.nix index 8d160143d..8bc255e71 100644 --- a/nix/pkg/wrapper.nix +++ b/nix/pkg/wrapper.nix @@ -18,9 +18,11 @@ flite, mesa-demos, udev, + libusb1, msaClientID ? null, gamemodeSupport ? stdenv.isLinux, textToSpeechSupport ? stdenv.isLinux, + controllerSupport ? stdenv.isLinux, jdks ? [jdk17 jdk8], additionalLibs ? [], additionalPrograms ? [], @@ -71,6 +73,7 @@ in ] ++ lib.optional gamemodeSupport gamemode.lib ++ lib.optional textToSpeechSupport flite + ++ lib.optional controllerSupport libusb1 ++ additionalLibs; runtimePrograms = From 97da8892b9fcb285f7023a671090b4a3becdf2d8 Mon Sep 17 00:00:00 2001 From: seth Date: Tue, 19 Sep 2023 13:45:52 -0400 Subject: [PATCH 226/268] chore(nix): add meta.mainProgram attribute Signed-off-by: seth --- nix/pkg/default.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/nix/pkg/default.nix b/nix/pkg/default.nix index 074214c4b..fd19a0b3d 100644 --- a/nix/pkg/default.nix +++ b/nix/pkg/default.nix @@ -58,6 +58,7 @@ assert lib.assertMsg (stdenv.isLinux || !gamemodeSupport) "gamemodeSupport is on dontWrapQtApps = true; meta = with lib; { + mainProgram = "prismlauncher"; homepage = "https://prismlauncher.org/"; description = "A free, open source launcher for Minecraft"; longDescription = '' From 81a3ba18bc9740d543fe9aa569f74f37ec46c2cc Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Wed, 20 Sep 2023 18:45:23 +0300 Subject: [PATCH 227/268] Update launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp Co-authored-by: TheKodeToad Signed-off-by: Alexandru Ionut Tripon --- launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp index 874391735..bd1fe9401 100644 --- a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp +++ b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp @@ -256,7 +256,7 @@ QHash GetModDependenciesTask::getRequiredBy() { QHash rby; auto fullList = m_selected + m_pack_dependencies; - for (auto mod : fullList) { + for (auto& mod : fullList) { auto addonId = mod->pack->addonId; auto provider = mod->pack->provider; auto version = mod->version.fileId; From f3c089792a5342eeff93f032dca134fe371db4b3 Mon Sep 17 00:00:00 2001 From: bolli24 <4827765-bolli24@users.noreply.gitlab.com> Date: Fri, 22 Sep 2023 21:10:27 +0200 Subject: [PATCH 228/268] Skip folders when updating mods. Previously the mod updater would fail, reporting "The mod updater was aborted!", when trying to update a folder. Signed-off-by: bolli24 <4827765-bolli24@users.noreply.gitlab.com> --- launcher/ui/dialogs/ModUpdateDialog.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index 1f0fa7cd2..cd115bd3b 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -236,6 +236,11 @@ auto ModUpdateDialog::ensureMetadata() -> bool if (skip_rest) continue; + if (candidate->type() == ResourceType::FOLDER) { + m_failed_metadata.append({ candidate, tr("This is a folder.") }); + continue; + } + if (confirm_rest) { addToTmp(candidate, provider_rest); should_try_others.insert(candidate->internal_id(), try_others_rest); From fa65ac3ff71c27a17c5e1a6b1d75842b1c6e62ed Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 24 Sep 2023 00:18:03 +0000 Subject: [PATCH 229/268] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nix-filter': 'github:numtide/nix-filter/ac030bd9ba98e318e1f4c4328d60766ade8ebe8b' (2023-09-04) → 'github:numtide/nix-filter/41fd48e00c22b4ced525af521ead8792402de0ea' (2023-09-16) • Updated input 'nixpkgs': 'github:nixos/nixpkgs/46688f8eb5cd6f1298d873d4d2b9cf245e09e88e' (2023-09-15) → 'github:nixos/nixpkgs/e12483116b3b51a185a33a272bf351e357ba9a99' (2023-09-21) --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index 8096fcebc..f96336551 100644 --- a/flake.lock +++ b/flake.lock @@ -91,11 +91,11 @@ }, "nix-filter": { "locked": { - "lastModified": 1693833173, - "narHash": "sha256-hlMABKrGbEiJD5dwUSfnw1CQ3bG7KKwDV+Nx3bEZd7U=", + "lastModified": 1694857738, + "narHash": "sha256-bxxNyLHjhu0N8T3REINXQ2ZkJco0ABFPn6PIe2QUfqo=", "owner": "numtide", "repo": "nix-filter", - "rev": "ac030bd9ba98e318e1f4c4328d60766ade8ebe8b", + "rev": "41fd48e00c22b4ced525af521ead8792402de0ea", "type": "github" }, "original": { @@ -106,11 +106,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1694760568, - "narHash": "sha256-3G07BiXrp2YQKxdcdms22MUx6spc6A++MSePtatCYuI=", + "lastModified": 1695318763, + "narHash": "sha256-FHVPDRP2AfvsxAdc+AsgFJevMz5VBmnZglFUMlxBkcY=", "owner": "nixos", "repo": "nixpkgs", - "rev": "46688f8eb5cd6f1298d873d4d2b9cf245e09e88e", + "rev": "e12483116b3b51a185a33a272bf351e357ba9a99", "type": "github" }, "original": { From 14af7044be6bead9f29e3481d9c8760d738f317b Mon Sep 17 00:00:00 2001 From: bolli24 <9805065+bolli24@users.noreply.github.com> Date: Sun, 24 Sep 2023 03:01:47 +0200 Subject: [PATCH 230/268] Update launcher/ui/dialogs/ModUpdateDialog.cpp Co-authored-by: TheKodeToad Signed-off-by: bolli24 <9805065+bolli24@users.noreply.github.com> --- launcher/ui/dialogs/ModUpdateDialog.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index cd115bd3b..04173c88a 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -237,7 +237,6 @@ auto ModUpdateDialog::ensureMetadata() -> bool continue; if (candidate->type() == ResourceType::FOLDER) { - m_failed_metadata.append({ candidate, tr("This is a folder.") }); continue; } From 1684cff7acde56aba7c8db232a918d96e220be29 Mon Sep 17 00:00:00 2001 From: alexia Date: Mon, 25 Sep 2023 15:46:22 +0200 Subject: [PATCH 231/268] Update links to Minecraft Wiki Minecraft Wiki has officially moved from Fandom to their own wiki. I updated some links I found in comments. Signed-off-by: alexia --- launcher/minecraft/MinecraftInstance.cpp | 2 +- launcher/minecraft/mod/DataPack.cpp | 2 +- launcher/minecraft/mod/DataPack.h | 2 +- launcher/minecraft/mod/ResourcePack.cpp | 2 +- launcher/minecraft/mod/ResourcePack.h | 2 +- launcher/minecraft/mod/tasks/LocalDataPackParseTask.cpp | 2 +- launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp | 2 +- launcher/ui/widgets/InfoFrame.cpp | 4 ++-- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 0e64c46d4..86ef3b30e 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -311,7 +311,7 @@ QString MinecraftInstance::getLocalLibraryPath() const bool MinecraftInstance::supportsDemo() const { Version instance_ver{ getPackProfile()->getComponentVersion("net.minecraft") }; - // Demo mode was introduced in 1.3.1: https://minecraft.fandom.com/wiki/Demo_mode#History + // Demo mode was introduced in 1.3.1: https://minecraft.wiki/w/Demo_mode#History // FIXME: Due to Version constraints atm, this can't handle well non-release versions return instance_ver >= Version("1.3.1"); } diff --git a/launcher/minecraft/mod/DataPack.cpp b/launcher/minecraft/mod/DataPack.cpp index 7bf5a3112..fc2d3f68b 100644 --- a/launcher/minecraft/mod/DataPack.cpp +++ b/launcher/minecraft/mod/DataPack.cpp @@ -28,7 +28,7 @@ #include "Version.h" // Values taken from: -// https://minecraft.fandom.com/wiki/Tutorials/Creating_a_data_pack#%22pack_format%22 +// https://minecraft.wiki/w/Tutorials/Creating_a_data_pack#%22pack_format%22 static const QMap> s_pack_format_versions = { { 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") } }, diff --git a/launcher/minecraft/mod/DataPack.h b/launcher/minecraft/mod/DataPack.h index fc2703c7a..b3787b238 100644 --- a/launcher/minecraft/mod/DataPack.h +++ b/launcher/minecraft/mod/DataPack.h @@ -63,7 +63,7 @@ class DataPack : public Resource { mutable QMutex m_data_lock; /* The 'version' of a data pack, as defined in the pack.mcmeta file. - * See https://minecraft.fandom.com/wiki/Data_pack#pack.mcmeta + * See https://minecraft.wiki/w/Data_pack#pack.mcmeta */ int m_pack_format = 0; diff --git a/launcher/minecraft/mod/ResourcePack.cpp b/launcher/minecraft/mod/ResourcePack.cpp index 2bb51dc5b..074534405 100644 --- a/launcher/minecraft/mod/ResourcePack.cpp +++ b/launcher/minecraft/mod/ResourcePack.cpp @@ -11,7 +11,7 @@ #include "minecraft/mod/tasks/LocalResourcePackParseTask.h" // Values taken from: -// https://minecraft.fandom.com/wiki/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta +// https://minecraft.wiki/w/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta static const QMap> s_pack_format_versions = { { 1, { Version("1.6.1"), Version("1.8.9") } }, { 2, { Version("1.9"), Version("1.10.2") } }, { 3, { Version("1.11"), Version("1.12.2") } }, { 4, { Version("1.13"), Version("1.14.4") } }, diff --git a/launcher/minecraft/mod/ResourcePack.h b/launcher/minecraft/mod/ResourcePack.h index da354bc1c..c06f3793d 100644 --- a/launcher/minecraft/mod/ResourcePack.h +++ b/launcher/minecraft/mod/ResourcePack.h @@ -51,7 +51,7 @@ class ResourcePack : public Resource { mutable QMutex m_data_lock; /* The 'version' of a resource pack, as defined in the pack.mcmeta file. - * See https://minecraft.fandom.com/wiki/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta + * See https://minecraft.wiki/w/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta */ int m_pack_format = 0; diff --git a/launcher/minecraft/mod/tasks/LocalDataPackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalDataPackParseTask.cpp index 5bb448778..82f6b9df9 100644 --- a/launcher/minecraft/mod/tasks/LocalDataPackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalDataPackParseTask.cpp @@ -133,7 +133,7 @@ bool processZIP(DataPack& pack, ProcessingLevel level) return true; } -// https://minecraft.fandom.com/wiki/Data_pack#pack.mcmeta +// https://minecraft.wiki/w/Data_pack#pack.mcmeta bool processMCMeta(DataPack& pack, QByteArray&& raw_data) { try { diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 73cbf891c..7b9f4f594 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -178,7 +178,7 @@ bool processZIP(ResourcePack& pack, ProcessingLevel level) return true; } -// https://minecraft.fandom.com/wiki/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta +// https://minecraft.wiki/w/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) { try { diff --git a/launcher/ui/widgets/InfoFrame.cpp b/launcher/ui/widgets/InfoFrame.cpp index 1f03f9eaf..69f72fea2 100644 --- a/launcher/ui/widgets/InfoFrame.cpp +++ b/launcher/ui/widgets/InfoFrame.cpp @@ -158,12 +158,12 @@ QString InfoFrame::renderColorCodes(QString input) // // TODO: Wrap links inside tags - // https://minecraft.fandom.com/wiki/Formatting_codes#Color_codes + // https://minecraft.wiki/w/Formatting_codes#Color_codes const QMap color_codes_map = { { '0', "#000000" }, { '1', "#0000AA" }, { '2', "#00AA00" }, { '3', "#00AAAA" }, { '4', "#AA0000" }, { '5', "#AA00AA" }, { '6', "#FFAA00" }, { '7', "#AAAAAA" }, { '8', "#555555" }, { '9', "#5555FF" }, { 'a', "#55FF55" }, { 'b', "#55FFFF" }, { 'c', "#FF5555" }, { 'd', "#FF55FF" }, { 'e', "#FFFF55" }, { 'f', "#FFFFFF" } }; - // https://minecraft.fandom.com/wiki/Formatting_codes#Formatting_codes + // https://minecraft.wiki/w/Formatting_codes#Formatting_codes const QMap formatting_codes_map = { { 'l', "b" }, { 'm', "s" }, { 'n', "u" }, { 'o', "i" } }; QString html(""); From 7c636d46080443357d10471b00406c549687ee11 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 25 Sep 2023 17:44:04 +0300 Subject: [PATCH 232/268] Removed mojang Signed-off-by: Trial97 --- launcher/CMakeLists.txt | 13 - launcher/minecraft/MinecraftInstance.cpp | 3 - launcher/minecraft/auth/AccountData.cpp | 121 +------ launcher/minecraft/auth/AccountData.h | 14 +- launcher/minecraft/auth/AccountList.cpp | 50 +-- launcher/minecraft/auth/AccountList.h | 2 - launcher/minecraft/auth/AuthSession.h | 4 - launcher/minecraft/auth/MinecraftAccount.cpp | 40 +- launcher/minecraft/auth/MinecraftAccount.h | 19 - launcher/minecraft/auth/Yggdrasil.cpp | 342 ------------------ launcher/minecraft/auth/Yggdrasil.h | 92 ----- launcher/minecraft/auth/flows/AuthFlow.h | 1 - launcher/minecraft/auth/flows/Mojang.cpp | 22 -- launcher/minecraft/auth/flows/Mojang.h | 17 - .../auth/steps/MigrationEligibilityStep.cpp | 45 --- .../auth/steps/MigrationEligibilityStep.h | 21 -- .../auth/steps/MinecraftProfileStep.cpp | 9 - .../auth/steps/MinecraftProfileStepMojang.cpp | 87 ----- .../auth/steps/MinecraftProfileStepMojang.h | 21 -- .../minecraft/auth/steps/YggdrasilStep.cpp | 57 --- launcher/minecraft/auth/steps/YggdrasilStep.h | 28 -- launcher/ui/dialogs/LoginDialog.cpp | 115 ------ launcher/ui/dialogs/LoginDialog.h | 56 --- launcher/ui/dialogs/LoginDialog.ui | 77 ---- launcher/ui/pages/global/AccountListPage.cpp | 18 +- launcher/ui/pages/global/AccountListPage.h | 1 - launcher/ui/pages/global/AccountListPage.ui | 6 - 27 files changed, 5 insertions(+), 1276 deletions(-) delete mode 100644 launcher/minecraft/auth/Yggdrasil.cpp delete mode 100644 launcher/minecraft/auth/Yggdrasil.h delete mode 100644 launcher/minecraft/auth/flows/Mojang.cpp delete mode 100644 launcher/minecraft/auth/flows/Mojang.h delete mode 100644 launcher/minecraft/auth/steps/MigrationEligibilityStep.cpp delete mode 100644 launcher/minecraft/auth/steps/MigrationEligibilityStep.h delete mode 100644 launcher/minecraft/auth/steps/MinecraftProfileStepMojang.cpp delete mode 100644 launcher/minecraft/auth/steps/MinecraftProfileStepMojang.h delete mode 100644 launcher/minecraft/auth/steps/YggdrasilStep.cpp delete mode 100644 launcher/minecraft/auth/steps/YggdrasilStep.h delete mode 100644 launcher/ui/dialogs/LoginDialog.cpp delete mode 100644 launcher/ui/dialogs/LoginDialog.h delete mode 100644 launcher/ui/dialogs/LoginDialog.ui diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 18e0acab1..de750afde 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -216,13 +216,9 @@ set(MINECRAFT_SOURCES minecraft/auth/MinecraftAccount.h minecraft/auth/Parsers.cpp minecraft/auth/Parsers.h - minecraft/auth/Yggdrasil.cpp - minecraft/auth/Yggdrasil.h minecraft/auth/flows/AuthFlow.cpp minecraft/auth/flows/AuthFlow.h - minecraft/auth/flows/Mojang.cpp - minecraft/auth/flows/Mojang.h minecraft/auth/flows/MSA.cpp minecraft/auth/flows/MSA.h minecraft/auth/flows/Offline.cpp @@ -236,12 +232,8 @@ set(MINECRAFT_SOURCES minecraft/auth/steps/GetSkinStep.h minecraft/auth/steps/LauncherLoginStep.cpp minecraft/auth/steps/LauncherLoginStep.h - minecraft/auth/steps/MigrationEligibilityStep.cpp - minecraft/auth/steps/MigrationEligibilityStep.h minecraft/auth/steps/MinecraftProfileStep.cpp minecraft/auth/steps/MinecraftProfileStep.h - minecraft/auth/steps/MinecraftProfileStepMojang.cpp - minecraft/auth/steps/MinecraftProfileStepMojang.h minecraft/auth/steps/MSAStep.cpp minecraft/auth/steps/MSAStep.h minecraft/auth/steps/XboxAuthorizationStep.cpp @@ -250,8 +242,6 @@ set(MINECRAFT_SOURCES minecraft/auth/steps/XboxProfileStep.h minecraft/auth/steps/XboxUserStep.cpp minecraft/auth/steps/XboxUserStep.h - minecraft/auth/steps/YggdrasilStep.cpp - minecraft/auth/steps/YggdrasilStep.h minecraft/gameoptions/GameOptions.h minecraft/gameoptions/GameOptions.cpp @@ -944,8 +934,6 @@ SET(LAUNCHER_SOURCES ui/dialogs/IconPickerDialog.h ui/dialogs/ImportResourceDialog.cpp ui/dialogs/ImportResourceDialog.h - ui/dialogs/LoginDialog.cpp - ui/dialogs/LoginDialog.h ui/dialogs/MSALoginDialog.cpp ui/dialogs/MSALoginDialog.h ui/dialogs/OfflineLoginDialog.cpp @@ -1104,7 +1092,6 @@ qt_wrap_ui(LAUNCHER_UI ui/dialogs/MSALoginDialog.ui ui/dialogs/OfflineLoginDialog.ui ui/dialogs/AboutDialog.ui - ui/dialogs/LoginDialog.ui ui/dialogs/EditAccountDialog.ui ui/dialogs/ReviewMessageBox.ui ui/dialogs/ScrollMessageBox.ui diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 0e64c46d4..57bbcd404 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -856,9 +856,6 @@ QMap MinecraftInstance::createCensorFilterFromSession(AuthSess if (sessionRef.access_token != "0") { addToFilter(sessionRef.access_token, tr("")); } - if (sessionRef.client_token.size()) { - addToFilter(sessionRef.client_token, tr("")); - } addToFilter(sessionRef.uuid, tr("")); return filter; diff --git a/launcher/minecraft/auth/AccountData.cpp b/launcher/minecraft/auth/AccountData.cpp index 474bf7c6e..e1f1e9b1e 100644 --- a/launcher/minecraft/auth/AccountData.cpp +++ b/launcher/minecraft/auth/AccountData.cpp @@ -278,67 +278,6 @@ bool entitlementFromJSONV3(const QJsonObject& parent, MinecraftEntitlement& out) } // namespace -bool AccountData::resumeStateFromV2(QJsonObject data) -{ - // The JSON object must at least have a username for it to be valid. - if (!data.value("username").isString()) { - qCritical() << "Can't load Mojang account info from JSON object. Username field is missing or of the wrong type."; - return false; - } - - QString userName = data.value("username").toString(""); - QString clientToken = data.value("clientToken").toString(""); - QString accessToken = data.value("accessToken").toString(""); - - QJsonArray profileArray = data.value("profiles").toArray(); - if (profileArray.size() < 1) { - qCritical() << "Can't load Mojang account with username \"" << userName << "\". No profiles found."; - return false; - } - - struct AccountProfile { - QString id; - QString name; - bool legacy; - }; - - QList profiles; - int currentProfileIndex = 0; - int index = -1; - QString currentProfile = data.value("activeProfile").toString(""); - for (QJsonValue profileVal : profileArray) { - index++; - QJsonObject profileObject = profileVal.toObject(); - QString id = profileObject.value("id").toString(""); - QString name = profileObject.value("name").toString(""); - bool legacy_ = profileObject.value("legacy").toBool(false); - if (id.isEmpty() || name.isEmpty()) { - qWarning() << "Unable to load a profile" << name << "because it was missing an ID or a name."; - continue; - } - if (id == currentProfile) { - currentProfileIndex = index; - } - profiles.append({ id, name, legacy_ }); - } - auto& profile = profiles[currentProfileIndex]; - - type = AccountType::Mojang; - legacy = profile.legacy; - - minecraftProfile.id = profile.id; - minecraftProfile.name = profile.name; - minecraftProfile.validity = Katabasis::Validity::Assumed; - - yggdrasilToken.token = accessToken; - yggdrasilToken.extra["clientToken"] = clientToken; - yggdrasilToken.extra["userName"] = userName; - yggdrasilToken.validity = Katabasis::Validity::Assumed; - - validity_ = minecraftProfile.validity; - return true; -} - bool AccountData::resumeStateFromV3(QJsonObject data) { auto typeV = data.value("type"); @@ -349,8 +288,6 @@ bool AccountData::resumeStateFromV3(QJsonObject data) auto typeS = typeV.toString(); if (typeS == "MSA") { type = AccountType::MSA; - } else if (typeS == "Mojang") { - type = AccountType::Mojang; } else if (typeS == "Offline") { type = AccountType::Offline; } else { @@ -358,11 +295,6 @@ bool AccountData::resumeStateFromV3(QJsonObject data) return false; } - if (type == AccountType::Mojang) { - legacy = data.value("legacy").toBool(false); - canMigrateToMSA = data.value("canMigrateToMSA").toBool(false); - } - if (type == AccountType::MSA) { auto clientIDV = data.value("msa-client-id"); if (clientIDV.isString()) { @@ -395,15 +327,7 @@ bool AccountData::resumeStateFromV3(QJsonObject data) QJsonObject AccountData::saveState() const { QJsonObject output; - if (type == AccountType::Mojang) { - output["type"] = "Mojang"; - if (legacy) { - output["legacy"] = true; - } - if (canMigrateToMSA) { - output["canMigrateToMSA"] = true; - } - } else if (type == AccountType::MSA) { + if (type == AccountType::MSA) { output["type"] = "MSA"; output["msa-client-id"] = msaClientID; tokenToJSONV3(output, msaToken, "msa"); @@ -420,51 +344,11 @@ QJsonObject AccountData::saveState() const return output; } -QString AccountData::userName() const -{ - if (type == AccountType::MSA) { - return QString(); - } - return yggdrasilToken.extra["userName"].toString(); -} - QString AccountData::accessToken() const { return yggdrasilToken.token; } -QString AccountData::clientToken() const -{ - if (type != AccountType::Mojang) { - return QString(); - } - return yggdrasilToken.extra["clientToken"].toString(); -} - -void AccountData::setClientToken(QString clientToken) -{ - if (type != AccountType::Mojang) { - return; - } - yggdrasilToken.extra["clientToken"] = clientToken; -} - -void AccountData::generateClientTokenIfMissing() -{ - if (yggdrasilToken.extra.contains("clientToken")) { - return; - } - invalidateClientToken(); -} - -void AccountData::invalidateClientToken() -{ - if (type != AccountType::Mojang) { - return; - } - yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegularExpression("[{-}]")); -} - QString AccountData::profileId() const { return minecraftProfile.id; @@ -482,9 +366,6 @@ QString AccountData::profileName() const QString AccountData::accountDisplayString() const { switch (type) { - case AccountType::Mojang: { - return userName(); - } case AccountType::Offline: { return QObject::tr(""); } diff --git a/launcher/minecraft/auth/AccountData.h b/launcher/minecraft/auth/AccountData.h index 9b626c34e..bac77e17f 100644 --- a/launcher/minecraft/auth/AccountData.h +++ b/launcher/minecraft/auth/AccountData.h @@ -71,27 +71,17 @@ struct MinecraftProfile { Katabasis::Validity validity = Katabasis::Validity::None; }; -enum class AccountType { MSA, Mojang, Offline }; +enum class AccountType { MSA, Offline }; enum class AccountState { Unchecked, Offline, Working, Online, Disabled, Errored, Expired, Gone }; struct AccountData { QJsonObject saveState() const; - bool resumeStateFromV2(QJsonObject data); bool resumeStateFromV3(QJsonObject data); //! userName for Mojang accounts, gamertag for MSA QString accountDisplayString() const; - //! Only valid for Mojang accounts. MSA does not preserve this information - QString userName() const; - - //! Only valid for Mojang accounts. - QString clientToken() const; - void setClientToken(QString clientToken); - void invalidateClientToken(); - void generateClientTokenIfMissing(); - //! Yggdrasil access token, as passed to the game. QString accessToken() const; @@ -101,8 +91,6 @@ struct AccountData { QString lastError() const; AccountType type = AccountType::MSA; - bool legacy = false; - bool canMigrateToMSA = false; QString msaClientID; Katabasis::Token msaToken; diff --git a/launcher/minecraft/auth/AccountList.cpp b/launcher/minecraft/auth/AccountList.cpp index 84dbd8416..a29de4ecf 100644 --- a/launcher/minecraft/auth/AccountList.cpp +++ b/launcher/minecraft/auth/AccountList.cpp @@ -54,7 +54,7 @@ #include -enum AccountListVersion { MojangOnly = 2, MojangMSA = 3 }; +enum AccountListVersion { MojangMSA = 3 }; AccountList::AccountList(QObject* parent) : QAbstractListModel(parent) { @@ -320,17 +320,6 @@ QVariant AccountList::data(const QModelIndex& index, int role) const } } - case MigrationColumn: { - if (account->isMSA() || account->isOffline()) { - return tr("N/A", "Can Migrate"); - } - if (account->canMigrate()) { - return tr("Yes", "Can Migrate"); - } else { - return tr("No", "Can Migrate"); - } - } - default: return QVariant(); } @@ -366,8 +355,6 @@ QVariant AccountList::headerData(int section, [[maybe_unused]] Qt::Orientation o return tr("Type"); case StatusColumn: return tr("Status"); - case MigrationColumn: - return tr("Can Migrate?"); default: return QVariant(); } @@ -382,8 +369,6 @@ QVariant AccountList::headerData(int section, [[maybe_unused]] Qt::Orientation o return tr("Type of the account - Mojang or MSA."); case StatusColumn: return tr("Current status of the account."); - case MigrationColumn: - return tr("Can this account migrate to a Microsoft account?"); default: return QVariant(); } @@ -473,9 +458,6 @@ bool AccountList::loadList() // Make sure the format version matches. auto listVersion = root.value("formatVersion").toVariant().toInt(); switch (listVersion) { - case AccountListVersion::MojangOnly: { - return loadV2(root); - } break; case AccountListVersion::MojangMSA: { return loadV3(root); } break; @@ -489,36 +471,6 @@ bool AccountList::loadList() } } -bool AccountList::loadV2(QJsonObject& root) -{ - beginResetModel(); - auto defaultUserName = root.value("activeAccount").toString(""); - QJsonArray accounts = root.value("accounts").toArray(); - for (QJsonValue accountVal : accounts) { - QJsonObject accountObj = accountVal.toObject(); - MinecraftAccountPtr account = MinecraftAccount::loadFromJsonV2(accountObj); - if (account.get() != nullptr) { - auto profileId = account->profileId(); - if (!profileId.size()) { - continue; - } - if (findAccountByProfileId(profileId) != -1) { - continue; - } - connect(account.get(), &MinecraftAccount::changed, this, &AccountList::accountChanged); - connect(account.get(), &MinecraftAccount::activityChanged, this, &AccountList::accountActivityChanged); - m_accounts.append(account); - if (defaultUserName.size() && account->mojangUserName() == defaultUserName) { - m_defaultAccount = account; - } - } else { - qWarning() << "Failed to load an account."; - } - } - endResetModel(); - return true; -} - bool AccountList::loadV3(QJsonObject& root) { beginResetModel(); diff --git a/launcher/minecraft/auth/AccountList.h b/launcher/minecraft/auth/AccountList.h index 6a0b01916..051d8f958 100644 --- a/launcher/minecraft/auth/AccountList.h +++ b/launcher/minecraft/auth/AccountList.h @@ -55,7 +55,6 @@ class AccountList : public QAbstractListModel { // TODO: Add icon column. ProfileNameColumn = 0, NameColumn, - MigrationColumn, TypeColumn, StatusColumn, @@ -97,7 +96,6 @@ class AccountList : public QAbstractListModel { void setListFilePath(QString path, bool autosave = false); bool loadList(); - bool loadV2(QJsonObject& root); bool loadV3(QJsonObject& root); bool saveList(); diff --git a/launcher/minecraft/auth/AuthSession.h b/launcher/minecraft/auth/AuthSession.h index 40519476d..074b2d6e3 100644 --- a/launcher/minecraft/auth/AuthSession.h +++ b/launcher/minecraft/auth/AuthSession.h @@ -24,10 +24,6 @@ struct AuthSession { GoneOrMigrated } status = Undetermined; - // client token - QString client_token; - // account user name - QString username; // combined session ID QString session; // volatile auth token diff --git a/launcher/minecraft/auth/MinecraftAccount.cpp b/launcher/minecraft/auth/MinecraftAccount.cpp index 6c2f0805f..545d06aed 100644 --- a/launcher/minecraft/auth/MinecraftAccount.cpp +++ b/launcher/minecraft/auth/MinecraftAccount.cpp @@ -51,7 +51,6 @@ #include #include "flows/MSA.h" -#include "flows/Mojang.h" #include "flows/Offline.h" MinecraftAccount::MinecraftAccount(QObject* parent) : QObject(parent) @@ -59,15 +58,6 @@ MinecraftAccount::MinecraftAccount(QObject* parent) : QObject(parent) data.internalId = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]")); } -MinecraftAccountPtr MinecraftAccount::loadFromJsonV2(const QJsonObject& json) -{ - MinecraftAccountPtr account(new MinecraftAccount()); - if (account->data.resumeStateFromV2(json)) { - return account; - } - return nullptr; -} - MinecraftAccountPtr MinecraftAccount::loadFromJsonV3(const QJsonObject& json) { MinecraftAccountPtr account(new MinecraftAccount()); @@ -77,15 +67,6 @@ MinecraftAccountPtr MinecraftAccount::loadFromJsonV3(const QJsonObject& json) return nullptr; } -MinecraftAccountPtr MinecraftAccount::createFromUsername(const QString& username) -{ - auto account = makeShared(); - account->data.type = AccountType::Mojang; - account->data.yggdrasilToken.extra["userName"] = username; - account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]")); - return account; -} - MinecraftAccountPtr MinecraftAccount::createBlankMSA() { MinecraftAccountPtr account(new MinecraftAccount()); @@ -138,18 +119,6 @@ QPixmap MinecraftAccount::getFace() const return skin.scaled(64, 64, Qt::KeepAspectRatio); } -shared_qobject_ptr MinecraftAccount::login(QString password) -{ - Q_ASSERT(m_currentTask.get() == nullptr); - - m_currentTask.reset(new MojangLogin(&data, password)); - connect(m_currentTask.get(), &Task::succeeded, this, &MinecraftAccount::authSucceeded); - connect(m_currentTask.get(), &Task::failed, this, &MinecraftAccount::authFailed); - connect(m_currentTask.get(), &Task::aborted, this, [this] { authFailed(tr("Aborted")); }); - emit activityChanged(true); - return m_currentTask; -} - shared_qobject_ptr MinecraftAccount::loginMSA() { Q_ASSERT(m_currentTask.get() == nullptr); @@ -182,10 +151,8 @@ shared_qobject_ptr MinecraftAccount::refresh() if (data.type == AccountType::MSA) { m_currentTask.reset(new MSASilent(&data)); - } else if (data.type == AccountType::Offline) { - m_currentTask.reset(new OfflineRefresh(&data)); } else { - m_currentTask.reset(new MojangRefresh(&data)); + m_currentTask.reset(new OfflineRefresh(&data)); } connect(m_currentTask.get(), &Task::succeeded, this, &MinecraftAccount::authSucceeded); @@ -296,13 +263,8 @@ void MinecraftAccount::fillSession(AuthSessionPtr session) } } - // the user name. you have to have an user name - // FIXME: not with MSA - session->username = data.userName(); // volatile auth token session->access_token = data.accessToken(); - // the semi-permanent client token - session->client_token = data.clientToken(); // profile name session->player_name = data.profileName(); // profile ID diff --git a/launcher/minecraft/auth/MinecraftAccount.h b/launcher/minecraft/auth/MinecraftAccount.h index f04f947fa..910f4a28a 100644 --- a/launcher/minecraft/auth/MinecraftAccount.h +++ b/launcher/minecraft/auth/MinecraftAccount.h @@ -85,13 +85,10 @@ class MinecraftAccount : public QObject, public Usable { //! Default constructor explicit MinecraftAccount(QObject* parent = 0); - static MinecraftAccountPtr createFromUsername(const QString& username); - static MinecraftAccountPtr createBlankMSA(); static MinecraftAccountPtr createOffline(const QString& username); - static MinecraftAccountPtr loadFromJsonV2(const QJsonObject& json); static MinecraftAccountPtr loadFromJsonV3(const QJsonObject& json); static QUuid uuidFromUsername(QString username); @@ -100,12 +97,6 @@ class MinecraftAccount : public QObject, public Usable { QJsonObject saveToJson() const; public: /* manipulation */ - /** - * Attempt to login. Empty password means we use the token. - * If the attempt fails because we already are performing some task, it returns false. - */ - shared_qobject_ptr login(QString password); - shared_qobject_ptr loginMSA(); shared_qobject_ptr loginOffline(); @@ -119,8 +110,6 @@ class MinecraftAccount : public QObject, public Usable { QString accountDisplayString() const { return data.accountDisplayString(); } - QString mojangUserName() const { return data.userName(); } - QString accessToken() const { return data.accessToken(); } QString profileId() const { return data.profileId(); } @@ -129,8 +118,6 @@ class MinecraftAccount : public QObject, public Usable { bool isActive() const; - bool canMigrate() const { return data.canMigrateToMSA; } - bool isMSA() const { return data.type == AccountType::MSA; } bool isOffline() const { return data.type == AccountType::Offline; } @@ -142,12 +129,6 @@ class MinecraftAccount : public QObject, public Usable { QString typeString() const { switch (data.type) { - case AccountType::Mojang: { - if (data.legacy) { - return "legacy"; - } - return "mojang"; - } break; case AccountType::MSA: { return "msa"; } break; diff --git a/launcher/minecraft/auth/Yggdrasil.cpp b/launcher/minecraft/auth/Yggdrasil.cpp deleted file mode 100644 index 97f2a78d4..000000000 --- a/launcher/minecraft/auth/Yggdrasil.cpp +++ /dev/null @@ -1,342 +0,0 @@ -/* Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Yggdrasil.h" -#include "AccountData.h" - -#include -#include -#include -#include -#include -#include - -#include - -#include "Application.h" - -Yggdrasil::Yggdrasil(AccountData* data, QObject* parent) : AccountTask(data, parent) -{ - changeState(AccountTaskState::STATE_CREATED); -} - -void Yggdrasil::sendRequest(QUrl endpoint, QByteArray content) -{ - changeState(AccountTaskState::STATE_WORKING); - - QNetworkRequest netRequest(endpoint); - netRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - m_netReply = APPLICATION->network()->post(netRequest, content); - connect(m_netReply, &QNetworkReply::finished, this, &Yggdrasil::processReply); - connect(m_netReply, &QNetworkReply::uploadProgress, this, &Yggdrasil::refreshTimers); - connect(m_netReply, &QNetworkReply::downloadProgress, this, &Yggdrasil::refreshTimers); - connect(m_netReply, &QNetworkReply::sslErrors, this, &Yggdrasil::sslErrors); - timeout_keeper.setSingleShot(true); - timeout_keeper.start(timeout_max); - counter.setSingleShot(false); - counter.start(time_step); - progress(0, timeout_max); - connect(&timeout_keeper, &QTimer::timeout, this, &Yggdrasil::abortByTimeout); - connect(&counter, &QTimer::timeout, this, &Yggdrasil::heartbeat); -} - -void Yggdrasil::executeTask() {} - -void Yggdrasil::refresh() -{ - start(); - /* - * { - * "clientToken": "client identifier" - * "accessToken": "current access token to be refreshed" - * "selectedProfile": // specifying this causes errors - * { - * "id": "profile ID" - * "name": "profile name" - * } - * "requestUser": true/false // request the user structure - * } - */ - QJsonObject req; - req.insert("clientToken", m_data->clientToken()); - req.insert("accessToken", m_data->accessToken()); - /* - { - auto currentProfile = m_account->currentProfile(); - QJsonObject profile; - profile.insert("id", currentProfile->id()); - profile.insert("name", currentProfile->name()); - req.insert("selectedProfile", profile); - } - */ - req.insert("requestUser", false); - QJsonDocument doc(req); - - QUrl reqUrl("https://authserver.mojang.com/refresh"); - QByteArray requestData = doc.toJson(); - - sendRequest(reqUrl, requestData); -} - -void Yggdrasil::login(QString password) -{ - start(); - /* - * { - * "agent": { // optional - * "name": "Minecraft", // So far this is the only encountered value - * "version": 1 // This number might be increased - * // by the vanilla client in the future - * }, - * "username": "mojang account name", // Can be an email address or player name for - * // unmigrated accounts - * "password": "mojang account password", - * "clientToken": "client identifier", // optional - * "requestUser": true/false // request the user structure - * } - */ - QJsonObject req; - - { - QJsonObject agent; - // C++ makes string literals void* for some stupid reason, so we have to tell it - // QString... Thanks Obama. - agent.insert("name", QString("Minecraft")); - agent.insert("version", 1); - req.insert("agent", agent); - } - - req.insert("username", m_data->userName()); - req.insert("password", password); - req.insert("requestUser", false); - - // If we already have a client token, give it to the server. - // Otherwise, let the server give us one. - - m_data->generateClientTokenIfMissing(); - req.insert("clientToken", m_data->clientToken()); - - QJsonDocument doc(req); - - QUrl reqUrl("https://authserver.mojang.com/authenticate"); - QNetworkRequest netRequest(reqUrl); - QByteArray requestData = doc.toJson(); - - sendRequest(reqUrl, requestData); -} - -void Yggdrasil::refreshTimers(qint64, qint64) -{ - timeout_keeper.stop(); - timeout_keeper.start(timeout_max); - progress(count = 0, timeout_max); -} - -void Yggdrasil::heartbeat() -{ - count += time_step; - progress(count, timeout_max); -} - -bool Yggdrasil::abort() -{ - progress(timeout_max, timeout_max); - // TODO: actually use this in a meaningful way - m_aborted = Yggdrasil::BY_USER; - m_netReply->abort(); - return true; -} - -void Yggdrasil::abortByTimeout() -{ - progress(timeout_max, timeout_max); - // TODO: actually use this in a meaningful way - m_aborted = Yggdrasil::BY_TIMEOUT; - m_netReply->abort(); -} - -void Yggdrasil::sslErrors(QList errors) -{ - int i = 1; - for (auto error : errors) { - qCritical() << "LOGIN SSL Error #" << i << " : " << error.errorString(); - auto cert = error.certificate(); - qCritical() << "Certificate in question:\n" << cert.toText(); - i++; - } -} - -void Yggdrasil::processResponse(QJsonObject responseData) -{ - // Read the response data. We need to get the client token, access token, and the selected - // profile. - qDebug() << "Processing authentication response."; - - // qDebug() << responseData; - // If we already have a client token, make sure the one the server gave us matches our - // existing one. - QString clientToken = responseData.value("clientToken").toString(""); - if (clientToken.isEmpty()) { - // Fail if the server gave us an empty client token - changeState(AccountTaskState::STATE_FAILED_HARD, tr("Authentication server didn't send a client token.")); - return; - } - if (m_data->clientToken().isEmpty()) { - m_data->setClientToken(clientToken); - } else if (clientToken != m_data->clientToken()) { - changeState(AccountTaskState::STATE_FAILED_HARD, - tr("Authentication server attempted to change the client token. This isn't supported.")); - return; - } - - // Now, we set the access token. - qDebug() << "Getting access token."; - QString accessToken = responseData.value("accessToken").toString(""); - if (accessToken.isEmpty()) { - // Fail if the server didn't give us an access token. - changeState(AccountTaskState::STATE_FAILED_HARD, tr("Authentication server didn't send an access token.")); - return; - } - // Set the access token. - m_data->yggdrasilToken.token = accessToken; - m_data->yggdrasilToken.validity = Katabasis::Validity::Certain; - m_data->yggdrasilToken.issueInstant = QDateTime::currentDateTimeUtc(); - - // Get UUID here since we need it for later - auto profile = responseData.value("selectedProfile"); - if (!profile.isObject()) { - changeState(AccountTaskState::STATE_FAILED_HARD, tr("Authentication server didn't send a selected profile.")); - return; - } - - auto profileObj = profile.toObject(); - for (auto i = profileObj.constBegin(); i != profileObj.constEnd(); ++i) { - if (i.key() == "name" && i.value().isString()) { - m_data->minecraftProfile.name = i->toString(); - } else if (i.key() == "id" && i.value().isString()) { - m_data->minecraftProfile.id = i->toString(); - } - } - - if (m_data->minecraftProfile.id.isEmpty()) { - changeState(AccountTaskState::STATE_FAILED_HARD, tr("Authentication server didn't send a UUID in selected profile.")); - return; - } - - // We've made it through the minefield of possible errors. Return true to indicate that - // we've succeeded. - qDebug() << "Finished reading authentication response."; - changeState(AccountTaskState::STATE_SUCCEEDED); -} - -void Yggdrasil::processReply() -{ - changeState(AccountTaskState::STATE_WORKING); - - switch (m_netReply->error()) { - case QNetworkReply::NoError: - break; - case QNetworkReply::TimeoutError: - changeState(AccountTaskState::STATE_FAILED_SOFT, tr("Authentication operation timed out.")); - return; - case QNetworkReply::OperationCanceledError: - changeState(AccountTaskState::STATE_FAILED_SOFT, tr("Authentication operation cancelled.")); - return; - case QNetworkReply::SslHandshakeFailedError: - changeState(AccountTaskState::STATE_FAILED_SOFT, - tr("SSL Handshake failed.
There might be a few causes for it:
" - "
    " - "
  • You use Windows and need to update your root certificates, please install any outstanding updates.
  • " - "
  • Some device on your network is interfering with SSL traffic. In that case, " - "you have bigger worries than Minecraft not starting.
  • " - "
  • Possibly something else. Check the log file for details
  • " - "
")); - return; - // used for invalid credentials and similar errors. Fall through. - case QNetworkReply::ContentAccessDenied: - case QNetworkReply::ContentOperationNotPermittedError: - break; - case QNetworkReply::ContentGoneError: { - changeState(AccountTaskState::STATE_FAILED_GONE, - tr("The Mojang account no longer exists. It may have been migrated to a Microsoft account.")); - return; - } - default: - changeState(AccountTaskState::STATE_FAILED_SOFT, tr("Authentication operation failed due to a network error: %1 (%2)") - .arg(m_netReply->errorString()) - .arg(m_netReply->error())); - return; - } - - // Try to parse the response regardless of the response code. - // Sometimes the auth server will give more information and an error code. - QJsonParseError jsonError; - QByteArray replyData = m_netReply->readAll(); - QJsonDocument doc = QJsonDocument::fromJson(replyData, &jsonError); - // Check the response code. - int responseCode = m_netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - - if (responseCode == 200) { - // If the response code was 200, then there shouldn't be an error. Make sure - // anyways. - // Also, sometimes an empty reply indicates success. If there was no data received, - // pass an empty json object to the processResponse function. - if (jsonError.error == QJsonParseError::NoError || replyData.size() == 0) { - processResponse(replyData.size() > 0 ? doc.object() : QJsonObject()); - return; - } else { - changeState(AccountTaskState::STATE_FAILED_SOFT, - tr("Failed to parse authentication server response JSON response: %1 at offset %2.") - .arg(jsonError.errorString()) - .arg(jsonError.offset)); - qCritical() << replyData; - } - return; - } - - // If the response code was not 200, then Yggdrasil may have given us information - // about the error. - // If we can parse the response, then get information from it. Otherwise just say - // there was an unknown error. - if (jsonError.error == QJsonParseError::NoError) { - // We were able to parse the server's response. Woo! - // Call processError. If a subclass has overridden it then they'll handle their - // stuff there. - qDebug() << "The request failed, but the server gave us an error message. Processing error."; - processError(doc.object()); - } else { - // The server didn't say anything regarding the error. Give the user an unknown - // error. - qDebug() << "The request failed and the server gave no error message. Unknown error."; - changeState( - AccountTaskState::STATE_FAILED_SOFT, - tr("An unknown error occurred when trying to communicate with the authentication server: %1").arg(m_netReply->errorString())); - } -} - -void Yggdrasil::processError(QJsonObject responseData) -{ - QJsonValue errorVal = responseData.value("error"); - QJsonValue errorMessageValue = responseData.value("errorMessage"); - QJsonValue causeVal = responseData.value("cause"); - - if (errorVal.isString() && errorMessageValue.isString()) { - m_error = std::shared_ptr(new Error{ errorVal.toString(""), errorMessageValue.toString(""), causeVal.toString("") }); - changeState(AccountTaskState::STATE_FAILED_HARD, m_error->m_errorMessageVerbose); - } else { - // Error is not in standard format. Don't set m_error and return unknown error. - changeState(AccountTaskState::STATE_FAILED_HARD, tr("An unknown Yggdrasil error occurred.")); - } -} diff --git a/launcher/minecraft/auth/Yggdrasil.h b/launcher/minecraft/auth/Yggdrasil.h deleted file mode 100644 index 560d7fb81..000000000 --- a/launcher/minecraft/auth/Yggdrasil.h +++ /dev/null @@ -1,92 +0,0 @@ -/* Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "AccountTask.h" - -#include -#include -#include -#include - -#include "MinecraftAccount.h" - -class QNetworkAccessManager; -class QNetworkReply; - -/** - * A Yggdrasil task is a task that performs an operation on a given mojang account. - */ -class Yggdrasil : public AccountTask { - Q_OBJECT - public: - explicit Yggdrasil(AccountData* data, QObject* parent = 0); - virtual ~Yggdrasil() = default; - - void refresh(); - void login(QString password); - - struct Error { - QString m_errorMessageShort; - QString m_errorMessageVerbose; - QString m_cause; - }; - std::shared_ptr m_error; - - enum AbortedBy { BY_NOTHING, BY_USER, BY_TIMEOUT } m_aborted = BY_NOTHING; - - protected: - void executeTask() override; - - /** - * Processes the response received from the server. - * If an error occurred, this should emit a failed signal. - * If Yggdrasil gave an error response, it should call setError() first, and then return false. - * Otherwise, it should return true. - * Note: If the response from the server was blank, and the HTTP code was 200, this function is called with - * an empty QJsonObject. - */ - void processResponse(QJsonObject responseData); - - /** - * Processes an error response received from the server. - * The default implementation will read data from Yggdrasil's standard error response format and set it as this task's Error. - * \returns a QString error message that will be passed to emitFailed. - */ - virtual void processError(QJsonObject responseData); - - protected slots: - void processReply(); - void refreshTimers(qint64, qint64); - void heartbeat(); - void sslErrors(QList); - void abortByTimeout(); - - public slots: - virtual bool abort() override; - - private: - void sendRequest(QUrl endpoint, QByteArray content); - - protected: - QNetworkReply* m_netReply = nullptr; - QTimer timeout_keeper; - QTimer counter; - int count = 0; // num msec since time reset - - const int timeout_max = 30000; - const int time_step = 50; -}; diff --git a/launcher/minecraft/auth/flows/AuthFlow.h b/launcher/minecraft/auth/flows/AuthFlow.h index c2c412abc..e39e926dd 100644 --- a/launcher/minecraft/auth/flows/AuthFlow.h +++ b/launcher/minecraft/auth/flows/AuthFlow.h @@ -12,7 +12,6 @@ #include "minecraft/auth/AccountData.h" #include "minecraft/auth/AccountTask.h" #include "minecraft/auth/AuthStep.h" -#include "minecraft/auth/Yggdrasil.h" class AuthFlow : public AccountTask { Q_OBJECT diff --git a/launcher/minecraft/auth/flows/Mojang.cpp b/launcher/minecraft/auth/flows/Mojang.cpp deleted file mode 100644 index 7e2db16fa..000000000 --- a/launcher/minecraft/auth/flows/Mojang.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "Mojang.h" - -#include "minecraft/auth/steps/GetSkinStep.h" -#include "minecraft/auth/steps/MigrationEligibilityStep.h" -#include "minecraft/auth/steps/MinecraftProfileStepMojang.h" -#include "minecraft/auth/steps/YggdrasilStep.h" - -MojangRefresh::MojangRefresh(AccountData* data, QObject* parent) : AuthFlow(data, parent) -{ - m_steps.append(makeShared(m_data, QString())); - m_steps.append(makeShared(m_data)); - m_steps.append(makeShared(m_data)); - m_steps.append(makeShared(m_data)); -} - -MojangLogin::MojangLogin(AccountData* data, QString password, QObject* parent) : AuthFlow(data, parent), m_password(password) -{ - m_steps.append(makeShared(m_data, m_password)); - m_steps.append(makeShared(m_data)); - m_steps.append(makeShared(m_data)); - m_steps.append(makeShared(m_data)); -} diff --git a/launcher/minecraft/auth/flows/Mojang.h b/launcher/minecraft/auth/flows/Mojang.h deleted file mode 100644 index 779ca7e34..000000000 --- a/launcher/minecraft/auth/flows/Mojang.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include "AuthFlow.h" - -class MojangRefresh : public AuthFlow { - Q_OBJECT - public: - explicit MojangRefresh(AccountData* data, QObject* parent = 0); -}; - -class MojangLogin : public AuthFlow { - Q_OBJECT - public: - explicit MojangLogin(AccountData* data, QString password, QObject* parent = 0); - - private: - QString m_password; -}; diff --git a/launcher/minecraft/auth/steps/MigrationEligibilityStep.cpp b/launcher/minecraft/auth/steps/MigrationEligibilityStep.cpp deleted file mode 100644 index 5ce953dfb..000000000 --- a/launcher/minecraft/auth/steps/MigrationEligibilityStep.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "MigrationEligibilityStep.h" - -#include - -#include "minecraft/auth/AuthRequest.h" -#include "minecraft/auth/Parsers.h" - -MigrationEligibilityStep::MigrationEligibilityStep(AccountData* data) : AuthStep(data) {} - -MigrationEligibilityStep::~MigrationEligibilityStep() noexcept = default; - -QString MigrationEligibilityStep::describe() -{ - return tr("Checking for migration eligibility."); -} - -void MigrationEligibilityStep::perform() -{ - auto url = QUrl("https://api.minecraftservices.com/rollout/v1/msamigration"); - QNetworkRequest request = QNetworkRequest(url); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - request.setRawHeader("Authorization", QString("Bearer %1").arg(m_data->yggdrasilToken.token).toUtf8()); - - AuthRequest* requestor = new AuthRequest(this); - connect(requestor, &AuthRequest::finished, this, &MigrationEligibilityStep::onRequestDone); - requestor->get(request); -} - -void MigrationEligibilityStep::rehydrate() -{ - // NOOP, for now. We only save bools and there's nothing to check. -} - -void MigrationEligibilityStep::onRequestDone(QNetworkReply::NetworkError error, - QByteArray data, - QList headers) -{ - auto requestor = qobject_cast(QObject::sender()); - requestor->deleteLater(); - - if (error == QNetworkReply::NoError) { - Parsers::parseRolloutResponse(data, m_data->canMigrateToMSA); - } - emit finished(AccountTaskState::STATE_WORKING, tr("Got migration flags")); -} diff --git a/launcher/minecraft/auth/steps/MigrationEligibilityStep.h b/launcher/minecraft/auth/steps/MigrationEligibilityStep.h deleted file mode 100644 index 8638975d8..000000000 --- a/launcher/minecraft/auth/steps/MigrationEligibilityStep.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include - -#include "QObjectPtr.h" -#include "minecraft/auth/AuthStep.h" - -class MigrationEligibilityStep : public AuthStep { - Q_OBJECT - - public: - explicit MigrationEligibilityStep(AccountData* data); - virtual ~MigrationEligibilityStep() noexcept; - - void perform() override; - void rehydrate() override; - - QString describe() override; - - private slots: - void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList); -}; diff --git a/launcher/minecraft/auth/steps/MinecraftProfileStep.cpp b/launcher/minecraft/auth/steps/MinecraftProfileStep.cpp index 7cdce23f0..a854342bc 100644 --- a/launcher/minecraft/auth/steps/MinecraftProfileStep.cpp +++ b/launcher/minecraft/auth/steps/MinecraftProfileStep.cpp @@ -41,10 +41,6 @@ void MinecraftProfileStep::onRequestDone(QNetworkReply::NetworkError error, QByt qCDebug(authCredentials()) << data; if (error == QNetworkReply::ContentNotFoundError) { // NOTE: Succeed even if we do not have a profile. This is a valid account state. - if (m_data->type == AccountType::Mojang) { - m_data->minecraftEntitlement.canPlayMinecraft = false; - m_data->minecraftEntitlement.ownsMinecraft = false; - } m_data->minecraftProfile = MinecraftProfile(); emit finished(AccountTaskState::STATE_SUCCEEDED, tr("Account has no Minecraft profile.")); return; @@ -73,10 +69,5 @@ void MinecraftProfileStep::onRequestDone(QNetworkReply::NetworkError error, QByt return; } - if (m_data->type == AccountType::Mojang) { - auto validProfile = m_data->minecraftProfile.validity == Katabasis::Validity::Certain; - m_data->minecraftEntitlement.canPlayMinecraft = validProfile; - m_data->minecraftEntitlement.ownsMinecraft = validProfile; - } emit finished(AccountTaskState::STATE_WORKING, tr("Minecraft Java profile acquisition succeeded.")); } diff --git a/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.cpp b/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.cpp deleted file mode 100644 index d035e39a0..000000000 --- a/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include "MinecraftProfileStepMojang.h" - -#include - -#include "Logging.h" -#include "minecraft/auth/AuthRequest.h" -#include "minecraft/auth/Parsers.h" -#include "net/NetUtils.h" - -MinecraftProfileStepMojang::MinecraftProfileStepMojang(AccountData* data) : AuthStep(data) {} - -MinecraftProfileStepMojang::~MinecraftProfileStepMojang() noexcept = default; - -QString MinecraftProfileStepMojang::describe() -{ - return tr("Fetching the Minecraft profile."); -} - -void MinecraftProfileStepMojang::perform() -{ - if (m_data->minecraftProfile.id.isEmpty()) { - emit finished(AccountTaskState::STATE_FAILED_HARD, tr("A UUID is required to get the profile.")); - return; - } - - // use session server instead of profile due to profile endpoint being locked for locked Mojang accounts - QUrl url = QUrl("https://sessionserver.mojang.com/session/minecraft/profile/" + m_data->minecraftProfile.id); - QNetworkRequest req = QNetworkRequest(url); - AuthRequest* request = new AuthRequest(this); - connect(request, &AuthRequest::finished, this, &MinecraftProfileStepMojang::onRequestDone); - request->get(req); -} - -void MinecraftProfileStepMojang::rehydrate() -{ - // NOOP, for now. We only save bools and there's nothing to check. -} - -void MinecraftProfileStepMojang::onRequestDone(QNetworkReply::NetworkError error, - QByteArray data, - QList headers) -{ - auto requestor = qobject_cast(QObject::sender()); - requestor->deleteLater(); - - qCDebug(authCredentials()) << data; - if (error == QNetworkReply::ContentNotFoundError) { - // NOTE: Succeed even if we do not have a profile. This is a valid account state. - if (m_data->type == AccountType::Mojang) { - m_data->minecraftEntitlement.canPlayMinecraft = false; - m_data->minecraftEntitlement.ownsMinecraft = false; - } - m_data->minecraftProfile = MinecraftProfile(); - emit finished(AccountTaskState::STATE_SUCCEEDED, tr("Account has no Minecraft profile.")); - return; - } - if (error != QNetworkReply::NoError) { - qWarning() << "Error getting profile:"; - qWarning() << " HTTP Status: " << requestor->httpStatus_; - qWarning() << " Internal error no.: " << error; - qWarning() << " Error string: " << requestor->errorString_; - - qWarning() << " Response:"; - qWarning() << QString::fromUtf8(data); - - if (Net::isApplicationError(error)) { - emit finished(AccountTaskState::STATE_FAILED_SOFT, - tr("Minecraft Java profile acquisition failed: %1").arg(requestor->errorString_)); - } else { - emit finished(AccountTaskState::STATE_OFFLINE, - tr("Minecraft Java profile acquisition failed: %1").arg(requestor->errorString_)); - } - return; - } - if (!Parsers::parseMinecraftProfileMojang(data, m_data->minecraftProfile)) { - m_data->minecraftProfile = MinecraftProfile(); - emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Minecraft Java profile response could not be parsed")); - return; - } - - if (m_data->type == AccountType::Mojang) { - auto validProfile = m_data->minecraftProfile.validity == Katabasis::Validity::Certain; - m_data->minecraftEntitlement.canPlayMinecraft = validProfile; - m_data->minecraftEntitlement.ownsMinecraft = validProfile; - } - emit finished(AccountTaskState::STATE_WORKING, tr("Minecraft Java profile acquisition succeeded.")); -} diff --git a/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.h b/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.h deleted file mode 100644 index 730ec3f68..000000000 --- a/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include - -#include "QObjectPtr.h" -#include "minecraft/auth/AuthStep.h" - -class MinecraftProfileStepMojang : public AuthStep { - Q_OBJECT - - public: - explicit MinecraftProfileStepMojang(AccountData* data); - virtual ~MinecraftProfileStepMojang() noexcept; - - void perform() override; - void rehydrate() override; - - QString describe() override; - - private slots: - void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList); -}; diff --git a/launcher/minecraft/auth/steps/YggdrasilStep.cpp b/launcher/minecraft/auth/steps/YggdrasilStep.cpp deleted file mode 100644 index fdcaa0d67..000000000 --- a/launcher/minecraft/auth/steps/YggdrasilStep.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "YggdrasilStep.h" - -#include "minecraft/auth/AuthRequest.h" -#include "minecraft/auth/Parsers.h" -#include "minecraft/auth/Yggdrasil.h" - -YggdrasilStep::YggdrasilStep(AccountData* data, QString password) : AuthStep(data), m_password(password) -{ - m_yggdrasil = new Yggdrasil(m_data, this); - - connect(m_yggdrasil, &Task::failed, this, &YggdrasilStep::onAuthFailed); - connect(m_yggdrasil, &Task::succeeded, this, &YggdrasilStep::onAuthSucceeded); - connect(m_yggdrasil, &Task::aborted, this, &YggdrasilStep::onAuthFailed); -} - -YggdrasilStep::~YggdrasilStep() noexcept = default; - -QString YggdrasilStep::describe() -{ - return tr("Logging in with Mojang account."); -} - -void YggdrasilStep::rehydrate() -{ - // NOOP, for now. -} - -void YggdrasilStep::perform() -{ - if (m_password.size()) { - m_yggdrasil->login(m_password); - } else { - m_yggdrasil->refresh(); - } -} - -void YggdrasilStep::onAuthSucceeded() -{ - emit finished(AccountTaskState::STATE_WORKING, tr("Logged in with Mojang")); -} - -void YggdrasilStep::onAuthFailed() -{ - // TODO: hook these in again, expand to MSA - // m_error = m_yggdrasil->m_error; - // m_aborted = m_yggdrasil->m_aborted; - - auto state = m_yggdrasil->taskState(); - QString errorMessage = tr("Mojang user authentication failed."); - - // NOTE: soft error in the first step means 'offline' - if (state == AccountTaskState::STATE_FAILED_SOFT) { - state = AccountTaskState::STATE_OFFLINE; - errorMessage = tr("Mojang user authentication ended with a network error."); - } - emit finished(state, errorMessage); -} diff --git a/launcher/minecraft/auth/steps/YggdrasilStep.h b/launcher/minecraft/auth/steps/YggdrasilStep.h deleted file mode 100644 index ef31f34d5..000000000 --- a/launcher/minecraft/auth/steps/YggdrasilStep.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once -#include - -#include "QObjectPtr.h" -#include "minecraft/auth/AuthStep.h" - -class Yggdrasil; - -class YggdrasilStep : public AuthStep { - Q_OBJECT - - public: - explicit YggdrasilStep(AccountData* data, QString password); - virtual ~YggdrasilStep() noexcept; - - void perform() override; - void rehydrate() override; - - QString describe() override; - - private slots: - void onAuthSucceeded(); - void onAuthFailed(); - - private: - Yggdrasil* m_yggdrasil = nullptr; - QString m_password; -}; diff --git a/launcher/ui/dialogs/LoginDialog.cpp b/launcher/ui/dialogs/LoginDialog.cpp deleted file mode 100644 index 7296a13ef..000000000 --- a/launcher/ui/dialogs/LoginDialog.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/* Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "LoginDialog.h" -#include "ui_LoginDialog.h" - -#include "minecraft/auth/AccountTask.h" - -#include - -LoginDialog::LoginDialog(QWidget* parent) : QDialog(parent), ui(new Ui::LoginDialog) -{ - ui->setupUi(this); - ui->progressBar->setVisible(false); - ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); - - connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); - connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); -} - -LoginDialog::~LoginDialog() -{ - delete ui; -} - -// Stage 1: User interaction -void LoginDialog::accept() -{ - setUserInputsEnabled(false); - ui->progressBar->setVisible(true); - - // Setup the login task and start it - m_account = MinecraftAccount::createFromUsername(ui->userTextBox->text()); - m_loginTask = m_account->login(ui->passTextBox->text()); - connect(m_loginTask.get(), &Task::failed, this, &LoginDialog::onTaskFailed); - connect(m_loginTask.get(), &Task::succeeded, this, &LoginDialog::onTaskSucceeded); - connect(m_loginTask.get(), &Task::status, this, &LoginDialog::onTaskStatus); - connect(m_loginTask.get(), &Task::progress, this, &LoginDialog::onTaskProgress); - m_loginTask->start(); -} - -void LoginDialog::setUserInputsEnabled(bool enable) -{ - ui->userTextBox->setEnabled(enable); - ui->passTextBox->setEnabled(enable); - ui->buttonBox->setEnabled(enable); -} - -// Enable the OK button only when both textboxes contain something. -void LoginDialog::on_userTextBox_textEdited(const QString& newText) -{ - ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!newText.isEmpty() && !ui->passTextBox->text().isEmpty()); -} -void LoginDialog::on_passTextBox_textEdited(const QString& newText) -{ - ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!newText.isEmpty() && !ui->userTextBox->text().isEmpty()); -} - -void LoginDialog::onTaskFailed(const QString& reason) -{ - // Set message - auto lines = reason.split('\n'); - QString processed; - for (auto line : lines) { - if (line.size()) { - processed += "" + line + "
"; - } else { - processed += "
"; - } - } - ui->label->setText(processed); - - // Re-enable user-interaction - setUserInputsEnabled(true); - ui->progressBar->setVisible(false); -} - -void LoginDialog::onTaskSucceeded() -{ - QDialog::accept(); -} - -void LoginDialog::onTaskStatus(const QString& status) -{ - ui->label->setText(status); -} - -void LoginDialog::onTaskProgress(qint64 current, qint64 total) -{ - ui->progressBar->setMaximum(total); - ui->progressBar->setValue(current); -} - -// Public interface -MinecraftAccountPtr LoginDialog::newAccount(QWidget* parent, QString msg) -{ - LoginDialog dlg(parent); - dlg.ui->label->setText(msg); - if (dlg.exec() == QDialog::Accepted) { - return dlg.m_account; - } - return nullptr; -} diff --git a/launcher/ui/dialogs/LoginDialog.h b/launcher/ui/dialogs/LoginDialog.h deleted file mode 100644 index 601b5fa77..000000000 --- a/launcher/ui/dialogs/LoginDialog.h +++ /dev/null @@ -1,56 +0,0 @@ -/* Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -#include "minecraft/auth/MinecraftAccount.h" -#include "tasks/Task.h" - -namespace Ui { -class LoginDialog; -} - -class LoginDialog : public QDialog { - Q_OBJECT - - public: - ~LoginDialog(); - - static MinecraftAccountPtr newAccount(QWidget* parent, QString message); - - private: - explicit LoginDialog(QWidget* parent = 0); - - void setUserInputsEnabled(bool enable); - - protected slots: - void accept(); - - void onTaskFailed(const QString& reason); - void onTaskSucceeded(); - void onTaskStatus(const QString& status); - void onTaskProgress(qint64 current, qint64 total); - - void on_userTextBox_textEdited(const QString& newText); - void on_passTextBox_textEdited(const QString& newText); - - private: - Ui::LoginDialog* ui; - MinecraftAccountPtr m_account; - Task::Ptr m_loginTask; -}; diff --git a/launcher/ui/dialogs/LoginDialog.ui b/launcher/ui/dialogs/LoginDialog.ui deleted file mode 100644 index 8fa4a45d2..000000000 --- a/launcher/ui/dialogs/LoginDialog.ui +++ /dev/null @@ -1,77 +0,0 @@ - - - LoginDialog - - - - 0 - 0 - 421 - 198 - - - - - 0 - 0 - - - - Add Account - - - - - - Message label placeholder. - - - Qt::RichText - - - Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - Email - - - - - - - QLineEdit::Password - - - Password - - - - - - - 24 - - - false - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - diff --git a/launcher/ui/pages/global/AccountListPage.cpp b/launcher/ui/pages/global/AccountListPage.cpp index c95bfabdd..90591c7f6 100644 --- a/launcher/ui/pages/global/AccountListPage.cpp +++ b/launcher/ui/pages/global/AccountListPage.cpp @@ -45,7 +45,6 @@ #include "net/NetJob.h" #include "ui/dialogs/CustomMessageBox.h" -#include "ui/dialogs/LoginDialog.h" #include "ui/dialogs/MSALoginDialog.h" #include "ui/dialogs/OfflineLoginDialog.h" #include "ui/dialogs/ProgressDialog.h" @@ -64,8 +63,7 @@ AccountListPage::AccountListPage(QWidget* parent) : QMainWindow(parent), ui(new ui->setupUi(this); ui->listView->setEmptyString( tr("Welcome!\n" - "If you're new here, you can select the \"Add Microsoft\" or \"Add Mojang\" buttons to link your Microsoft and/or Mojang " - "accounts.")); + "If you're new here, you can select the \"Add Microsoft\" button to link your Microsoft account.")); ui->listView->setEmptyMode(VersionListView::String); ui->listView->setContextMenuPolicy(Qt::CustomContextMenu); @@ -74,7 +72,6 @@ AccountListPage::AccountListPage(QWidget* parent) : QMainWindow(parent), ui(new ui->listView->setModel(m_accounts.get()); ui->listView->header()->setSectionResizeMode(AccountList::VListColumns::ProfileNameColumn, QHeaderView::Stretch); ui->listView->header()->setSectionResizeMode(AccountList::VListColumns::NameColumn, QHeaderView::Stretch); - ui->listView->header()->setSectionResizeMode(AccountList::VListColumns::MigrationColumn, QHeaderView::ResizeToContents); ui->listView->header()->setSectionResizeMode(AccountList::VListColumns::TypeColumn, QHeaderView::ResizeToContents); ui->listView->header()->setSectionResizeMode(AccountList::VListColumns::StatusColumn, QHeaderView::ResizeToContents); ui->listView->setSelectionMode(QAbstractItemView::SingleSelection); @@ -139,19 +136,6 @@ void AccountListPage::listChanged() updateButtonStates(); } -void AccountListPage::on_actionAddMojang_triggered() -{ - MinecraftAccountPtr account = - LoginDialog::newAccount(this, tr("Please enter your Mojang account email and password to add your account.")); - - if (account) { - m_accounts->addAccount(account); - if (m_accounts->count() == 1) { - m_accounts->setDefaultAccount(account); - } - } -} - void AccountListPage::on_actionAddMicrosoft_triggered() { MinecraftAccountPtr account = diff --git a/launcher/ui/pages/global/AccountListPage.h b/launcher/ui/pages/global/AccountListPage.h index add0f4aa0..f3b80191d 100644 --- a/launcher/ui/pages/global/AccountListPage.h +++ b/launcher/ui/pages/global/AccountListPage.h @@ -70,7 +70,6 @@ class AccountListPage : public QMainWindow, public BasePage { void retranslate() override; public slots: - void on_actionAddMojang_triggered(); void on_actionAddMicrosoft_triggered(); void on_actionAddOffline_triggered(); void on_actionRemove_triggered(); diff --git a/launcher/ui/pages/global/AccountListPage.ui b/launcher/ui/pages/global/AccountListPage.ui index 469955b51..d8cf3ac0a 100644 --- a/launcher/ui/pages/global/AccountListPage.ui +++ b/launcher/ui/pages/global/AccountListPage.ui @@ -53,7 +53,6 @@ false - @@ -63,11 +62,6 @@
- - - Add &Mojang - - Remo&ve From 86b47b3421101083a5d0a93a11d33f35c3f8f4a2 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 25 Sep 2023 22:33:36 +0300 Subject: [PATCH 233/268] Do not display invalid shaders Signed-off-by: Trial97 --- launcher/minecraft/mod/ShaderPack.cpp | 7 ++++++- launcher/minecraft/mod/ShaderPack.h | 1 + launcher/minecraft/mod/ShaderPackFolderModel.h | 13 +++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/launcher/minecraft/mod/ShaderPack.cpp b/launcher/minecraft/mod/ShaderPack.cpp index 6a9641de2..2c094f26a 100644 --- a/launcher/minecraft/mod/ShaderPack.cpp +++ b/launcher/minecraft/mod/ShaderPack.cpp @@ -22,7 +22,7 @@ #include "ShaderPack.h" -#include "minecraft/mod/tasks/LocalShaderPackParseTask.h" +#include void ShaderPack::setPackFormat(ShaderPackFormat new_format) { @@ -35,3 +35,8 @@ bool ShaderPack::valid() const { return m_pack_format != ShaderPackFormat::INVALID; } + +bool ShaderPack::applyFilter(QRegularExpression filter) const +{ + return valid() && Resource::applyFilter(filter); +} diff --git a/launcher/minecraft/mod/ShaderPack.h b/launcher/minecraft/mod/ShaderPack.h index ec0f9404e..d07c124be 100644 --- a/launcher/minecraft/mod/ShaderPack.h +++ b/launcher/minecraft/mod/ShaderPack.h @@ -54,6 +54,7 @@ class ShaderPack : public Resource { void setPackFormat(ShaderPackFormat new_format); bool valid() const override; + [[nodiscard]] bool applyFilter(QRegularExpression filter) const override; protected: mutable QMutex m_data_lock; diff --git a/launcher/minecraft/mod/ShaderPackFolderModel.h b/launcher/minecraft/mod/ShaderPackFolderModel.h index 44ed37a47..186d02139 100644 --- a/launcher/minecraft/mod/ShaderPackFolderModel.h +++ b/launcher/minecraft/mod/ShaderPackFolderModel.h @@ -1,6 +1,9 @@ #pragma once #include "ResourceFolderModel.h" +#include "minecraft/mod/ShaderPack.h" +#include "minecraft/mod/tasks/BasicFolderLoadTask.h" +#include "minecraft/mod/tasks/LocalShaderPackParseTask.h" class ShaderPackFolderModel : public ResourceFolderModel { Q_OBJECT @@ -9,4 +12,14 @@ class ShaderPackFolderModel : public ResourceFolderModel { explicit ShaderPackFolderModel(const QString& dir, BaseInstance* instance) : ResourceFolderModel(QDir(dir), instance) {} virtual QString id() const override { return "shaderpacks"; } + + [[nodiscard]] Task* createUpdateTask() override + { + return new BasicFolderLoadTask(m_dir, [](QFileInfo const& entry) { return makeShared(entry); }); + } + + [[nodiscard]] Task* createParseTask(Resource& resource) override + { + return new LocalShaderPackParseTask(m_next_resolution_ticket, static_cast(resource)); + } }; From 4ee6a6711d462d4f85c84afe6bc0e0e6640b1244 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 25 Sep 2023 23:04:21 +0300 Subject: [PATCH 234/268] Added version check for neoforge filter Signed-off-by: Trial97 --- launcher/minecraft/PackProfile.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp index 9e42c5dd6..31974cd00 100644 --- a/launcher/minecraft/PackProfile.cpp +++ b/launcher/minecraft/PackProfile.cpp @@ -1018,8 +1018,8 @@ std::optional PackProfile::getSupportedModLoaders() // TODO: remove this or add version condition once Quilt drops official Fabric support if (loaders & ModPlatform::Quilt) loaders |= ModPlatform::Fabric; - // TODO: remove this or add version condition once NeoForge drops official Forge support - if (loaders & ModPlatform::NeoForge) + Version instance_ver{ getComponentVersion("net.minecraft") }; + if (instance_ver <= Version("1.20.1") && loaders & ModPlatform::NeoForge) loaders |= ModPlatform::Forge; return loaders; } From b8d9c3d7795692f2d012421a28232c3eb1a73301 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 26 Sep 2023 20:19:35 +0300 Subject: [PATCH 235/268] format Signed-off-by: Trial97 --- launcher/Application.cpp | 2 +- launcher/FileSystem.h | 2 -- launcher/MMCZip.cpp | 1 - launcher/MMCZip.h | 1 - launcher/StringUtils.h | 2 +- launcher/filelink/FileLink.h | 11 ++--------- launcher/filelink/filelink_main.cpp | 5 ++--- launcher/modplatform/modrinth/ModrinthAPI.cpp | 1 - launcher/ui/MainWindow.cpp | 6 ++---- launcher/ui/dialogs/UpdateAvailableDialog.cpp | 6 +++--- launcher/ui/dialogs/UpdateAvailableDialog.h | 3 +-- launcher/updater/prismupdater/UpdaterDialogs.h | 2 +- 12 files changed, 13 insertions(+), 29 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 955ec692f..ba60b1e04 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -875,7 +875,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) updateCapabilities(); detectLibraries(); - + // check update locks { auto update_log_path = FS::PathCombine(m_dataPath, "logs", "prism_launcher_update.log"); diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h index c474334c3..861cfa267 100644 --- a/launcher/FileSystem.h +++ b/launcher/FileSystem.h @@ -61,7 +61,6 @@ class FileSystemException : public ::Exception { */ void write(const QString& filename, const QByteArray& data); - /** * append data to a file safely */ @@ -72,7 +71,6 @@ void appendSafe(const QString& filename, const QByteArray& data); */ void append(const QString& filename, const QByteArray& data); - /** * read data from a file safely\ */ diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp index 40790a824..3bfe16ab5 100644 --- a/launcher/MMCZip.cpp +++ b/launcher/MMCZip.cpp @@ -510,4 +510,3 @@ bool ExportToZipTask::abort() #endif } // namespace MMCZip - diff --git a/launcher/MMCZip.h b/launcher/MMCZip.h index 8afb12410..1c6ebe34a 100644 --- a/launcher/MMCZip.h +++ b/launcher/MMCZip.h @@ -151,7 +151,6 @@ bool extractFile(QString fileCompressed, QString file, QString dir); */ bool collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList* files, FilterFunction excludeFilter); - #if defined(LAUNCHER_APPLICATION) class ExportToZipTask : public Task { public: diff --git a/launcher/StringUtils.h b/launcher/StringUtils.h index 9244d3461..9d2bdd85e 100644 --- a/launcher/StringUtils.h +++ b/launcher/StringUtils.h @@ -36,8 +36,8 @@ #pragma once -#include #include +#include #include #include diff --git a/launcher/filelink/FileLink.h b/launcher/filelink/FileLink.h index ab3e9d360..583d0d43a 100644 --- a/launcher/filelink/FileLink.h +++ b/launcher/filelink/FileLink.h @@ -41,17 +41,10 @@ class FileLinkApp : public QCoreApplication { // friends for the purpose of limiting access to deprecated stuff Q_OBJECT public: - enum Status { - Starting, - Failed, - Succeeded, - Initialized - }; + enum Status { Starting, Failed, Succeeded, Initialized }; FileLinkApp(int& argc, char** argv); virtual ~FileLinkApp(); - Status status() const { - return m_status; - } + Status status() const { return m_status; } private: void joinServer(QString server); diff --git a/launcher/filelink/filelink_main.cpp b/launcher/filelink/filelink_main.cpp index a656a9c96..2a8bcb703 100644 --- a/launcher/filelink/filelink_main.cpp +++ b/launcher/filelink/filelink_main.cpp @@ -26,10 +26,9 @@ int main(int argc, char* argv[]) { FileLinkApp ldh(argc, argv); - switch(ldh.status()) { + switch (ldh.status()) { case FileLinkApp::Starting: - case FileLinkApp::Initialized: - { + case FileLinkApp::Initialized: { return ldh.exec(); } case FileLinkApp::Failed: diff --git a/launcher/modplatform/modrinth/ModrinthAPI.cpp b/launcher/modplatform/modrinth/ModrinthAPI.cpp index 355c759cd..f453f5cb9 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.cpp +++ b/launcher/modplatform/modrinth/ModrinthAPI.cpp @@ -10,7 +10,6 @@ #include "net/ApiUpload.h" #include "net/NetJob.h" #include "net/Upload.h" -#include "net/ApiDownload.h" Task::Ptr ModrinthAPI::currentVersion(QString hash, QString hash_format, std::shared_ptr response) { diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 3a5ce9c47..326b22043 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -676,8 +676,7 @@ void MainWindow::repopulateAccountsMenu() void MainWindow::updatesAllowedChanged(bool allowed) { - if(!APPLICATION->updaterEnabled()) - { + if (!APPLICATION->updaterEnabled()) { return; } ui->actionCheckUpdate->setEnabled(allowed); @@ -1209,8 +1208,7 @@ void MainWindow::refreshInstances() void MainWindow::checkForUpdates() { - if(APPLICATION->updaterEnabled()) - { + if (APPLICATION->updaterEnabled()) { APPLICATION->triggerUpdateCheck(); } else { qWarning() << "Updater not set up. Cannot check for updates."; diff --git a/launcher/ui/dialogs/UpdateAvailableDialog.cpp b/launcher/ui/dialogs/UpdateAvailableDialog.cpp index 28f7167cb..5eebe87a3 100644 --- a/launcher/ui/dialogs/UpdateAvailableDialog.cpp +++ b/launcher/ui/dialogs/UpdateAvailableDialog.cpp @@ -46,17 +46,17 @@ UpdateAvailableDialog::UpdateAvailableDialog(const QString& currentVersion, ui->releaseNotes->setHtml(releaseNotesHtml); ui->releaseNotes->setOpenExternalLinks(true); - connect(ui->skipButton, &QPushButton::clicked, this, [this](){ + connect(ui->skipButton, &QPushButton::clicked, this, [this]() { setResult(ResultCode::Skip); done(ResultCode::Skip); }); - connect(ui->delayButton, &QPushButton::clicked, this, [this](){ + connect(ui->delayButton, &QPushButton::clicked, this, [this]() { setResult(ResultCode::DontInstall); done(ResultCode::DontInstall); }); - connect(ui->installButton, &QPushButton::clicked, this, [this](){ + connect(ui->installButton, &QPushButton::clicked, this, [this]() { setResult(ResultCode::Install); done(ResultCode::Install); }); diff --git a/launcher/ui/dialogs/UpdateAvailableDialog.h b/launcher/ui/dialogs/UpdateAvailableDialog.h index f3ea5cbb1..6af9ace36 100644 --- a/launcher/ui/dialogs/UpdateAvailableDialog.h +++ b/launcher/ui/dialogs/UpdateAvailableDialog.h @@ -31,13 +31,12 @@ class UpdateAvailableDialog : public QDialog { Q_OBJECT public: - enum ResultCode { Install = 10, DontInstall = 11, Skip = 12, }; - + explicit UpdateAvailableDialog(const QString& currentVersion, const QString& availableVersion, const QString& releaseNotes, diff --git a/launcher/updater/prismupdater/UpdaterDialogs.h b/launcher/updater/prismupdater/UpdaterDialogs.h index 249ca1b5d..e336c0e2c 100644 --- a/launcher/updater/prismupdater/UpdaterDialogs.h +++ b/launcher/updater/prismupdater/UpdaterDialogs.h @@ -25,8 +25,8 @@ #include #include -#include "Version.h" #include "GitHubRelease.h" +#include "Version.h" namespace Ui { class SelectReleaseDialog; From f370f0d749f4b4ff85daf3e494e4a64ae0c5782a Mon Sep 17 00:00:00 2001 From: seth Date: Wed, 27 Sep 2023 05:33:22 -0400 Subject: [PATCH 236/268] chore: cleanup a few more mentions of mojang accounts Signed-off-by: seth --- launcher/LaunchController.cpp | 4 ++-- launcher/minecraft/auth/AccountList.cpp | 2 +- launcher/ui/MainWindow.cpp | 2 +- launcher/ui/pages/global/AccountListPage.cpp | 2 +- launcher/ui/pages/instance/VersionPage.cpp | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp index b21eb281f..e1953abd6 100644 --- a/launcher/LaunchController.cpp +++ b/launcher/LaunchController.cpp @@ -88,8 +88,8 @@ void LaunchController::decideAccount() if (accounts->count() <= 0) { // Tell the user they need to log in at least one account in order to play. auto reply = CustomMessageBox::selectable(m_parentWidget, tr("No Accounts"), - tr("In order to play Minecraft, you must have at least one Microsoft or Mojang " - "account logged in. Mojang accounts can only be used offline. " + tr("In order to play Minecraft, you must have at least one Microsoft " + "account logged in." "Would you like to open the account manager to add an account now?"), QMessageBox::Information, QMessageBox::Yes | QMessageBox::No) ->exec(); diff --git a/launcher/minecraft/auth/AccountList.cpp b/launcher/minecraft/auth/AccountList.cpp index a29de4ecf..eed11e030 100644 --- a/launcher/minecraft/auth/AccountList.cpp +++ b/launcher/minecraft/auth/AccountList.cpp @@ -366,7 +366,7 @@ QVariant AccountList::headerData(int section, [[maybe_unused]] Qt::Orientation o case NameColumn: return tr("User name of the account."); case TypeColumn: - return tr("Type of the account - Mojang or MSA."); + return tr("Type of the account (currently only MSA is supported.)"); case StatusColumn: return tr("Current status of the account."); default: diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 5e55a5abb..64873ebb8 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -872,7 +872,7 @@ void MainWindow::finalizeInstance(InstancePtr inst) } else { CustomMessageBox::selectable(this, tr("Error"), tr("The launcher cannot download Minecraft or update instances unless you have at least " - "one account added.\nPlease add your Microsoft or Mojang account."), + "one account added.\nPlease add a Microsoft account."), QMessageBox::Warning) ->show(); } diff --git a/launcher/ui/pages/global/AccountListPage.cpp b/launcher/ui/pages/global/AccountListPage.cpp index 90591c7f6..50f3ff2b6 100644 --- a/launcher/ui/pages/global/AccountListPage.cpp +++ b/launcher/ui/pages/global/AccountListPage.cpp @@ -153,7 +153,7 @@ void AccountListPage::on_actionAddOffline_triggered() { if (!m_accounts->anyAccountIsValid()) { QMessageBox::warning(this, tr("Error"), - tr("You must add a Microsoft or Mojang account that owns Minecraft before you can add an offline account." + tr("You must add a Microsoft account that owns Minecraft before you can add an offline account." "

" "If you have lost your account you can contact Microsoft for support.")); return; diff --git a/launcher/ui/pages/instance/VersionPage.cpp b/launcher/ui/pages/instance/VersionPage.cpp index e22c764ca..2918261d2 100644 --- a/launcher/ui/pages/instance/VersionPage.cpp +++ b/launcher/ui/pages/instance/VersionPage.cpp @@ -411,7 +411,7 @@ void VersionPage::on_actionDownload_All_triggered() if (!APPLICATION->accounts()->anyAccountIsValid()) { CustomMessageBox::selectable(this, tr("Error"), tr("Cannot download Minecraft or update instances unless you have at least " - "one account added.\nPlease add your Microsoft or Mojang account."), + "one account added.\nPlease add a Microsoft account."), QMessageBox::Warning) ->show(); return; From 498c9db1cedc9b20d631d6e3cb38b4145d660344 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 28 Sep 2023 15:14:59 +0300 Subject: [PATCH 237/268] fixed appImageUpdate Signed-off-by: Trial97 --- launcher/updater/PrismExternalUpdater.cpp | 2 ++ launcher/updater/prismupdater/PrismUpdater.cpp | 10 +++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index 3595042d6..bee72e3a0 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -338,6 +338,8 @@ void PrismExternalUpdater::performUpdate(const QString& version_tag) auto env = QProcessEnvironment::systemEnvironment(); env.insert("__COMPAT_LAYER", "RUNASINVOKER"); proc.setProcessEnvironment(env); +#else + exe_name = QString("bin/%1").arg(exe_name); #endif QStringList args = { "--dir", priv->dataDir.absolutePath(), "--install-version", version_tag }; diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 2604d2e0b..4ffe29b35 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -900,7 +900,7 @@ bool PrismUpdaterApp::callAppImageUpdate() auto appimage_path = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE")); QProcess proc = QProcess(); qDebug() << "Calling: AppImageUpdate" << appimage_path; - proc.setProgram("AppImageUpdate"); + proc.setProgram("bin/AppImageUpdate"); proc.setArguments({ appimage_path }); auto result = proc.startDetached(); if (!result) @@ -1213,8 +1213,14 @@ bool PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) } auto out = proc.readAllStandardOutput(); auto lines = out.split('\n'); + lines.removeAll(""); if (lines.length() < 2) return false; + else if (lines.length() > 2) { + auto line1 = lines.takeLast(); + auto line2 = lines.takeLast(); + lines = { line2, line1 }; + } auto first = lines.takeFirst(); auto first_parts = first.split(' '); if (first_parts.length() < 2) @@ -1230,6 +1236,8 @@ bool PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) m_prsimVersionChannel = "stable"; } auto version_parts = version.split('.'); + if (version_parts.length() < 2) + return false; m_prismVersionMajor = version_parts.takeFirst().toInt(); m_prismVersionMinor = version_parts.takeFirst().toInt(); m_prismGitCommit = lines.takeFirst().simplified(); From 606c12ffeb871e711b15025dedf93634dc4989be Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 28 Sep 2023 18:47:21 +0300 Subject: [PATCH 238/268] more fixing Signed-off-by: Trial97 --- .github/workflows/build.yml | 74 ++++++++----------- .github/workflows/trigger_builds.yml | 29 ++++---- .github/workflows/trigger_release.yml | 9 ++- .../updater/prismupdater/PrismUpdater.cpp | 2 +- 4 files changed, 53 insertions(+), 61 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4b703977b..e1641ed50 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,7 +37,6 @@ jobs: fail-fast: false matrix: include: - - os: ubuntu-20.04 qt_ver: 5 @@ -127,9 +126,9 @@ jobs: - name: Checkout uses: actions/checkout@v4 with: - submodules: 'true' + submodules: "true" - - name: 'Setup MSYS2' + - name: "Setup MSYS2" if: runner.os == 'Windows' && matrix.msystem != '' uses: msys2/setup-msys2@v2 with: @@ -169,7 +168,7 @@ jobs: path: '${{ github.workspace }}\.ccache' key: ${{ matrix.os }}-mingw-w64-ccache-${{ github.run_id }} restore-keys: | - ${{ matrix.os }}-mingw-w64-ccache + ${{ matrix.os }}-mingw-w64-ccache - name: Setup ccache (Windows MinGW-w64) if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug' @@ -214,35 +213,35 @@ jobs: if: runner.os == 'Windows' && matrix.architecture == 'arm64' uses: jurplel/install-qt-action@v3 with: - aqtversion: '==3.1.*' - py7zrversion: '>=0.20.2' - 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 + aqtversion: "==3.1.*" + py7zrversion: ">=0.20.2" + 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: - aqtversion: '==3.1.*' - py7zrversion: '>=0.20.2' - version: ${{ matrix.qt_version }} - host: ${{ matrix.qt_host }} - target: 'desktop' - arch: ${{ matrix.qt_arch }} - modules: ${{ matrix.qt_modules }} - tools: ${{ matrix.qt_tools }} - cache: ${{ inputs.is_qt_cached }} + aqtversion: "==3.1.*" + py7zrversion: ">=0.20.2" + version: ${{ matrix.qt_version }} + host: ${{ matrix.qt_host }} + target: "desktop" + arch: ${{ matrix.qt_arch }} + modules: ${{ matrix.qt_modules }} + tools: ${{ matrix.qt_tools }} + cache: ${{ inputs.is_qt_cached }} - name: Install MSVC (Windows MSVC) - if: runner.os == 'Windows' # We want this for MinGW builds as well, as we need SignTool + if: runner.os == 'Windows' # We want this for MinGW builds as well, as we need SignTool uses: ilammy/msvc-dev-cmd@v1 with: vsversion: 2022 @@ -343,7 +342,7 @@ jobs: - name: Test (Windows MSVC) if: runner.os == 'Windows' && matrix.msystem == '' && matrix.architecture != 'arm64' run: | - ctest -E "^example64|example$" --test-dir build --output-on-failure -C ${{ inputs.build_type }} + ctest -E "^example64|example$" --test-dir build --output-on-failure -C ${{ inputs.build_type }} ## # PACKAGE BUILDS @@ -385,7 +384,7 @@ jobs: run: | cmake --install ${{ env.BUILD_DIR }} touch ${{ env.INSTALL_DIR }}/manifest.txt - for l in $(find ${{ env.INSTALL_DIR }} -type f); do l=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}/}; l=${l#./}; echo $l; done >> ${{ env.INSTALL_DIR }}/manifest.txt + for l in $(find ${{ env.INSTALL_DIR }} -type f); do l=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}/}; l=${l#./}; echo $l; done >> ${{ env.INSTALL_DIR }}/manifest.txt - name: Package (Windows MSVC) if: runner.os == 'Windows' && matrix.msystem == '' @@ -402,10 +401,9 @@ jobs: Get-ChildItem ${{ env.INSTALL_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt - - name: Fetch codesign certificate (Windows) if: runner.os == 'Windows' - shell: bash # yes, we are not using MSYS2 or PowerShell here + shell: bash # yes, we are not using MSYS2 or PowerShell here run: | echo '${{ secrets.WINDOWS_CODESIGN_CERT }}' | base64 --decode > codesign.pfx @@ -507,15 +505,7 @@ jobs: export LD_LIBRARY_PATH chmod +x AppImageUpdate-x86_64.AppImage - ./AppImageUpdate-x86_64.AppImage --appimage-extract - - mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/optional - mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins - - cp -r squashfs-root/usr/bin/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/bin - cp -r squashfs-root/usr/lib/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib - cp -r squashfs-root/usr/optional/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/optional - cp -r squashfs-root/usr/optional/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins + cp AppImageUpdate-x86_64.AppImage ${{ env.INSTALL_APPIMAGE_DIR }}/usr/bin export UPDATE_INFORMATION="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|PrismLauncher-Linux-x86_64.AppImage.zsync" @@ -599,7 +589,7 @@ jobs: with: name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage path: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage - + - name: Upload AppImage Zsync (Linux) if: runner.os == 'Linux' && matrix.qt_ver != 5 uses: actions/upload-artifact@v3 @@ -623,10 +613,10 @@ jobs: uses: actions/checkout@v4 if: inputs.build_type == 'Debug' with: - submodules: 'true' + submodules: "true" - name: Build Flatpak (Linux) if: inputs.build_type == 'Debug' uses: flatpak/flatpak-github-actions/flatpak-builder@v6 with: bundle: "Prism Launcher.flatpak" - manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml + manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml diff --git a/.github/workflows/trigger_builds.yml b/.github/workflows/trigger_builds.yml index 26ee4380b..70fda60ed 100644 --- a/.github/workflows/trigger_builds.yml +++ b/.github/workflows/trigger_builds.yml @@ -3,26 +3,25 @@ name: Build Application on: push: branches-ignore: - - 'renovate/**' + - "renovate/**" paths-ignore: - - '**.md' - - '**/LICENSE' - - 'flake.lock' - - 'packages/**' - - '.github/ISSUE_TEMPLATE/**' - - '.markdownlint**' + - "**.md" + - "**/LICENSE" + - "flake.lock" + - "packages/**" + - ".github/ISSUE_TEMPLATE/**" + - ".markdownlint**" pull_request: paths-ignore: - - '**.md' - - '**/LICENSE' - - 'flake.lock' - - 'packages/**' - - '.github/ISSUE_TEMPLATE/**' - - '.markdownlint**' + - "**.md" + - "**/LICENSE" + - "flake.lock" + - "packages/**" + - ".github/ISSUE_TEMPLATE/**" + - ".markdownlint**" workflow_dispatch: jobs: - build_debug: name: Build Debug uses: ./.github/workflows/build.yml @@ -34,3 +33,5 @@ jobs: WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }} WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }} CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} + GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} + GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }} diff --git a/.github/workflows/trigger_release.yml b/.github/workflows/trigger_release.yml index bda75e354..44bcd539c 100644 --- a/.github/workflows/trigger_release.yml +++ b/.github/workflows/trigger_release.yml @@ -3,10 +3,9 @@ name: Build Application and Make Release on: push: tags: - - '*' + - "*" jobs: - build_release: name: Build Release uses: ./.github/workflows/build.yml @@ -18,6 +17,8 @@ jobs: WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }} WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }} CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} + GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} + GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }} create_release: needs: build_release @@ -28,8 +29,8 @@ jobs: - name: Checkout uses: actions/checkout@v4 with: - submodules: 'true' - path: 'PrismLauncher-source' + submodules: "true" + path: "PrismLauncher-source" - name: Download artifacts uses: actions/download-artifact@v3 - name: Grab and store version diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 4ffe29b35..761c368f0 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -900,7 +900,7 @@ bool PrismUpdaterApp::callAppImageUpdate() auto appimage_path = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE")); QProcess proc = QProcess(); qDebug() << "Calling: AppImageUpdate" << appimage_path; - proc.setProgram("bin/AppImageUpdate"); + proc.setProgram(FS::PathCombine(m_rootPath, "bin", "AppImageUpdate-x86_64.AppImage")); proc.setArguments({ appimage_path }); auto result = proc.startDetached(); if (!result) From 742384909f95eb25ab736094c81f4b48f834f176 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 28 Sep 2023 21:24:31 +0300 Subject: [PATCH 239/268] updated portable update Signed-off-by: Trial97 --- .../updater/prismupdater/PrismUpdater.cpp | 82 +++++++++++++------ 1 file changed, 55 insertions(+), 27 deletions(-) diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 761c368f0..110945f55 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -27,20 +27,20 @@ #include #include -#include -#include -#include - #include +#include +#include +#include #include #include +#include #include #include +#include #include #include -#include #if defined Q_OS_WIN32 #ifndef WIN32_LEAN_AND_MEAN @@ -660,28 +660,42 @@ void PrismUpdaterApp::moveAndFinishUpdate(QDir target) bool error = false; - QProgressDialog progress(tr("Backing up install at %1").arg(m_rootPath), "", 0, file_list.length()); + QProgressDialog progress(tr("Installing from %1").arg(m_rootPath), "", 0, file_list.length()); progress.setCancelButton(nullptr); progress.setMinimumWidth(400); progress.adjustSize(); progress.show(); QCoreApplication::processEvents(); + logUpdate(tr("Installing from %1").arg(m_rootPath)); + + auto copy = [this, app_dir, target](QString to_install_file) { + auto rel_path = app_dir.relativeFilePath(to_install_file); + auto install_path = FS::PathCombine(target.absolutePath(), rel_path); + logUpdate(tr("Installing %1 from %2").arg(install_path).arg(to_install_file)); + FS::ensureFilePathExists(install_path); + auto result = FS::copy(to_install_file, install_path).overwrite(true)(); + if (!result) { + logUpdate(tr("Failed copy %1 to %2").arg(to_install_file).arg(install_path)); + return true; + } + return false; + }; + int i = 0; for (auto glob : file_list) { QDirIterator iter(m_rootPath, QStringList({ glob }), QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); progress.setValue(i); QCoreApplication::processEvents(); - while (iter.hasNext()) { - auto to_install_file = iter.next(); - auto rel_path = app_dir.relativeFilePath(to_install_file); - auto install_path = FS::PathCombine(target.absolutePath(), rel_path); - logUpdate(tr("Installing %1 from %2").arg(install_path).arg(to_install_file)); - FS::ensureFilePathExists(install_path); - auto result = FS::copy(to_install_file, install_path).overwrite(true)(); - if (!result) { - error = true; - logUpdate(tr("Failed copy %1 to %2").arg(to_install_file).arg(install_path)); + if (!iter.hasNext() && !glob.isEmpty()) { + if (auto file_info = QFileInfo(FS::PathCombine(m_rootPath, glob)); file_info.exists()) { + error |= copy(file_info.absoluteFilePath()); + } else { + logUpdate(tr("File doesn't exist, ignoring: %1").arg(FS::PathCombine(m_rootPath, glob))); + } + } else { + while (iter.hasNext()) { + error |= copy(iter.next()); } } i++; @@ -1124,23 +1138,37 @@ void PrismUpdaterApp::backupAppDir() progress.adjustSize(); progress.show(); QCoreApplication::processEvents(); + + logUpdate(tr("Backing up install at %1").arg(m_rootPath)); + + auto copy = [this, app_dir, backup_dir](QString to_bak_file) { + auto rel_path = app_dir.relativeFilePath(to_bak_file); + auto bak_path = FS::PathCombine(backup_dir, rel_path); + logUpdate(tr("Backing up and then removing %1").arg(to_bak_file)); + FS::ensureFilePathExists(bak_path); + auto result = FS::copy(to_bak_file, bak_path).overwrite(true)(); + if (!result) { + logUpdate(tr("Failed to backup %1 to %2").arg(to_bak_file).arg(bak_path)); + } else { + if (!FS::deletePath(to_bak_file)) + logUpdate(tr("Failed to remove %1").arg(to_bak_file)); + } + }; + int i = 0; for (auto glob : file_list) { QDirIterator iter(app_dir.absolutePath(), QStringList({ glob }), QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); progress.setValue(i); QCoreApplication::processEvents(); - while (iter.hasNext()) { - auto to_bak_file = iter.next(); - auto rel_path = app_dir.relativeFilePath(to_bak_file); - auto bak_path = FS::PathCombine(backup_dir, rel_path); - logUpdate(tr("Backing up and then removing %1").arg(to_bak_file)); - FS::ensureFilePathExists(bak_path); - auto result = FS::copy(to_bak_file, bak_path).overwrite(true)(); - if (!result) { - logUpdate(tr("Failed to backup %1 to %2").arg(to_bak_file).arg(bak_path)); + if (!iter.hasNext() && !glob.isEmpty()) { + if (auto file_info = QFileInfo(FS::PathCombine(m_rootPath, glob)); file_info.exists()) { + copy(file_info.absoluteFilePath()); } else { - if (!FS::deletePath(to_bak_file)) - logUpdate(tr("Failed to remove %1").arg(to_bak_file)); + logUpdate(tr("File doesn't exist, ignoring: %1").arg(FS::PathCombine(m_rootPath, glob))); + } + } else { + while (iter.hasNext()) { + copy(iter.next()); } } i++; From bef701eba847ecb1c4e6b0420315bee1ea045eef Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 28 Sep 2023 21:27:11 +0300 Subject: [PATCH 240/268] chaged folder again Signed-off-by: Trial97 --- launcher/updater/prismupdater/PrismUpdater.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 110945f55..1c5aaf1e8 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -1161,10 +1161,10 @@ void PrismUpdaterApp::backupAppDir() progress.setValue(i); QCoreApplication::processEvents(); if (!iter.hasNext() && !glob.isEmpty()) { - if (auto file_info = QFileInfo(FS::PathCombine(m_rootPath, glob)); file_info.exists()) { + if (auto file_info = QFileInfo(FS::PathCombine(app_dir.absolutePath(), glob)); file_info.exists()) { copy(file_info.absoluteFilePath()); } else { - logUpdate(tr("File doesn't exist, ignoring: %1").arg(FS::PathCombine(m_rootPath, glob))); + logUpdate(tr("File doesn't exist, ignoring: %1").arg(FS::PathCombine(app_dir.absolutePath(), glob))); } } else { while (iter.hasNext()) { From 5b7c5607a90cbb6ff78ba140a15d6180842d069b Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 28 Sep 2023 22:53:40 +0300 Subject: [PATCH 241/268] removed flame consturctor Signed-off-by: Trial97 --- launcher/modplatform/ModIndex.cpp | 17 ----------------- launcher/modplatform/ModIndex.h | 1 - launcher/modplatform/flame/FlameModIndex.cpp | 17 ++++++++++++++++- launcher/modplatform/flame/FlamePackIndex.cpp | 17 ++++++++++++++++- 4 files changed, 32 insertions(+), 20 deletions(-) diff --git a/launcher/modplatform/ModIndex.cpp b/launcher/modplatform/ModIndex.cpp index 72ba88d90..fc79dff15 100644 --- a/launcher/modplatform/ModIndex.cpp +++ b/launcher/modplatform/ModIndex.cpp @@ -32,23 +32,6 @@ static const QMap s_indexed_version_ty IndexedVersionType::IndexedVersionType(const QString& type) : IndexedVersionType(enumFromString(type)) {} -IndexedVersionType::IndexedVersionType(int flame_type) -{ - switch (flame_type) { - case 1: - m_type = IndexedVersionType::VersionType::Release; - break; - case 2: - m_type = IndexedVersionType::VersionType::Beta; - break; - case 3: - m_type = IndexedVersionType::VersionType::Alpha; - break; - default: - m_type = IndexedVersionType::VersionType::Unknown; - } -} - IndexedVersionType::IndexedVersionType(const IndexedVersionType::VersionType& type) { m_type = type; diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index 4ed8bcd86..4d6759d3b 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -62,7 +62,6 @@ struct DonationData { struct IndexedVersionType { enum class VersionType { Release = 1, Beta, Alpha, Unknown }; IndexedVersionType(const QString& type); - IndexedVersionType(int flame_type); IndexedVersionType(const IndexedVersionType::VersionType& type); IndexedVersionType(const IndexedVersionType& type); IndexedVersionType() : IndexedVersionType(IndexedVersionType::VersionType::Unknown) {} diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index 3754ae1a6..2adcd7814 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -139,7 +139,22 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) -> file.version = Json::requireString(obj, "displayName"); file.downloadUrl = Json::ensureString(obj, "downloadUrl"); file.fileName = Json::requireString(obj, "fileName"); - file.version_type = ModPlatform::IndexedVersionType(Json::requireInteger(obj, "releaseType")); + + ModPlatform::IndexedVersionType::VersionType ver_type; + switch (Json::requireInteger(obj, "releaseType")) { + case 1: + ver_type = ModPlatform::IndexedVersionType::VersionType::Release; + break; + case 2: + ver_type = ModPlatform::IndexedVersionType::VersionType::Beta; + break; + case 3: + ver_type = ModPlatform::IndexedVersionType::VersionType::Alpha; + break; + default: + ver_type = ModPlatform::IndexedVersionType::VersionType::Unknown; + } + file.version_type = ModPlatform::IndexedVersionType(ver_type); auto hash_list = Json::ensureArray(obj, "hashes"); for (auto h : hash_list) { diff --git a/launcher/modplatform/flame/FlamePackIndex.cpp b/launcher/modplatform/flame/FlamePackIndex.cpp index 5a1606734..71f1e4a2d 100644 --- a/launcher/modplatform/flame/FlamePackIndex.cpp +++ b/launcher/modplatform/flame/FlamePackIndex.cpp @@ -89,7 +89,22 @@ void Flame::loadIndexedPackVersions(Flame::IndexedPack& pack, QJsonArray& arr) // pick the latest version supported file.mcVersion = versionArray[0].toString(); file.version = Json::requireString(version, "displayName"); - file.version_type = ModPlatform::IndexedVersionType(Json::requireInteger(version, "releaseType")); + + ModPlatform::IndexedVersionType::VersionType ver_type; + switch (Json::requireInteger(version, "releaseType")) { + case 1: + ver_type = ModPlatform::IndexedVersionType::VersionType::Release; + break; + case 2: + ver_type = ModPlatform::IndexedVersionType::VersionType::Beta; + break; + case 3: + ver_type = ModPlatform::IndexedVersionType::VersionType::Alpha; + break; + default: + ver_type = ModPlatform::IndexedVersionType::VersionType::Unknown; + } + file.version_type = ModPlatform::IndexedVersionType(ver_type); file.downloadUrl = Json::ensureString(version, "downloadUrl"); // only add if we have a download URL (third party distribution is enabled) From 34294383ebaeff65d2a66a49426b2141c2d840c1 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 28 Sep 2023 23:18:23 +0300 Subject: [PATCH 242/268] added version type to dep update Signed-off-by: Trial97 --- launcher/ui/dialogs/ModUpdateDialog.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index 43609121e..1a70ea59a 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -219,8 +219,10 @@ void ModUpdateDialog::checkCandidates() if (dep->pack->provider == ModPlatform::ResourceProvider::FLAME) changelog = api.getModFileChangelog(dep->version.addonId.toInt(), dep->version.fileId.toInt()); auto download_task = makeShared(dep->pack, dep->version, m_mod_model); - CheckUpdateTask::UpdatableMod updatable = { dep->pack->name, dep->version.hash, "", dep->version.version, - changelog, dep->pack->provider, download_task }; + CheckUpdateTask::UpdatableMod updatable = { + dep->pack->name, dep->version.hash, "", dep->version.version, dep->version.version_type, + changelog, dep->pack->provider, download_task + }; appendMod(updatable, getRequiredBy.value(dep->version.addonId.toString())); m_tasks.insert(updatable.name, updatable.download); From 84269f95961a49434c51b67aef15822ec3028ea6 Mon Sep 17 00:00:00 2001 From: seth Date: Sat, 30 Sep 2023 12:50:26 -0400 Subject: [PATCH 243/268] refactor(nix): use addOpenGLRunpath.driverLink in wrapper Signed-off-by: seth --- nix/pkg/wrapper.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nix/pkg/wrapper.nix b/nix/pkg/wrapper.nix index 8bc255e71..cd356c8d7 100644 --- a/nix/pkg/wrapper.nix +++ b/nix/pkg/wrapper.nix @@ -4,6 +4,7 @@ symlinkJoin, prismlauncher-unwrapped, wrapQtAppsHook, + addOpenGLRunpath, qtbase, # needed for wrapQtAppsHook qtsvg, qtwayland, @@ -85,7 +86,7 @@ in in ["--prefix PRISMLAUNCHER_JAVA_PATHS : ${lib.makeSearchPath "bin/java" jdks}"] ++ lib.optionals stdenv.isLinux [ - "--set LD_LIBRARY_PATH /run/opengl-driver/lib:${lib.makeLibraryPath runtimeLibs}" + "--set LD_LIBRARY_PATH ${addOpenGLRunpath.driverLink}/lib:${lib.makeLibraryPath runtimeLibs}" # xorg.xrandr needed for LWJGL [2.9.2, 3) https://github.com/LWJGL/lwjgl/issues/128 "--prefix PATH : ${lib.makeBinPath runtimePrograms}" ]; From 00bbbdc6e9f9e3e772c73b024b5f06a6ddf76f9a Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Sun, 1 Oct 2023 00:31:51 +0300 Subject: [PATCH 244/268] Update launcher/LaunchController.cpp Co-authored-by: TheKodeToad Signed-off-by: Alexandru Ionut Tripon --- launcher/LaunchController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp index e1953abd6..21a146062 100644 --- a/launcher/LaunchController.cpp +++ b/launcher/LaunchController.cpp @@ -89,7 +89,7 @@ void LaunchController::decideAccount() // Tell the user they need to log in at least one account in order to play. auto reply = CustomMessageBox::selectable(m_parentWidget, tr("No Accounts"), tr("In order to play Minecraft, you must have at least one Microsoft " - "account logged in." + "account which owns Minecraft logged in." "Would you like to open the account manager to add an account now?"), QMessageBox::Information, QMessageBox::Yes | QMessageBox::No) ->exec(); From 317c7b5544a0f797593c0465d927089953c2b506 Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Sun, 1 Oct 2023 00:32:17 +0300 Subject: [PATCH 245/268] Update launcher/minecraft/auth/AccountList.cpp Co-authored-by: Sefa Eyeoglu Signed-off-by: Alexandru Ionut Tripon --- launcher/minecraft/auth/AccountList.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/minecraft/auth/AccountList.cpp b/launcher/minecraft/auth/AccountList.cpp index eed11e030..b141909a9 100644 --- a/launcher/minecraft/auth/AccountList.cpp +++ b/launcher/minecraft/auth/AccountList.cpp @@ -366,7 +366,7 @@ QVariant AccountList::headerData(int section, [[maybe_unused]] Qt::Orientation o case NameColumn: return tr("User name of the account."); case TypeColumn: - return tr("Type of the account (currently only MSA is supported.)"); + return tr("Type of the account (MSA or Offline)"); case StatusColumn: return tr("Current status of the account."); default: From be8a9f6541b2fe4cf552e8e5bb12fde4b9e83c49 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 1 Oct 2023 00:19:50 +0000 Subject: [PATCH 246/268] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:nixos/nixpkgs/e12483116b3b51a185a33a272bf351e357ba9a99' (2023-09-21) → 'github:nixos/nixpkgs/bd9b686c0168041aea600222be0805a0de6e6ab8' (2023-09-29) • Updated input 'pre-commit-hooks': 'github:cachix/pre-commit-hooks.nix/4f883a76282bc28eb952570afc3d8a1bf6f481d7' (2023-09-10) → 'github:cachix/pre-commit-hooks.nix/cb770e93516a1609652fa8e945a0f310e98f10c0' (2023-09-24) --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index f96336551..3ffe2dc8e 100644 --- a/flake.lock +++ b/flake.lock @@ -106,11 +106,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1695318763, - "narHash": "sha256-FHVPDRP2AfvsxAdc+AsgFJevMz5VBmnZglFUMlxBkcY=", + "lastModified": 1695978539, + "narHash": "sha256-lta5HToBZMWZ2hl5CautNSUgIZViR41QxN7JKbMAjgQ=", "owner": "nixos", "repo": "nixpkgs", - "rev": "e12483116b3b51a185a33a272bf351e357ba9a99", + "rev": "bd9b686c0168041aea600222be0805a0de6e6ab8", "type": "github" }, "original": { @@ -153,11 +153,11 @@ ] }, "locked": { - "lastModified": 1694364351, - "narHash": "sha256-oadhSCqopYXxURwIA6/Anpe5IAG11q2LhvTJNP5zE6o=", + "lastModified": 1695576016, + "narHash": "sha256-71KxwRhTfVuh7kNrg3/edNjYVg9DCyKZl2QIKbhRggg=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "4f883a76282bc28eb952570afc3d8a1bf6f481d7", + "rev": "cb770e93516a1609652fa8e945a0f310e98f10c0", "type": "github" }, "original": { From 2cea7454efc8195f12b077adb67acfb2f064c0bd Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 1 Oct 2023 23:04:19 +0300 Subject: [PATCH 247/268] fix: do not update mods if no mod loader is selected Signed-off-by: Trial97 --- launcher/ui/pages/instance/ModFolderPage.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index 69c34dafe..625d37933 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -206,6 +206,14 @@ void ModFolderPage::installMods() void ModFolderPage::updateMods() { + if (m_instance->typeName() != "Minecraft") + return; // this is a null instance or a legacy instance + + auto profile = static_cast(m_instance)->getPackProfile(); + if (!profile->getModLoaders().has_value()) { + QMessageBox::critical(this, tr("Error"), tr("Please install a mod loader first!")); + return; + } auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); auto mods_list = m_model->selectedMods(selection); From 93be8b07356058f85dd2977f941ab1fe219b36de Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 2 Oct 2023 09:13:02 +0300 Subject: [PATCH 248/268] refresh default account when list changes Signed-off-by: Trial97 --- 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 64873ebb8..1202c3319 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -363,7 +363,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi // Shouldn't have to use lambdas here like this, but if I don't, the compiler throws a fit. // Template hell sucks... connect(APPLICATION->accounts().get(), &AccountList::defaultAccountChanged, [this] { defaultAccountChanged(); }); - connect(APPLICATION->accounts().get(), &AccountList::listChanged, [this] { repopulateAccountsMenu(); }); + connect(APPLICATION->accounts().get(), &AccountList::listChanged, [this] { defaultAccountChanged(); }); // Show initial account defaultAccountChanged(); From 8069de29b2857a357809c3d99a8b07c1bcb49b74 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 2 Oct 2023 18:03:00 +0300 Subject: [PATCH 249/268] fix folder attributes on windows copy Signed-off-by: Trial97 --- launcher/FileSystem.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index defb2cb9e..1f60a7db2 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -238,6 +238,28 @@ bool ensureFolderPathExists(QString foldernamepath) return success; } +bool copyFileAttributes(QString src, QString dst) +{ +#ifdef Q_OS_WIN32 + auto attrs = GetFileAttributesW(src.toStdWString().c_str()); + if (attrs == INVALID_FILE_ATTRIBUTES) + return false; + return SetFileAttributesW(dst.toStdWString().c_str(), attrs); +#endif + return true; +} + +// needs folders to exists +void copyFolderAttributes(QString src, QString dst, QString relative) +{ + auto path = PathCombine(src, relative); + QDir dsrc(src); + while ((path = QFileInfo(path).path()).length() >= src.length()) { + auto dst_path = PathCombine(dst, dsrc.relativeFilePath(path)); + copyFileAttributes(path, dst_path); + } +} + /** * @brief Copies a directory and it's contents from src to dest * @param offset subdirectory form src to copy to dest @@ -273,6 +295,9 @@ bool copy::operator()(const QString& offset, bool dryRun) auto dst_path = PathCombine(dst, relative_dst_path); if (!dryRun) { ensureFilePathExists(dst_path); +#ifdef Q_OS_WIN32 + copyFolderAttributes(src, dst, relative_dst_path); +#endif fs::copy(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), opt, err); } if (err) { From 77979b4c953aeff261a8d8bdd5b8a44297012de8 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 3 Oct 2023 17:23:26 +0300 Subject: [PATCH 250/268] revert back the release order Signed-off-by: Trial97 --- launcher/modplatform/flame/FlameAPI.cpp | 4 +--- launcher/modplatform/flame/FlameModIndex.cpp | 3 +-- launcher/modplatform/modrinth/ModrinthPackIndex.cpp | 3 +-- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/launcher/modplatform/flame/FlameAPI.cpp b/launcher/modplatform/flame/FlameAPI.cpp index a9697893d..e99ce3a56 100644 --- a/launcher/modplatform/flame/FlameAPI.cpp +++ b/launcher/modplatform/flame/FlameAPI.cpp @@ -133,9 +133,7 @@ auto FlameAPI::getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::Indexe for (auto file : arr) { auto file_obj = Json::requireObject(file); auto file_tmp = FlameMod::loadIndexedPackVersion(file_obj); - if (file_tmp.date > ver.date && - (!args.loaders.has_value() || !file_tmp.loaders || args.loaders.value() & file_tmp.loaders) && - file_tmp.version_type <= ver.version_type) + if (file_tmp.date > ver.date && (!args.loaders.has_value() || !file_tmp.loaders || args.loaders.value() & file_tmp.loaders)) ver = file_tmp; } diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index 2adcd7814..345883c17 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -96,9 +96,8 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, } auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool { - bool a_better_release = a.version_type <= b.version_type; // dates are in RFC 3339 format - return a.date > b.date && a_better_release; + return a.date > b.date; }; std::sort(unsortedVersions.begin(), unsortedVersions.end(), orderSortPredicate); pack.versions = unsortedVersions; diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index f1e77d465..5c8aed1ac 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -109,9 +109,8 @@ void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArra unsortedVersions.append(file); } auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool { - bool a_better_release = a.version_type <= b.version_type; // dates are in RFC 3339 format - return a.date > b.date && a_better_release; + return a.date > b.date; }; std::sort(unsortedVersions.begin(), unsortedVersions.end(), orderSortPredicate); pack.versions = unsortedVersions; From 3cba5adb5af0bac91f19805fbbc7a99611c6b5a5 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 4 Oct 2023 09:41:40 +0300 Subject: [PATCH 251/268] updated dependency cycle Signed-off-by: Trial97 --- launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp index bd1fe9401..df8c690af 100644 --- a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp +++ b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp @@ -94,7 +94,7 @@ QList GetModDependenciesTask::getDependenciesForVersion for (auto ver_dep : version.dependencies) { if (ver_dep.type != ModPlatform::DependencyType::REQUIRED) continue; - + ver_dep = getOverride(ver_dep, providerName); auto isOnlyVersion = providerName == ModPlatform::ResourceProvider::MODRINTH && ver_dep.addonId.toString().isEmpty(); if (auto dep = std::find_if(c_dependencies.begin(), c_dependencies.end(), [&ver_dep, isOnlyVersion](const ModPlatform::Dependency& i) { @@ -127,7 +127,7 @@ QList GetModDependenciesTask::getDependenciesForVersion dep != m_pack_dependencies.end()) // check loaded dependencies continue; - c_dependencies.append(getOverride(ver_dep, providerName)); + c_dependencies.append(ver_dep); } return c_dependencies; } From c78d4e7bb624cd79fcd330708040d92b9e607c88 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 4 Oct 2023 19:16:21 +0300 Subject: [PATCH 252/268] added version test for snapshot Signed-off-by: Trial97 --- tests/Version_test.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/Version_test.cpp b/tests/Version_test.cpp index d25bf7bb5..4c67cc544 100644 --- a/tests/Version_test.cpp +++ b/tests/Version_test.cpp @@ -55,6 +55,8 @@ class VersionTest : public QObject { << "2.2.0" << true << false; QTest::newRow("lessThan, two-digit") << "1.41" << "1.42" << true << false; + QTest::newRow("lessThan, snapshot") << "1.20.0-rc2" + << "1.20.1" << true << false; QTest::newRow("greaterThan, explicit 1") << "1.2.1" << "1.2.0" << false << false; @@ -72,6 +74,8 @@ class VersionTest : public QObject { << "1.2" << false << false; QTest::newRow("greaterThan, two-digit") << "1.42" << "1.41" << false << false; + QTest::newRow("greaterThan, snapshot") << "1.20.2-rc2" + << "1.20.1" << false << false; } private slots: From e913f61305de434cda5755f1db6bd3bb2ce4ea5e Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 4 Oct 2023 19:29:37 +0300 Subject: [PATCH 253/268] added a more strict condition for neoforge forge support Signed-off-by: Trial97 --- launcher/minecraft/PackProfile.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp index 31974cd00..aea78b552 100644 --- a/launcher/minecraft/PackProfile.cpp +++ b/launcher/minecraft/PackProfile.cpp @@ -1018,8 +1018,7 @@ std::optional PackProfile::getSupportedModLoaders() // TODO: remove this or add version condition once Quilt drops official Fabric support if (loaders & ModPlatform::Quilt) loaders |= ModPlatform::Fabric; - Version instance_ver{ getComponentVersion("net.minecraft") }; - if (instance_ver <= Version("1.20.1") && loaders & ModPlatform::NeoForge) + if (getComponentVersion("net.minecraft") == "1.20.1" && loaders & ModPlatform::NeoForge) loaders |= ModPlatform::Forge; return loaders; } From c6c17036e3842e0197c1c1aa4207f45610b812a7 Mon Sep 17 00:00:00 2001 From: nea Date: Wed, 4 Oct 2023 21:20:59 +0200 Subject: [PATCH 254/268] Add more Java installation dirs Signed-off-by: nea --- launcher/java/JavaUtils.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/launcher/java/JavaUtils.cpp b/launcher/java/JavaUtils.cpp index 3512c3079..cca1ed6d4 100644 --- a/launcher/java/JavaUtils.cpp +++ b/launcher/java/JavaUtils.cpp @@ -403,6 +403,14 @@ QList JavaUtils::FindJavaPaths() scanJavaDirs("/opt/jdks"); // flatpak scanJavaDirs("/app/jdk"); + + auto home = qEnvironmentVariable("HOME"); + + // javas downloaded by IntelliJ + scanJavaDirs(FS::PathCombine(home, ".jdks")); + // javas downloaded by sdkman + scanJavaDirs(FS::PathCombine(home, ".sdkman/candidates/java")); + javas = addJavasFromEnv(javas); javas.removeDuplicates(); return javas; From b67c2c71d10945775b9121c5f22e2b1ac5531fe8 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 5 Oct 2023 22:44:49 +0300 Subject: [PATCH 255/268] removed windows legacy builds Signed-off-by: Trial97 --- .github/workflows/build.yml | 12 ------------ .github/workflows/trigger_release.yml | 10 +++------- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 059795a11..4665a7d56 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -54,18 +54,6 @@ jobs: msystem: clang64 vcvars_arch: 'amd64_x86' - - os: windows-2022 - name: "Windows-MSVC-Legacy" - msystem: '' - architecture: 'win32' - vcvars_arch: 'amd64_x86' - qt_ver: 5 - qt_host: windows - qt_arch: 'win32_msvc2019' - qt_version: '5.15.2' - qt_modules: '' - qt_tools: 'tools_openssl_x86' - - os: windows-2022 name: "Windows-MSVC" msystem: '' diff --git a/.github/workflows/trigger_release.yml b/.github/workflows/trigger_release.yml index bda75e354..b67346d80 100644 --- a/.github/workflows/trigger_release.yml +++ b/.github/workflows/trigger_release.yml @@ -3,10 +3,9 @@ name: Build Application and Make Release on: push: tags: - - '*' + - "*" jobs: - build_release: name: Build Release uses: ./.github/workflows/build.yml @@ -28,8 +27,8 @@ jobs: - name: Checkout uses: actions/checkout@v4 with: - submodules: 'true' - path: 'PrismLauncher-source' + submodules: "true" + path: "PrismLauncher-source" - name: Download artifacts uses: actions/download-artifact@v3 - name: Grab and store version @@ -95,9 +94,6 @@ jobs: 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 From d49f81d132f57089361dd7f8af0e0ef1781028e0 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 6 Oct 2023 11:27:04 +0100 Subject: [PATCH 256/268] Drop leftover mod loader settings I messed up the merge. Signed-off-by: TheKodeToad --- launcher/Application.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 6209acf76..e5bbf2f30 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -579,9 +579,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) m_settings->registerSetting("IgnoreJavaCompatibility", false); m_settings->registerSetting("IgnoreJavaWizard", false); - // Mod loader settings - m_settings->registerSetting("DisableQuiltBeacon", false); - // Legacy settings m_settings->registerSetting("OnlineFixes", true); From bca5e8f3952d44a1ff517123ff51ab439b295f84 Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Fri, 6 Oct 2023 16:16:03 +0300 Subject: [PATCH 257/268] Update launcher/minecraft/PackProfile.cpp Co-authored-by: TheKodeToad Signed-off-by: Alexandru Ionut Tripon --- launcher/minecraft/PackProfile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp index aea78b552..9e706ae0a 100644 --- a/launcher/minecraft/PackProfile.cpp +++ b/launcher/minecraft/PackProfile.cpp @@ -1018,7 +1018,7 @@ std::optional PackProfile::getSupportedModLoaders() // TODO: remove this or add version condition once Quilt drops official Fabric support if (loaders & ModPlatform::Quilt) loaders |= ModPlatform::Fabric; - if (getComponentVersion("net.minecraft") == "1.20.1" && loaders & ModPlatform::NeoForge) + if (getComponentVersion("net.minecraft") == "1.20.1" && (loaders & ModPlatform::NeoForge)) loaders |= ModPlatform::Forge; return loaders; } From 05caa874bc3550c370ea6f278c1849cc481b8125 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 8 Oct 2023 00:18:30 +0000 Subject: [PATCH 258/268] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'flake-compat': 'github:edolstra/flake-compat/35bb57c0c8d8b62bbfd284272c928ceb64ddbde9' (2023-01-17) → 'github:edolstra/flake-compat/0f9255e01c2351cc7d116c072cb317785dd33b33' (2023-10-04) • Updated input 'flake-parts': 'github:hercules-ci/flake-parts/7f53fdb7bdc5bb237da7fefef12d099e4fd611ca' (2023-09-01) → 'github:hercules-ci/flake-parts/c9afaba3dfa4085dbd2ccb38dfade5141e33d9d4' (2023-10-03) • Updated input 'flake-parts/nixpkgs-lib': 'github:NixOS/nixpkgs/3e52e76b70d5508f3cec70b882a29199f4d1ee85?dir=lib' (2023-08-31) → 'github:NixOS/nixpkgs/f5892ddac112a1e9b3612c39af1b72987ee5783a?dir=lib' (2023-09-29) • Updated input 'nixpkgs': 'github:nixos/nixpkgs/bd9b686c0168041aea600222be0805a0de6e6ab8' (2023-09-29) → 'github:nixos/nixpkgs/2de1be5b51c3d6fa833f1c1f222dc867dd054b31' (2023-10-07) • Updated input 'pre-commit-hooks': 'github:cachix/pre-commit-hooks.nix/cb770e93516a1609652fa8e945a0f310e98f10c0' (2023-09-24) → 'github:cachix/pre-commit-hooks.nix/66c352d33e0907239e4a69416334f64af2c685cc' (2023-10-05) --- flake.lock | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/flake.lock b/flake.lock index 3ffe2dc8e..b381db5ae 100644 --- a/flake.lock +++ b/flake.lock @@ -3,11 +3,11 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1673956053, - "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", "owner": "edolstra", "repo": "flake-compat", - "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", "type": "github" }, "original": { @@ -21,11 +21,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1693611461, - "narHash": "sha256-aPODl8vAgGQ0ZYFIRisxYG5MOGSkIczvu2Cd8Gb9+1Y=", + "lastModified": 1696343447, + "narHash": "sha256-B2xAZKLkkeRFG5XcHHSXXcP7To9Xzr59KXeZiRf4vdQ=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "7f53fdb7bdc5bb237da7fefef12d099e4fd611ca", + "rev": "c9afaba3dfa4085dbd2ccb38dfade5141e33d9d4", "type": "github" }, "original": { @@ -106,11 +106,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1695978539, - "narHash": "sha256-lta5HToBZMWZ2hl5CautNSUgIZViR41QxN7JKbMAjgQ=", + "lastModified": 1696661029, + "narHash": "sha256-GIB5VTkvsDIqfMpdtuetOzpm64P8wm8nBSv5Eo8XM3Y=", "owner": "nixos", "repo": "nixpkgs", - "rev": "bd9b686c0168041aea600222be0805a0de6e6ab8", + "rev": "2de1be5b51c3d6fa833f1c1f222dc867dd054b31", "type": "github" }, "original": { @@ -123,11 +123,11 @@ "nixpkgs-lib": { "locked": { "dir": "lib", - "lastModified": 1693471703, - "narHash": "sha256-0l03ZBL8P1P6z8MaSDS/MvuU8E75rVxe5eE1N6gxeTo=", + "lastModified": 1696019113, + "narHash": "sha256-X3+DKYWJm93DRSdC5M6K5hLqzSya9BjibtBsuARoPco=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "3e52e76b70d5508f3cec70b882a29199f4d1ee85", + "rev": "f5892ddac112a1e9b3612c39af1b72987ee5783a", "type": "github" }, "original": { @@ -153,11 +153,11 @@ ] }, "locked": { - "lastModified": 1695576016, - "narHash": "sha256-71KxwRhTfVuh7kNrg3/edNjYVg9DCyKZl2QIKbhRggg=", + "lastModified": 1696516544, + "narHash": "sha256-8rKE8Je6twTNFRTGF63P9mE3lZIq917RAicdc4XJO80=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "cb770e93516a1609652fa8e945a0f310e98f10c0", + "rev": "66c352d33e0907239e4a69416334f64af2c685cc", "type": "github" }, "original": { From 5f5214e5dd21d70efb8481bf3aea0bb0dabf4b12 Mon Sep 17 00:00:00 2001 From: Muslim <42213155+GitMuslim@users.noreply.github.com> Date: Mon, 9 Oct 2023 17:00:57 +0300 Subject: [PATCH 259/268] add fullstop Signed-off-by: Muslim <42213155+GitMuslim@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 641622b5c..8761b950a 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,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?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-git) [![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--qt5--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-qt5-git) [![prismlauncher-git](https://img.shields.io/badge/mpr-prismlauncher--git-A80030?label=MPR&logo=debian&logoColor=white)](https://mpr.makedeb.org/packages/prismlauncher-git)
[![prismlauncher-nightly](https://img.shields.io/badge/copr-prismlauncher--nightly-51A2DA?label=COPR&logo=fedora&logoColor=white)](https://copr.fedorainfracloud.org/coprs/g3tchoo/prismlauncher/) [![prismlauncher-nightly](https://img.shields.io/badge/OBS-prismlauncher--nightly-3AB6A9?logo=opensuse&logoColor=white)](https://build.opensuse.org/project/show/home:getchoo) [![prismlauncher-9999](https://img.shields.io/badge/gentoo-prismlauncher--9999-4D4270?label=Gentoo&logo=gentoo&logoColor=white)](https://packages.gentoo.org/packages/games-action/prismlauncher) +[![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-git) [![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--qt5--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-qt5-git) [![prismlauncher-git](https://img.shields.io/badge/mpr-prismlauncher--git-A80030?label=MPR&logo=debian&logoColor=white)](https://mpr.makedeb.org/packages/prismlauncher-git)
[![prismlauncher-nightly](https://img.shields.io/badge/copr-prismlauncher--nightly-51A2DA?label=COPR&logo=fedora&logoColor=white)](https://copr.fedorainfracloud.org/coprs/g3tchoo/prismlauncher/) [![prismlauncher-nightly](https://img.shields.io/badge/OBS-prismlauncher--nightly-3AB6A9?logo=opensuse&logoColor=white)](https://build.opensuse.org/project/show/home:getchoo) [![prismlauncher-9999](https://img.shields.io/badge/gentoo-prismlauncher--9999-4D4270?label=Gentoo&logo=gentoo&logoColor=white)](https://packages.gentoo.org/packages/games-action/prismlauncher). These packages are also availiable to all the distributions based on the ones mentioned above. From 8eb8f167ab86647da60fe81997acc397ddbd438d Mon Sep 17 00:00:00 2001 From: Muslim <42213155+GitMuslim@users.noreply.github.com> Date: Mon, 9 Oct 2023 18:02:56 +0300 Subject: [PATCH 260/268] oops Signed-off-by: Muslim <42213155+GitMuslim@users.noreply.github.com> --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8761b950a..093bc94da 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,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?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-git) [![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--qt5--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-qt5-git) [![prismlauncher-git](https://img.shields.io/badge/mpr-prismlauncher--git-A80030?label=MPR&logo=debian&logoColor=white)](https://mpr.makedeb.org/packages/prismlauncher-git)
[![prismlauncher-nightly](https://img.shields.io/badge/copr-prismlauncher--nightly-51A2DA?label=COPR&logo=fedora&logoColor=white)](https://copr.fedorainfracloud.org/coprs/g3tchoo/prismlauncher/) [![prismlauncher-nightly](https://img.shields.io/badge/OBS-prismlauncher--nightly-3AB6A9?logo=opensuse&logoColor=white)](https://build.opensuse.org/project/show/home:getchoo) [![prismlauncher-9999](https://img.shields.io/badge/gentoo-prismlauncher--9999-4D4270?label=Gentoo&logo=gentoo&logoColor=white)](https://packages.gentoo.org/packages/games-action/prismlauncher). +[![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-git) [![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--qt5--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-qt5-git) [![prismlauncher-git](https://img.shields.io/badge/mpr-prismlauncher--git-A80030?label=MPR&logo=debian&logoColor=white)](https://mpr.makedeb.org/packages/prismlauncher-git)
[![prismlauncher-nightly](https://img.shields.io/badge/copr-prismlauncher--nightly-51A2DA?label=COPR&logo=fedora&logoColor=white)](https://copr.fedorainfracloud.org/coprs/g3tchoo/prismlauncher/) [![prismlauncher-nightly](https://img.shields.io/badge/OBS-prismlauncher--nightly-3AB6A9?logo=opensuse&logoColor=white)](https://build.opensuse.org/project/show/home:getchoo) [![prismlauncher-9999](https://img.shields.io/badge/gentoo-prismlauncher--9999-4D4270?label=Gentoo&logo=gentoo&logoColor=white)](https://packages.gentoo.org/packages/games-action/prismlauncher) These packages are also availiable to all the distributions based on the ones mentioned above. @@ -50,7 +50,7 @@ Feel free to create a GitHub issue if you find a bug or want to suggest a new fe ## Translations -The translation effort for Prism Launcher is hosted on [Weblate](https://hosted.weblate.org/projects/prismlauncher/launcher/) and information about translating Prism Launcher is available at +The translation effort for Prism Launcher is hosted on [Weblate](https://hosted.weblate.org/projects/prismlauncher/launcher/) and information about translating Prism Launcher is available at . ## Building From 5cb6d931360ff9924164f4bc08050674a93b87aa Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 9 Oct 2023 22:25:45 +0300 Subject: [PATCH 261/268] fixed updater build Signed-off-by: Trial97 --- launcher/net/NetJob.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/launcher/net/NetJob.cpp b/launcher/net/NetJob.cpp index b99c5acb0..784d81c37 100644 --- a/launcher/net/NetJob.cpp +++ b/launcher/net/NetJob.cpp @@ -36,11 +36,16 @@ */ #include "NetJob.h" +#if defined(LAUNCHER_APPLICATION) #include "Application.h" +#endif -NetJob::NetJob(QString job_name, shared_qobject_ptr network) - : ConcurrentTask(nullptr, job_name, APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()), m_network(network) -{} +NetJob::NetJob(QString job_name, shared_qobject_ptr network) : ConcurrentTask(nullptr, job_name), m_network(network) +{ +#if defined(LAUNCHER_APPLICATION) + setMaxConcurrent(APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); +#endif +} auto NetJob::addNetAction(NetAction::Ptr action) -> bool { From 32eaaa25d984519a3fddc9844f921c3b22f4d5de Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 13 Oct 2023 00:04:36 +0300 Subject: [PATCH 262/268] removed the better release for modrinth modpacks Signed-off-by: Trial97 --- launcher/modplatform/modrinth/ModrinthPackManifest.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp index a154317fe..c1531a94d 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp @@ -111,9 +111,8 @@ void loadIndexedVersions(Modpack& pack, QJsonDocument& doc) unsortedVersions.append(file); } auto orderSortPredicate = [](const ModpackVersion& a, const ModpackVersion& b) -> bool { - bool a_better_release = a.version_type <= b.version_type; // dates are in RFC 3339 format - return a.date > b.date && a_better_release; + return a.date > b.date; }; std::sort(unsortedVersions.begin(), unsortedVersions.end(), orderSortPredicate); From 023b3e3c3945592845fcae6eec8fc9d90871d2c2 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 14 Oct 2023 09:40:56 +0300 Subject: [PATCH 263/268] Fixed arch build Signed-off-by: Trial97 --- launcher/translations/TranslationsModel.cpp | 36 +++++++++++---------- launcher/translations/TranslationsModel.h | 4 ++- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/launcher/translations/TranslationsModel.cpp b/launcher/translations/TranslationsModel.cpp index 933fe2d35..56ade8e32 100644 --- a/launcher/translations/TranslationsModel.cpp +++ b/launcher/translations/TranslationsModel.cpp @@ -206,7 +206,7 @@ void TranslationsModel::indexReceived() reloadLocalFiles(); auto language = d->m_system_locale; - if (!findLanguage(language)) { + if (!findLanguageAsOptional(language).has_value()) { language = d->m_system_language; } selectLanguage(language); @@ -417,14 +417,17 @@ int TranslationsModel::columnCount([[maybe_unused]] const QModelIndex& parent) c return 2; } -Language* TranslationsModel::findLanguage(const QString& key) +QVector::Iterator TranslationsModel::findLanguage(const QString& key) { - auto found = std::find_if(d->m_languages.begin(), d->m_languages.end(), [&](Language& lang) { return lang.key == key; }); - if (found == d->m_languages.end()) { - return nullptr; - } else { - return found; - } + return std::find_if(d->m_languages.begin(), d->m_languages.end(), [&](Language& lang) { return lang.key == key; }); +} + +std::optional TranslationsModel::findLanguageAsOptional(const QString& key) +{ + auto found = findLanguage(key); + if (found != d->m_languages.end()) + return *found; + return {}; } void TranslationsModel::setUseSystemLocale(bool useSystemLocale) @@ -436,13 +439,13 @@ void TranslationsModel::setUseSystemLocale(bool useSystemLocale) bool TranslationsModel::selectLanguage(QString key) { QString& langCode = key; - auto langPtr = findLanguage(key); + auto langPtr = findLanguageAsOptional(key); if (langCode.isEmpty()) { d->no_language_set = true; } - if (!langPtr) { + if (!langPtr.has_value()) { qWarning() << "Selected invalid language" << key << ", defaulting to" << defaultLangCode; langCode = defaultLangCode; } else { @@ -527,9 +530,8 @@ bool TranslationsModel::selectLanguage(QString key) QModelIndex TranslationsModel::selectedIndex() { auto found = findLanguage(d->m_selectedLanguage); - if (found) { - // QVector iterator freely converts to pointer to contained type - return index(found - d->m_languages.begin(), 0, QModelIndex()); + if (found != d->m_languages.end()) { + return index(std::distance(d->m_languages.begin(), found), 0, QModelIndex()); } return QModelIndex(); } @@ -562,8 +564,8 @@ void TranslationsModel::updateLanguage(QString key) qWarning() << "Cannot update builtin language" << key; return; } - auto found = findLanguage(key); - if (!found) { + auto found = findLanguageAsOptional(key); + if (!found.has_value()) { qWarning() << "Cannot update invalid language" << key; return; } @@ -578,8 +580,8 @@ void TranslationsModel::downloadTranslation(QString key) d->m_nextDownload = key; return; } - auto lang = findLanguage(key); - if (!lang) { + auto lang = findLanguageAsOptional(key); + if (!lang.has_value()) { qWarning() << "Will not download an unknown translation" << key; return; } diff --git a/launcher/translations/TranslationsModel.h b/launcher/translations/TranslationsModel.h index cff23ce74..96a0e9f8b 100644 --- a/launcher/translations/TranslationsModel.h +++ b/launcher/translations/TranslationsModel.h @@ -17,6 +17,7 @@ #include #include +#include struct Language; @@ -40,7 +41,8 @@ class TranslationsModel : public QAbstractListModel { void setUseSystemLocale(bool useSystemLocale); private: - Language* findLanguage(const QString& key); + QVector::Iterator findLanguage(const QString& key); + std::optional findLanguageAsOptional(const QString& key); void reloadLocalFiles(); void downloadTranslation(QString key); void downloadNext(); From 4939a33456916ceb03fbe04bb850089895cbf62d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 15 Oct 2023 00:18:39 +0000 Subject: [PATCH 264/268] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:nixos/nixpkgs/2de1be5b51c3d6fa833f1c1f222dc867dd054b31' (2023-10-07) → 'github:nixos/nixpkgs/01441e14af5e29c9d27ace398e6dd0b293e25a54' (2023-10-11) • Updated input 'pre-commit-hooks': 'github:cachix/pre-commit-hooks.nix/66c352d33e0907239e4a69416334f64af2c685cc' (2023-10-05) → 'github:cachix/pre-commit-hooks.nix/42e1b6095ef80a51f79595d9951eb38e91c4e6ca' (2023-10-09) --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index b381db5ae..ad18ff615 100644 --- a/flake.lock +++ b/flake.lock @@ -106,11 +106,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1696661029, - "narHash": "sha256-GIB5VTkvsDIqfMpdtuetOzpm64P8wm8nBSv5Eo8XM3Y=", + "lastModified": 1697009197, + "narHash": "sha256-viVRhBTFT8fPJTb1N3brQIpFZnttmwo3JVKNuWRVc3s=", "owner": "nixos", "repo": "nixpkgs", - "rev": "2de1be5b51c3d6fa833f1c1f222dc867dd054b31", + "rev": "01441e14af5e29c9d27ace398e6dd0b293e25a54", "type": "github" }, "original": { @@ -153,11 +153,11 @@ ] }, "locked": { - "lastModified": 1696516544, - "narHash": "sha256-8rKE8Je6twTNFRTGF63P9mE3lZIq917RAicdc4XJO80=", + "lastModified": 1696846637, + "narHash": "sha256-0hv4kbXxci2+pxhuXlVgftj/Jq79VSmtAyvfabCCtYk=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "66c352d33e0907239e4a69416334f64af2c685cc", + "rev": "42e1b6095ef80a51f79595d9951eb38e91c4e6ca", "type": "github" }, "original": { From 7112d04df58e3cdf1e149157fe133f76c888da5e Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sun, 15 Oct 2023 14:11:08 +0100 Subject: [PATCH 265/268] Mark setting as optional Signed-off-by: TheKodeToad --- launcher/Application.cpp | 2 +- launcher/ui/pages/global/MinecraftPage.ui | 4 ++-- launcher/ui/pages/instance/InstanceSettingsPage.ui | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index e5bbf2f30..73f594d17 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -580,7 +580,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) m_settings->registerSetting("IgnoreJavaWizard", false); // Legacy settings - m_settings->registerSetting("OnlineFixes", true); + m_settings->registerSetting("OnlineFixes", false); // Native library workarounds m_settings->registerSetting("UseNativeOpenAL", false); diff --git a/launcher/ui/pages/global/MinecraftPage.ui b/launcher/ui/pages/global/MinecraftPage.ui index 79785fc92..2a3c0d96d 100644 --- a/launcher/ui/pages/global/MinecraftPage.ui +++ b/launcher/ui/pages/global/MinecraftPage.ui @@ -138,7 +138,7 @@
- + Show time spent playing in hours @@ -209,7 +209,7 @@ <html><head/><body><p>Emulates usages of old online services which are no longer operating.</p><p>This currently allows modern skins to be used.</p></body></html> - Enable online fixes + Enable online fixes (experimental) diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index f4f4c5a9d..20992df6d 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -39,7 +39,7 @@ QTabWidget::Rounded - 0 + 5 @@ -601,7 +601,7 @@ <html><head/><body><p>Emulates usages of old online services which are no longer operating.</p><p>This currently allows modern skins to be used.</p></body></html> - Enable online fixes + Enable online fixes (experimental) From 6fb7a98901784ecb1052db2aa8e0dca1b3f0d920 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sun, 15 Oct 2023 14:45:05 +0100 Subject: [PATCH 266/268] =?UTF-8?q?Fix=20small=20mistake=20=F0=9F=98=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: TheKodeToad --- launcher/ui/pages/instance/InstanceSettingsPage.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index 20992df6d..8defaccb3 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -39,7 +39,7 @@ QTabWidget::Rounded - 5 + 0 From ef0813754be290ed842b5acaa62d2de7fa620291 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 15 Oct 2023 17:56:11 +0300 Subject: [PATCH 267/268] added explicit qt version for linux portable Signed-off-by: Trial97 --- .github/workflows/build.yml | 56 +++++++++++++-------------- .github/workflows/trigger_release.yml | 10 ++--- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3c05747d0..ee5662ba6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,58 +43,58 @@ jobs: - os: ubuntu-20.04 qt_ver: 6 qt_host: linux - qt_arch: '' - qt_version: '6.2.4' - qt_modules: 'qt5compat qtimageformats' - qt_tools: '' + qt_arch: "" + qt_version: "6.2.4" + qt_modules: "qt5compat qtimageformats" + qt_tools: "" - os: windows-2022 name: "Windows-MinGW-w64" msystem: clang64 - vcvars_arch: 'amd64_x86' + vcvars_arch: "amd64_x86" - os: windows-2022 name: "Windows-MSVC" - msystem: '' - architecture: 'x64' - vcvars_arch: 'amd64' + msystem: "" + architecture: "x64" + vcvars_arch: "amd64" qt_ver: 6 qt_host: windows - qt_arch: '' - qt_version: '6.5.2' - qt_modules: 'qt5compat qtimageformats' - qt_tools: '' + qt_arch: "" + qt_version: "6.5.2" + qt_modules: "qt5compat qtimageformats" + qt_tools: "" - os: windows-2022 name: "Windows-MSVC-arm64" - msystem: '' - architecture: 'arm64' - vcvars_arch: 'amd64_arm64' + msystem: "" + architecture: "arm64" + vcvars_arch: "amd64_arm64" qt_ver: 6 qt_host: windows - qt_arch: 'win64_msvc2019_arm64' - qt_version: '6.5.2' - qt_modules: 'qt5compat qtimageformats' - qt_tools: '' + qt_arch: "win64_msvc2019_arm64" + qt_version: "6.5.2" + qt_modules: "qt5compat qtimageformats" + qt_tools: "" - os: macos-12 name: macOS macosx_deployment_target: 11.0 qt_ver: 6 qt_host: mac - qt_arch: '' - qt_version: '6.5.2' - qt_modules: 'qt5compat qtimageformats' - qt_tools: '' + qt_arch: "" + qt_version: "6.5.2" + qt_modules: "qt5compat qtimageformats" + qt_tools: "" - os: macos-12 name: macOS-Legacy macosx_deployment_target: 10.13 qt_ver: 5 qt_host: mac - qt_version: '5.15.2' - qt_modules: '' - qt_tools: '' + qt_version: "5.15.2" + qt_modules: "" + qt_tools: "" runs-on: ${{ matrix.os }} @@ -547,14 +547,14 @@ jobs: if: runner.os == 'Linux' && matrix.qt_ver != 6 uses: actions/upload-artifact@v3 with: - name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }} + name: PrismLauncher-${{ runner.os }}-Qt5-${{ env.VERSION }}-${{ inputs.build_type }} path: PrismLauncher.tar.gz - name: Upload binary tarball (Linux, portable, Qt 5) if: runner.os == 'Linux' && matrix.qt_ver != 6 uses: actions/upload-artifact@v3 with: - name: PrismLauncher-${{ runner.os }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }} + name: PrismLauncher-${{ runner.os }}-Qt5-Portable-${{ env.VERSION }}-${{ inputs.build_type }} path: PrismLauncher-portable.tar.gz - name: Upload binary tarball (Linux, Qt 6) diff --git a/.github/workflows/trigger_release.yml b/.github/workflows/trigger_release.yml index 98842664d..28578165f 100644 --- a/.github/workflows/trigger_release.yml +++ b/.github/workflows/trigger_release.yml @@ -41,9 +41,9 @@ jobs: run: | mv ${{ github.workspace }}/PrismLauncher-source PrismLauncher-${{ env.VERSION }} mv PrismLauncher-Linux-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz - mv PrismLauncher-Linux-Qt6*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz - mv PrismLauncher-Linux-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz - mv PrismLauncher-Linux*/PrismLauncher.tar.gz PrismLauncher-Linux-${{ env.VERSION }}.tar.gz + mv PrismLauncher-Linux-Qt6*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz + mv PrismLauncher-Linux-Qt5-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz + mv PrismLauncher-Linux-Qt5*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt5-${{ env.VERSION }}.tar.gz mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-x86_64.AppImage mv PrismLauncher-*.AppImage.zsync/PrismLauncher-*.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync mv PrismLauncher-macOS-Legacy*/PrismLauncher.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz @@ -87,8 +87,8 @@ jobs: draft: true prerelease: false files: | - PrismLauncher-Linux-${{ env.VERSION }}.tar.gz - PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz + PrismLauncher-Linux-Qt5-${{ env.VERSION }}.tar.gz + PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz PrismLauncher-Linux-x86_64.AppImage PrismLauncher-Linux-x86_64.AppImage.zsync PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz From 6c9856b9dc8380cec9060156e464ca24b5533a5a Mon Sep 17 00:00:00 2001 From: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Date: Mon, 16 Oct 2023 15:42:16 +0200 Subject: [PATCH 268/268] chore: bump Qt to Qt 6.6.0 Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4665a7d56..0521c00c7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -62,7 +62,7 @@ jobs: qt_ver: 6 qt_host: windows qt_arch: '' - qt_version: '6.5.2' + qt_version: '6.6.0' qt_modules: 'qt5compat qtimageformats' qt_tools: '' @@ -74,7 +74,7 @@ jobs: qt_ver: 6 qt_host: windows qt_arch: 'win64_msvc2019_arm64' - qt_version: '6.5.2' + qt_version: '6.6.0' qt_modules: 'qt5compat qtimageformats' qt_tools: '' @@ -84,7 +84,7 @@ jobs: qt_ver: 6 qt_host: mac qt_arch: '' - qt_version: '6.5.2' + qt_version: '6.6.0' qt_modules: 'qt5compat qtimageformats' qt_tools: ''