From b544661e81bfe4358e41b001aef79579e1bbd324 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 11 Nov 2022 10:32:54 +0000 Subject: [PATCH 001/247] 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.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 @@ private static ExitCode listen() { } 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/247] 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 | 71 +++++----- .../org/prismlauncher/utils/UrlUtils.java | 121 ++++++++++++++++++ 5 files changed, 176 insertions(+), 57 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 @@ 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 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 @@ private URL redirect(URL address) throws IOException { @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); + @Override + public String getName() { + return "legacySkinFix"; + } - 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); + @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; } - } - static URLConnection openConnection(URL url) throws Throwable { - return (URLConnection) openConnection.invokeExact(http, url); + return true; } - static URLConnection openConnection(URL url, Proxy proxy) throws Throwable { - return (URLConnection) openConnection2.invokeExact(http, url, proxy); - } + 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; + } - @Override - public String getName() { - return "legacySkinFix"; - } + if (!UrlUtils.isSupported()) { + Log.warning("Cannot access the necessary Java internals for skin fix"); + return false; + } - @Override - public boolean isApplicable(Parameters parameters) { - return http != null && openConnection != null; + return true; } @Override @@ -99,7 +94,7 @@ public void apply() { @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/247] 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.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 @@ private static Map getTextures(String owner) throws IOException 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 String getName() { @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 @@ private boolean readNull() throws IOException { 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 @@ * 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/247] 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 String getName() { 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/247] 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 | 58 +++++++--------- .../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, 152 insertions(+), 78 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 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); - return UrlUtils.openHttpConnection(address, proxy); - } - private URL convertSkin(URL defaultUrl, String owner) throws IOException { - Map textures = getTextures(owner); + if (address == null) + return NullConnection.INSTANCE; - if (textures != null) { - textures = (Map) textures.get("SKIN"); - return new URL((String) textures.get("url")); - } - - return defaultUrl; + return UrlUtils.openHttpConnection(address, proxy); } - private URL convertCape(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("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 @@ private static Map getTextures(String owner) throws IOException Map result = (Map) JsonParser .parse(new String(Base64.decode((String) property.get("value")))); result = (Map) result.get("textures"); + return result; } } @@ -128,11 +119,11 @@ private static Map getTextures(String owner) throws IOException 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 @@ private static String findSkinOwner(URL address) { 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 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.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 @@ private boolean readNull() throws IOException { 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 static URLConnection openConnection(URLStreamHandler handler, URL url) th } } - 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/247] 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 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 boolean isApplicable(Parameters params) { @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/247] 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.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 String getName() { @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; } } + } + + /** + * Determines whether base64 is supported. + * + * @return true if base64 can be parsed + */ + public static boolean isSupported() { + return supported; + } - // support for Java versions < 8 - return DatatypeConverter.parseBase64Binary(input); + 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 static boolean isSupported() { } 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/247] 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 @@ class JavaVersion 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 String getName() { 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/247] 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 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 @@ protected URLConnection openConnection(URL address) throws IOException { @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 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 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 static URLConnection openConnection(URLStreamHandler handler, URL url, Pr } 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/247] 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 @@ * @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 @@ private URL redirect(URL address) throws IOException { 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/247] 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 @@ private boolean readNull() throws IOException { 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/247] 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.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 @@ protected URLConnection openConnection(URL address) throws IOException { @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.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"); - - try (InputStream profileIn = new URL("https://sessionserver.mojang.com/session/minecraft/profile/" + id) - .openStream()) { - Map profile = (Map) JsonParser.parse(profileIn); + private static URLConnection getSkinConnection(String owner, Proxy proxy) throws IOException { + Texture texture = MojangApi.getTexture(MojangApi.getUuid(owner), "SKIN"); + if (texture == null) + return null; + + 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)); + + BufferedImage subimage; + + if (image.getHeight() > 32) { + // flatten second layers + subimage = image.getSubimage(0, 32, 56, 16); + graphics.drawImage(subimage, 0, 16, null); + } - 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"); + if (texture.isSlim()) { + // convert slim to wide + subimage = image.getSubimage(45, 16, 9, 16); + graphics.drawImage(subimage, 46, 16, null); - return result; - } - } + subimage = image.getSubimage(49, 16, 2, 4); + graphics.drawImage(subimage, 50, 16, null); - return 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 static boolean isSupported() { 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/247] 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 (99%) 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 @@ class MinecraftInstance: public BaseInstance /// 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 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 @@ /** * 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 LegacyLauncher(Parameters params) { @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 void launch() throws Throwable { 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 void launch() throws Throwable { 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.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 @@ */ 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 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.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 99% 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.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 @@ private static ExitCode listen() { } try { - Fixes.apply(params); + LegacyProxy.applyOnlineFixes(params); Launcher launcher; String type = params.getString("launcher"); @@ -119,7 +118,7 @@ private static ExitCode listen() { 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 @@ 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/247] 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 Launcher(Applet applet, URL documentBase) { 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 @@ public void start(String user, String session, int width, int height, boolean ma 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.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 @@ public void launch() throws Throwable { } } - // 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 @@ public void launch() throws Throwable { 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.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 static void apply(Parameters params) { 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 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 @@ static URLConnection openConnection(URL address, Proxy proxy) throws IOException 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 @@ private static URLConnection getSkinConnection(String owner, Proxy proxy) throws 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 @@ private static URLConnection getSkinConnection(String owner, Proxy proxy) throws } 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 @@ private static URLConnection getSkinConnection(String owner, Proxy proxy) throws 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.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 @@ private Double readNumber() throws IOException { if (character() == '0') { result.append((char) character()); read(); + if (isDigit()) throw new JsonParseException("Found superfluous leading zero"); } else if (!isDigit()) @@ -395,11 +397,10 @@ private Boolean readBoolean() throws IOException { 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.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 static String getUuid(String username) throws IOException { } } - 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/247] 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 @@ private static String findSkinOwner(URL address) { 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/247] 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.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.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/247] 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/247] 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/247] 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 JsonParseException(String message) { 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 static URLConnection openConnection(URLStreamHandler handler, URL url, Pr } } -} \ 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/247] 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 static void apply(Parameters params) { 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 static void apply(Parameters params) { 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/247] 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/247] 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/247] 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/247] 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/247] 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/247] 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/247] 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/247] 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/247] 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/247] 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/247] 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/247] 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/247] 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/247] 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/247] 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/247] 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/247] 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/247] 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/247] 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/247] 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/247] 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/247] 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/247] 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/247] 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/247] 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/247] 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/247] 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/247] 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/247] 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 | 91 +++++++++---------- launcher/updater/prismupdater/PrismUpdater.h | 17 +++- 5 files changed, 91 insertions(+), 57 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"); - - 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 responce = std::make_shared(); + auto download = Net::Download::makeByteArray(page_url, responce.get()); + download->setNetwork(m_network); + m_current_url = page_url; + + 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/247] 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(); - GitHubRelease release = dlg.selectedRelease(); 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 {}; + } + + 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/247] 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 install" << selected_asset; + auto file = downloadAsset(selected_asset); + + if (!file.exists()) { + return showFatalErrorMessage(tr("Failed to Download"), tr("Failed to download the selected asset.")); } - qDebug() << "will intall" << 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/247] 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/247] 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 - // 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(); + +// 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); + } + } + } } - if(freopen("CON", "w", stderr)) - { - std::cerr.sync_with_stdio(); + } + + // 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); + } + } + } } - if(freopen("CON", "r", stdin)) - { - std::cin.sync_with_stdio(); + } + + // 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); + } + } + } } - auto out = GetStdHandle (STD_OUTPUT_HANDLE); - DWORD written; - const char * endline = "\n"; - WriteConsole(out, endline, strlen(endline), &written, NULL); + } + + // 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)) { + 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; - } - proxyDesc += QString("%1:%2") - .arg(proxy.hostName()) - .arg(proxy.port()); + 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()); 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/247] 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 | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 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; - - 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")) + 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 = !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/247] 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/247] 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 35c233d4c506b45587d30ce6be8d0468dfad1784 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sat, 24 Jun 2023 22:30:09 -0700 Subject: [PATCH 057/247] DCO Remediation Commit for Rachel Powers <508861+Ryex@users.noreply.github.com> I, Rachel Powers <508861+Ryex@users.noreply.github.com>, hereby add my Signed-off-by to this commit: f5729d9a7c953d88ae60e106a787b1d7c35ede8b Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> 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/247] 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/247] 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/247] 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/247] 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/247] 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/247] 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 @@ class Application : public QApplication 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 slots: 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/247] 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/247] 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/247] 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/247] 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 -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(); + +// 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); + } + } + } } - if (freopen("CON", "w", stderr)) { - std::cerr.sync_with_stdio(); + } + + // 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); + } + } + } } - if (freopen("CON", "r", stdin)) { - std::cin.sync_with_stdio(); + } + + // 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 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; } - 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/247] 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/247] 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/247] 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/247] 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/247] 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/247] 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/247] 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/247] 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 25ffc4c4b0239ba782a5d10397965a6a61885d82 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 29 Jun 2023 17:58:09 +0300 Subject: [PATCH 076/247] Refactor ImgurUpload Signed-off-by: Trial97 --- launcher/CMakeLists.txt | 1 + launcher/net/NetRequest.cpp | 2 + launcher/net/StaticHeaderProxy.h | 39 ++++++ launcher/screenshots/ImgurAlbumCreation.cpp | 121 ++++++++---------- launcher/screenshots/ImgurAlbumCreation.h | 60 ++++----- launcher/screenshots/ImgurUpload.cpp | 119 +++++++---------- launcher/screenshots/ImgurUpload.h | 47 ++++--- .../ui/pages/instance/ScreenshotsPage.cpp | 70 ++++------ 8 files changed, 223 insertions(+), 236 deletions(-) create mode 100644 launcher/net/StaticHeaderProxy.h diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 44988db20..e5fb815c3 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -139,6 +139,7 @@ set(NET_SOURCES net/HeaderProxy.h net/RawHeaderProxy.h net/ApiHeaderProxy.h + net/StaticHeaderProxy.h net/ApiDownload.h net/ApiDownload.cpp net/ApiUpload.cpp diff --git a/launcher/net/NetRequest.cpp b/launcher/net/NetRequest.cpp index 304577121..35072d355 100644 --- a/launcher/net/NetRequest.cpp +++ b/launcher/net/NetRequest.cpp @@ -111,6 +111,8 @@ void NetRequest::executeTask() m_last_progress_bytes = 0; QNetworkReply* rep = getReply(request); + if (rep == nullptr) // it failed + return; m_reply.reset(rep); connect(rep, &QNetworkReply::downloadProgress, this, &NetRequest::downloadProgress); connect(rep, &QNetworkReply::finished, this, &NetRequest::downloadFinished); diff --git a/launcher/net/StaticHeaderProxy.h b/launcher/net/StaticHeaderProxy.h new file mode 100644 index 000000000..8af7d203d --- /dev/null +++ b/launcher/net/StaticHeaderProxy.h @@ -0,0 +1,39 @@ +// 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 "net/HeaderProxy.h" + +namespace Net { + +class StaticHeaderProxy : public HeaderProxy { + public: + StaticHeaderProxy(QList hdrs = {}) : HeaderProxy(), m_hdrs(hdrs){}; + virtual ~StaticHeaderProxy() = default; + + public: + virtual QList headers(const QNetworkRequest&) const override { return m_hdrs; }; + void setHeaders(QList hdrs) { m_hdrs = hdrs; }; + + private: + QList m_hdrs; +}; + +} // namespace Net diff --git a/launcher/screenshots/ImgurAlbumCreation.cpp b/launcher/screenshots/ImgurAlbumCreation.cpp index ab425f1a0..c9d73b8d4 100644 --- a/launcher/screenshots/ImgurAlbumCreation.cpp +++ b/launcher/screenshots/ImgurAlbumCreation.cpp @@ -36,96 +36,79 @@ #include "ImgurAlbumCreation.h" -#include +#include #include #include -#include +#include +#include #include -#include +#include +#include #include "BuildConfig.h" -#include "Application.h" +#include "net/StaticHeaderProxy.h" -ImgurAlbumCreation::ImgurAlbumCreation(QList screenshots) : NetAction(), m_screenshots(screenshots) +Net::NetRequest::Ptr ImgurAlbumCreation::make(std::shared_ptr output, QList screenshots) { - m_url = BuildConfig.IMGUR_BASE_URL + "album.json"; - m_state = State::Inactive; + auto up = makeShared(); + up->m_url = BuildConfig.IMGUR_BASE_URL + "album.json"; + up->m_sink.reset(new Sink(output)); + up->m_screenshots = screenshots; + return up; } -void ImgurAlbumCreation::executeTask() +QNetworkReply* ImgurAlbumCreation::getReply(QNetworkRequest& request) { - m_state = State::Running; - QNetworkRequest request(m_url); - request.setHeader(QNetworkRequest::UserAgentHeader, APPLICATION->getUserAgentUncached().toUtf8()); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); - request.setRawHeader("Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toStdString().c_str()); - request.setRawHeader("Accept", "application/json"); - QStringList hashes; - for (auto shot : m_screenshots) - { + for (auto shot : m_screenshots) { hashes.append(shot->m_imgurDeleteHash); } - const QByteArray data = "deletehashes=" + hashes.join(',').toUtf8() + "&title=Minecraft%20Screenshots&privacy=hidden"; + return m_network->post(request, data); +}; - QNetworkReply *rep = APPLICATION->network()->post(request, data); - - m_reply.reset(rep); - connect(rep, &QNetworkReply::uploadProgress, this, &ImgurAlbumCreation::downloadProgress); - connect(rep, &QNetworkReply::finished, this, &ImgurAlbumCreation::downloadFinished); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 - connect(rep, &QNetworkReply::errorOccurred, this, &ImgurAlbumCreation::downloadError); -#else - connect(rep, QOverload::of(&QNetworkReply::error), this, &ImgurAlbumCreation::downloadError); -#endif - connect(rep, &QNetworkReply::sslErrors, this, &ImgurAlbumCreation::sslErrors); +void ImgurAlbumCreation::init() +{ + qDebug() << "Setting up imgur upload"; + auto api_headers = new Net::StaticHeaderProxy( + QList{ { "Content-Type", "application/x-www-form-urlencoded" }, + { "Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toStdString().c_str() }, + { "Accept", "application/json" } }); + addHeaderProxy(api_headers); } -void ImgurAlbumCreation::downloadError(QNetworkReply::NetworkError error) +auto ImgurAlbumCreation::Sink::init(QNetworkRequest& request) -> Task::State { - qDebug() << m_reply->errorString(); - m_state = State::Failed; -} + m_output.clear(); + return Task::State::Running; +}; -void ImgurAlbumCreation::downloadFinished() +auto ImgurAlbumCreation::Sink::write(QByteArray& data) -> Task::State { - if (m_state != State::Failed) - { - QByteArray data = m_reply->readAll(); - m_reply.reset(); - QJsonParseError jsonError; - QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); - if (jsonError.error != QJsonParseError::NoError) - { - qDebug() << jsonError.errorString(); - emitFailed(); - return; - } - auto object = doc.object(); - if (!object.value("success").toBool()) - { - qDebug() << doc.toJson(); - emitFailed(); - return; - } - m_deleteHash = object.value("data").toObject().value("deletehash").toString(); - m_id = object.value("data").toObject().value("id").toString(); - m_state = State::Succeeded; - emit succeeded(); - return; - } - else - { - qDebug() << m_reply->readAll(); - m_reply.reset(); - emitFailed(); - return; - } + m_output.append(data); + return Task::State::Running; } -void ImgurAlbumCreation::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) +auto ImgurAlbumCreation::Sink::abort() -> Task::State { - setProgress(bytesReceived, bytesTotal); - emit progress(bytesReceived, bytesTotal); + m_output.clear(); + return Task::State::Failed; } + +auto ImgurAlbumCreation::Sink::finalize(QNetworkReply&) -> Task::State +{ + QJsonParseError jsonError; + QJsonDocument doc = QJsonDocument::fromJson(m_output, &jsonError); + if (jsonError.error != QJsonParseError::NoError) { + qDebug() << jsonError.errorString(); + return Task::State::Failed; + } + auto object = doc.object(); + if (!object.value("success").toBool()) { + qDebug() << doc.toJson(); + return Task::State::Failed; + } + m_result->deleteHash = object.value("data").toObject().value("deletehash").toString(); + m_result->id = object.value("data").toObject().value("id").toString(); + return Task::State::Succeeded; +} \ No newline at end of file diff --git a/launcher/screenshots/ImgurAlbumCreation.h b/launcher/screenshots/ImgurAlbumCreation.h index a2b70d8b0..670c35eb3 100644 --- a/launcher/screenshots/ImgurAlbumCreation.h +++ b/launcher/screenshots/ImgurAlbumCreation.h @@ -35,44 +35,40 @@ #pragma once -#include "net/NetAction.h" #include "Screenshot.h" +#include "net/NetRequest.h" -typedef shared_qobject_ptr ImgurAlbumCreationPtr; -class ImgurAlbumCreation : public NetAction -{ -public: - explicit ImgurAlbumCreation(QList screenshots); - static ImgurAlbumCreationPtr make(QList screenshots) - { - return ImgurAlbumCreationPtr(new ImgurAlbumCreation(screenshots)); - } +class ImgurAlbumCreation : public Net::NetRequest { + public: + virtual ~ImgurAlbumCreation() = default; - QString deleteHash() const - { - return m_deleteHash; - } - QString id() const - { - return m_id; - } + struct Result { + QString deleteHash; + QString id; + }; - void init() override {}; + class Sink : public Net::Sink { + public: + Sink(std::shared_ptr res) : m_result(res){}; + virtual ~Sink() = default; -protected -slots: - void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override; - void downloadError(QNetworkReply::NetworkError error) override; - void downloadFinished() override; - void downloadReadyRead() override {} + public: + auto init(QNetworkRequest& request) -> Task::State override; + auto write(QByteArray& data) -> Task::State override; + auto abort() -> Task::State override; + auto finalize(QNetworkReply& reply) -> Task::State override; + auto hasLocalData() -> bool override { return false; } -public -slots: - void executeTask() override; + private: + std::shared_ptr m_result; + QByteArray m_output; + }; -private: - QList m_screenshots; + static NetRequest::Ptr make(std::shared_ptr output, QList screenshots); + QNetworkReply* getReply(QNetworkRequest& request) override; + + void init() override; - QString m_deleteHash; - QString m_id; + private: + QList m_screenshots; }; diff --git a/launcher/screenshots/ImgurUpload.cpp b/launcher/screenshots/ImgurUpload.cpp index a50f9afae..5213d2d71 100644 --- a/launcher/screenshots/ImgurUpload.cpp +++ b/launcher/screenshots/ImgurUpload.cpp @@ -36,120 +36,95 @@ #include "ImgurUpload.h" #include "BuildConfig.h" -#include "Application.h" +#include "net/StaticHeaderProxy.h" -#include +#include +#include #include +#include #include #include -#include -#include +#include #include -#include -ImgurUpload::ImgurUpload(ScreenShot::Ptr shot) : NetAction(), m_shot(shot) +void ImgurUpload::init() { - m_url = BuildConfig.IMGUR_BASE_URL + "upload.json"; - m_state = State::Inactive; + qDebug() << "Setting up imgur upload"; + auto api_headers = new Net::StaticHeaderProxy( + QList{ { "Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toStdString().c_str() }, + { "Accept", "application/json" } }); + addHeaderProxy(api_headers); } -void ImgurUpload::executeTask() +QNetworkReply* ImgurUpload::getReply(QNetworkRequest& request) { - finished = false; - m_state = Task::State::Running; - QNetworkRequest request(m_url); - request.setHeader(QNetworkRequest::UserAgentHeader, APPLICATION->getUserAgentUncached().toUtf8()); - request.setRawHeader("Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toStdString().c_str()); - request.setRawHeader("Accept", "application/json"); + auto file = new QFile(m_fileInfo.absoluteFilePath()); - QFile f(m_shot->m_file.absoluteFilePath()); - if (!f.open(QFile::ReadOnly)) - { + if (!file->open(QFile::ReadOnly)) { emitFailed(); - return; + return nullptr; } - QHttpMultiPart *multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + QHttpMultiPart* multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + file->setParent(multipart); QHttpPart filePart; - filePart.setBody(f.readAll().toBase64()); + filePart.setBodyDevice(file); filePart.setHeader(QNetworkRequest::ContentTypeHeader, "image/png"); filePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"image\""); multipart->append(filePart); QHttpPart typePart; typePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"type\""); - typePart.setBody("base64"); + typePart.setBody("file"); multipart->append(typePart); QHttpPart namePart; namePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"name\""); - namePart.setBody(m_shot->m_file.baseName().toUtf8()); + namePart.setBody(m_fileInfo.baseName().toUtf8()); multipart->append(namePart); - QNetworkReply *rep = m_network->post(request, multipart); + return m_network->post(request, multipart); +}; - m_reply.reset(rep); - connect(rep, &QNetworkReply::uploadProgress, this, &ImgurUpload::downloadProgress); - connect(rep, &QNetworkReply::finished, this, &ImgurUpload::downloadFinished); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 - connect(rep, &QNetworkReply::errorOccurred, this, &ImgurUpload::downloadError); -#else - connect(rep, QOverload::of(&QNetworkReply::error), this, &ImgurUpload::downloadError); -#endif - connect(rep, &QNetworkReply::sslErrors, this, &ImgurUpload::sslErrors); +auto ImgurUpload::Sink::init(QNetworkRequest& request) -> Task::State +{ + m_output.clear(); + return Task::State::Running; +}; + +auto ImgurUpload::Sink::write(QByteArray& data) -> Task::State +{ + m_output.append(data); + return Task::State::Running; } -void ImgurUpload::downloadError(QNetworkReply::NetworkError error) +auto ImgurUpload::Sink::abort() -> Task::State { - qCritical() << "ImgurUpload failed with error" << m_reply->errorString() << "Server reply:\n" << m_reply->readAll(); - if(finished) - { - qCritical() << "Double finished ImgurUpload!"; - return; - } - m_state = Task::State::Failed; - finished = true; - m_reply.reset(); - emitFailed(); + m_output.clear(); + return Task::State::Failed; } -void ImgurUpload::downloadFinished() +auto ImgurUpload::Sink::finalize(QNetworkReply&) -> Task::State { - if(finished) - { - qCritical() << "Double finished ImgurUpload!"; - return; - } - QByteArray data = m_reply->readAll(); - m_reply.reset(); QJsonParseError jsonError; - QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); - if (jsonError.error != QJsonParseError::NoError) - { + QJsonDocument doc = QJsonDocument::fromJson(m_output, &jsonError); + if (jsonError.error != QJsonParseError::NoError) { qDebug() << "imgur server did not reply with JSON" << jsonError.errorString(); - finished = true; - m_reply.reset(); - emitFailed(); - return; + return Task::State::Failed; } auto object = doc.object(); - if (!object.value("success").toBool()) - { + if (!object.value("success").toBool()) { qDebug() << "Screenshot upload not successful:" << doc.toJson(); - finished = true; - m_reply.reset(); - emitFailed(); - return; + return Task::State::Failed; } m_shot->m_imgurId = object.value("data").toObject().value("id").toString(); m_shot->m_url = object.value("data").toObject().value("link").toString(); m_shot->m_imgurDeleteHash = object.value("data").toObject().value("deletehash").toString(); - m_state = Task::State::Succeeded; - finished = true; - emit succeeded(); - return; + return Task::State::Succeeded; } -void ImgurUpload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) +Net::NetRequest::Ptr ImgurUpload::make(ScreenShot::Ptr m_shot) { - setProgress(bytesReceived, bytesTotal); - emit progress(bytesReceived, bytesTotal); + auto up = makeShared(m_shot->m_file); + up->m_url = std::move(BuildConfig.IMGUR_BASE_URL + "upload.json"); + up->m_sink.reset(new Sink(m_shot)); + return up; } diff --git a/launcher/screenshots/ImgurUpload.h b/launcher/screenshots/ImgurUpload.h index e8a6d8d70..e99ba3a4a 100644 --- a/launcher/screenshots/ImgurUpload.h +++ b/launcher/screenshots/ImgurUpload.h @@ -35,31 +35,36 @@ #pragma once -#include "net/NetAction.h" +#include #include "Screenshot.h" +#include "net/NetRequest.h" -class ImgurUpload : public NetAction { -public: - using Ptr = shared_qobject_ptr; +class ImgurUpload : public Net::NetRequest { + public: + class Sink : public Net::Sink { + public: + Sink(ScreenShot::Ptr shot) : m_shot(shot){}; + virtual ~Sink() = default; - explicit ImgurUpload(ScreenShot::Ptr shot); - static Ptr make(ScreenShot::Ptr shot) { - return Ptr(new ImgurUpload(shot)); - } - void init() override {}; + public: + auto init(QNetworkRequest& request) -> Task::State override; + auto write(QByteArray& data) -> Task::State override; + auto abort() -> Task::State override; + auto finalize(QNetworkReply& reply) -> Task::State override; + auto hasLocalData() -> bool override { return false; } -protected -slots: - void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override; - void downloadError(QNetworkReply::NetworkError error) override; - void downloadFinished() override; - void downloadReadyRead() override {} + private: + ScreenShot::Ptr m_shot; + QByteArray m_output; + }; + ImgurUpload(QFileInfo info) : m_fileInfo(info) {} + virtual ~ImgurUpload() = default; -public -slots: - void executeTask() override; + static NetRequest::Ptr make(ScreenShot::Ptr m_shot); -private: - ScreenShot::Ptr m_shot; - bool finished = true; + void init() override; + + private: + virtual QNetworkReply* getReply(QNetworkRequest&) override; + const QFileInfo m_fileInfo; }; diff --git a/launcher/ui/pages/instance/ScreenshotsPage.cpp b/launcher/ui/pages/instance/ScreenshotsPage.cpp index 352375941..128d79503 100644 --- a/launcher/ui/pages/instance/ScreenshotsPage.cpp +++ b/launcher/ui/pages/instance/ScreenshotsPage.cpp @@ -403,41 +403,37 @@ void ScreenshotsPage::on_actionUpload_triggered() QList uploaded; auto job = NetJob::Ptr(new NetJob("Screenshot Upload", APPLICATION->network())); - if(selection.size() < 2) - { + + ProgressDialog dialog(this); + dialog.setSkipButton(true, tr("Abort")); + + if (selection.size() < 2) { auto item = selection.at(0); auto info = m_model->fileInfo(item); auto screenshot = std::make_shared(info); job->addNetAction(ImgurUpload::make(screenshot)); m_uploadActive = true; - ProgressDialog dialog(this); - if(dialog.execWithTask(job.get()) != QDialog::Accepted) - { - CustomMessageBox::selectable(this, tr("Failed to upload screenshots!"), - tr("Unknown error"), QMessageBox::Warning)->exec(); - } - else - { + if (dialog.execWithTask(job.get()) != QDialog::Accepted) { + CustomMessageBox::selectable(this, tr("Failed to upload screenshots!"), tr("Unknown error"), QMessageBox::Warning)->exec(); + } else { auto link = screenshot->m_url; - QClipboard *clipboard = QApplication::clipboard(); + QClipboard* clipboard = QApplication::clipboard(); + qDebug() << "ImgurUpload link" << link; clipboard->setText(link); CustomMessageBox::selectable( - this, - tr("Upload finished"), - tr("The link to the uploaded screenshot has been placed in your clipboard.") - .arg(link), - QMessageBox::Information - )->exec(); + this, tr("Upload finished"), + tr("The link to the uploaded screenshot has been placed in your clipboard.").arg(link), + QMessageBox::Information) + ->exec(); } m_uploadActive = false; return; } - for (auto item : selection) - { + for (auto item : selection) { auto info = m_model->fileInfo(item); auto screenshot = std::make_shared(info); uploaded.push_back(screenshot); @@ -445,32 +441,23 @@ void ScreenshotsPage::on_actionUpload_triggered() } SequentialTask task; auto albumTask = NetJob::Ptr(new NetJob("Imgur Album Creation", APPLICATION->network())); - auto imgurAlbum = ImgurAlbumCreation::make(uploaded); + auto imgurResult = std::make_shared(); + auto imgurAlbum = ImgurAlbumCreation::make(imgurResult, uploaded); albumTask->addNetAction(imgurAlbum); task.addTask(job); task.addTask(albumTask); m_uploadActive = true; - ProgressDialog prog(this); - if (prog.execWithTask(&task) != QDialog::Accepted) - { - CustomMessageBox::selectable( - this, - tr("Failed to upload screenshots!"), - tr("Unknown error"), - QMessageBox::Warning - )->exec(); - } - else - { - auto link = QString("https://imgur.com/a/%1").arg(imgurAlbum->id()); - QClipboard *clipboard = QApplication::clipboard(); + if (dialog.execWithTask(&task) != QDialog::Accepted || imgurResult->id.isEmpty()) { + CustomMessageBox::selectable(this, tr("Failed to upload screenshots!"), tr("Unknown error"), QMessageBox::Warning)->exec(); + } else { + auto link = QString("https://imgur.com/a/%1").arg(imgurResult->id); + qDebug() << "ImgurUpload link" << link; + QClipboard* clipboard = QApplication::clipboard(); clipboard->setText(link); - CustomMessageBox::selectable( - this, - tr("Upload finished"), - tr("The link to the uploaded album has been placed in your clipboard.") .arg(link), - QMessageBox::Information - )->exec(); + CustomMessageBox::selectable(this, tr("Upload finished"), + tr("The link to the uploaded album has been placed in your clipboard.").arg(link), + QMessageBox::Information) + ->exec(); } m_uploadActive = false; } @@ -478,8 +465,7 @@ void ScreenshotsPage::on_actionUpload_triggered() void ScreenshotsPage::on_actionCopy_Image_triggered() { auto selection = ui->listView->selectionModel()->selectedRows(); - if(selection.size() < 1) - { + if (selection.size() < 1) { return; } 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 077/247] 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 @@ class Application : public QApplication 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 078/247] 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 079/247] 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 080/247] 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 081/247] 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 082/247] 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 083/247] 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 084/247] 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 085/247] 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 086/247] 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 087/247] 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 088/247] 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 089/247] 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 090/247] 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 091/247] 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 092/247] 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 997a3709d40ad66e586c5b5342e8aeb8fe485a32 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Wed, 12 Jul 2023 18:10:13 +0100 Subject: [PATCH 093/247] Custom environment variables UI and implementation Signed-off-by: TheKodeToad --- launcher/Application.cpp | 4 + launcher/CMakeLists.txt | 5 + launcher/minecraft/MinecraftInstance.cpp | 22 + .../resources/breeze_dark/breeze_dark.qrc | 1 + .../scalable/environment-variables.svg | 13 + .../resources/breeze_light/breeze_light.qrc | 1 + .../scalable/environment-variables.svg | 13 + launcher/resources/flat/flat.qrc | 1 + .../flat/scalable/custom-commands.svg | 87 +- .../flat/scalable/environment-variables.svg | 86 + launcher/resources/flat_white/flat_white.qrc | 1 + .../flat_white/scalable/custom-commands.svg | 87 +- .../scalable/environment-variables.svg | 86 + launcher/resources/multimc/multimc.qrc | 2 +- .../multimc/scalable/custom-commands.svg | 4591 +++++++++++++++-- .../scalable/environment-variables.svg | 346 ++ launcher/resources/pe_blue/pe_blue.qrc | 1 + .../scalable/environment-variables.svg | 345 ++ launcher/resources/pe_colored/pe_colored.qrc | 1 + .../scalable/environment-variables.svg | 347 ++ launcher/resources/pe_dark/pe_dark.qrc | 1 + .../scalable/environment-variables.svg | 345 ++ launcher/resources/pe_light/pe_light.qrc | 1 + .../scalable/environment-variables.svg | 345 ++ .../pages/global/EnvironmentVariablesPage.cpp | 64 + .../pages/global/EnvironmentVariablesPage.h | 42 + .../pages/instance/InstanceSettingsPage.cpp | 17 + .../ui/pages/instance/InstanceSettingsPage.ui | 25 +- launcher/ui/widgets/EnvironmentVariables.cpp | 138 + launcher/ui/widgets/EnvironmentVariables.h | 44 + launcher/ui/widgets/EnvironmentVariables.ui | 122 + 31 files changed, 6710 insertions(+), 474 deletions(-) create mode 100644 launcher/resources/breeze_dark/scalable/environment-variables.svg create mode 100644 launcher/resources/breeze_light/scalable/environment-variables.svg create mode 100644 launcher/resources/flat/scalable/environment-variables.svg create mode 100644 launcher/resources/flat_white/scalable/environment-variables.svg create mode 100644 launcher/resources/multimc/scalable/environment-variables.svg create mode 100644 launcher/resources/pe_blue/scalable/environment-variables.svg create mode 100644 launcher/resources/pe_colored/scalable/environment-variables.svg create mode 100644 launcher/resources/pe_dark/scalable/environment-variables.svg create mode 100644 launcher/resources/pe_light/scalable/environment-variables.svg create mode 100644 launcher/ui/pages/global/EnvironmentVariablesPage.cpp create mode 100644 launcher/ui/pages/global/EnvironmentVariablesPage.h create mode 100644 launcher/ui/widgets/EnvironmentVariables.cpp create mode 100644 launcher/ui/widgets/EnvironmentVariables.h create mode 100644 launcher/ui/widgets/EnvironmentVariables.ui diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 7858d7132..862e4256e 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -64,6 +64,7 @@ #include "ui/pages/global/AccountListPage.h" #include "ui/pages/global/APIPage.h" #include "ui/pages/global/CustomCommandsPage.h" +#include "ui/pages/global/EnvironmentVariablesPage.h" #include "ui/setupwizard/SetupWizard.h" #include "ui/setupwizard/LanguageWizardPage.h" @@ -701,6 +702,8 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_settings->registerSetting("CloseAfterLaunch", false); m_settings->registerSetting("QuitAfterGameStop", false); + m_settings->registerSetting("Env", QVariant(QMap())); + // Custom Microsoft Authentication Client ID m_settings->registerSetting("MSAClientIDOverride", ""); @@ -726,6 +729,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_globalSettingsProvider->addPage(); m_globalSettingsProvider->addPage(); m_globalSettingsProvider->addPage(); + m_globalSettingsProvider->addPage(); m_globalSettingsProvider->addPage(); m_globalSettingsProvider->addPage(); m_globalSettingsProvider->addPage(); diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index af3bc28e3..8992b1e6c 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -812,6 +812,8 @@ SET(LAUNCHER_SOURCES ui/pages/global/AccountListPage.h ui/pages/global/CustomCommandsPage.cpp ui/pages/global/CustomCommandsPage.h + ui/pages/global/EnvironmentVariablesPage.cpp + ui/pages/global/EnvironmentVariablesPage.h ui/pages/global/ExternalToolsPage.cpp ui/pages/global/ExternalToolsPage.h ui/pages/global/JavaPage.cpp @@ -953,6 +955,8 @@ SET(LAUNCHER_SOURCES ui/widgets/Common.h ui/widgets/CustomCommands.cpp ui/widgets/CustomCommands.h + ui/widgets/EnvironmentVariables.cpp + ui/widgets/EnvironmentVariables.h ui/widgets/DropLabel.cpp ui/widgets/DropLabel.h ui/widgets/FocusLineEdit.cpp @@ -1044,6 +1048,7 @@ qt_wrap_ui(LAUNCHER_UI ui/pages/modplatform/technic/TechnicPage.ui ui/widgets/InstanceCardWidget.ui ui/widgets/CustomCommands.ui + ui/widgets/EnvironmentVariables.ui ui/widgets/InfoFrame.ui ui/widgets/ModFilterWidget.ui ui/widgets/SubTaskProgressBar.ui diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 4867cc7a3..6afc4e9a5 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -186,6 +186,10 @@ void MinecraftInstance::loadSpecificSettings() m_settings->registerOverride(global_settings->getSetting("CloseAfterLaunch"), miscellaneousOverride); m_settings->registerOverride(global_settings->getSetting("QuitAfterGameStop"), miscellaneousOverride); + m_settings->registerSetting("UseEnv", false); + m_settings->registerSetting("OverrideEnv", false); + m_settings->registerSetting("Env", QVariant(QMap())); + m_settings->set("InstanceType", "OneSix"); } @@ -526,6 +530,24 @@ QProcessEnvironment MinecraftInstance::createLaunchEnvironment() } #endif + // custom env + + auto insertEnv = [&env](QMap envMap) { + if (envMap.isEmpty()) + return; + + for (auto iter = envMap.begin(); iter != envMap.end(); iter++) + env.insert(iter.key(), iter.value().toString()); + }; + + bool useEnv = settings()->get("UseEnv").toBool(); + bool overrideEnv = useEnv && settings()->get("OverrideEnv").toBool(); + + if (!overrideEnv) + insertEnv(APPLICATION->settings()->get("Env").toMap()); + if (useEnv) + insertEnv(settings()->get("Env").toMap()); + return env; } diff --git a/launcher/resources/breeze_dark/breeze_dark.qrc b/launcher/resources/breeze_dark/breeze_dark.qrc index 320ca8171..61d82ec30 100644 --- a/launcher/resources/breeze_dark/breeze_dark.qrc +++ b/launcher/resources/breeze_dark/breeze_dark.qrc @@ -9,6 +9,7 @@ scalable/copy.svg scalable/coremods.svg scalable/custom-commands.svg + scalable/environment-variables.svg scalable/discord.svg scalable/externaltools.svg scalable/help.svg diff --git a/launcher/resources/breeze_dark/scalable/environment-variables.svg b/launcher/resources/breeze_dark/scalable/environment-variables.svg new file mode 100644 index 000000000..308c4a239 --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/environment-variables.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_light/breeze_light.qrc b/launcher/resources/breeze_light/breeze_light.qrc index e88cd9a00..2211c7188 100644 --- a/launcher/resources/breeze_light/breeze_light.qrc +++ b/launcher/resources/breeze_light/breeze_light.qrc @@ -9,6 +9,7 @@ scalable/copy.svg scalable/coremods.svg scalable/custom-commands.svg + scalable/environment-variables.svg scalable/discord.svg scalable/externaltools.svg scalable/help.svg diff --git a/launcher/resources/breeze_light/scalable/environment-variables.svg b/launcher/resources/breeze_light/scalable/environment-variables.svg new file mode 100644 index 000000000..f5d4acc3b --- /dev/null +++ b/launcher/resources/breeze_light/scalable/environment-variables.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/flat/flat.qrc b/launcher/resources/flat/flat.qrc index 2fd5daefe..8876027da 100644 --- a/launcher/resources/flat/flat.qrc +++ b/launcher/resources/flat/flat.qrc @@ -11,6 +11,7 @@ scalable/copy.svg scalable/coremods.svg scalable/custom-commands.svg + scalable/environment-variables.svg scalable/discord.svg scalable/externaltools.svg scalable/help.svg diff --git a/launcher/resources/flat/scalable/custom-commands.svg b/launcher/resources/flat/scalable/custom-commands.svg index a35634b16..f2e587843 100644 --- a/launcher/resources/flat/scalable/custom-commands.svg +++ b/launcher/resources/flat/scalable/custom-commands.svg @@ -1,86 +1 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - + diff --git a/launcher/resources/flat/scalable/environment-variables.svg b/launcher/resources/flat/scalable/environment-variables.svg new file mode 100644 index 000000000..a35634b16 --- /dev/null +++ b/launcher/resources/flat/scalable/environment-variables.svg @@ -0,0 +1,86 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + diff --git a/launcher/resources/flat_white/flat_white.qrc b/launcher/resources/flat_white/flat_white.qrc index a1c940da0..83b178cbf 100644 --- a/launcher/resources/flat_white/flat_white.qrc +++ b/launcher/resources/flat_white/flat_white.qrc @@ -11,6 +11,7 @@ scalable/copy.svg scalable/coremods.svg scalable/custom-commands.svg + scalable/environment-variables.svg scalable/discord.svg scalable/externaltools.svg scalable/help.svg diff --git a/launcher/resources/flat_white/scalable/custom-commands.svg b/launcher/resources/flat_white/scalable/custom-commands.svg index fe1cf9987..0ba459cff 100644 --- a/launcher/resources/flat_white/scalable/custom-commands.svg +++ b/launcher/resources/flat_white/scalable/custom-commands.svg @@ -1,86 +1 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - + diff --git a/launcher/resources/flat_white/scalable/environment-variables.svg b/launcher/resources/flat_white/scalable/environment-variables.svg new file mode 100644 index 000000000..fe1cf9987 --- /dev/null +++ b/launcher/resources/flat_white/scalable/environment-variables.svg @@ -0,0 +1,86 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + diff --git a/launcher/resources/multimc/multimc.qrc b/launcher/resources/multimc/multimc.qrc index 2c00f28fa..88a7829f5 100644 --- a/launcher/resources/multimc/multimc.qrc +++ b/launcher/resources/multimc/multimc.qrc @@ -73,8 +73,8 @@ 64x64/screenshots.png scalable/screenshots.svg - scalable/custom-commands.svg + scalable/environment-variables.svg 16x16/cat.png diff --git a/launcher/resources/multimc/scalable/custom-commands.svg b/launcher/resources/multimc/scalable/custom-commands.svg index b7f1a149b..0d502bb1d 100644 --- a/launcher/resources/multimc/scalable/custom-commands.svg +++ b/launcher/resources/multimc/scalable/custom-commands.svg @@ -1,338 +1,4339 @@ + - - - + width="128" + height="128" + id="svg2756" + sodipodi:version="0.32" + inkscape:version="0.45.1" + version="1.0" + sodipodi:docname="konsole.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + sodipodi:docbase="/home/david/sandbox"> + id="defs2758"> + + + + + inkscape:collect="always" + id="linearGradient10342"> + + + + + style="stop-color:#8e8e8e;stop-opacity:1;" + id="stop9768" /> + + offset="1" + style="stop-color:#9d9d9d;stop-opacity:1;" + id="stop9772" /> + + + + + + + + + style="stop-color:#888888" + id="stop619" /> + id="XMLID_177_" + gradientUnits="userSpaceOnUse" + x1="10.3647" + y1="110.1777" + x2="11.5172" + y2="111.6687" + gradientTransform="translate(-4,-19)"> + style="stop-color:#555555" + id="stop610" /> + + + + offset="0" + style="stop-color:#555555" + id="stop603" /> + style="stop-color:#888888" + id="stop605" /> + id="XMLID_175_" + gradientUnits="userSpaceOnUse" + x1="14.3643" + y1="110.1777" + x2="15.5172" + y2="111.6694" + gradientTransform="translate(-4,-19)"> + style="stop-color:#555555" + id="stop596" /> + style="stop-color:#888888" + id="stop598" /> + id="XMLID_174_" + gradientUnits="userSpaceOnUse" + x1="8.3643" + y1="104.1777" + x2="9.5167" + y2="105.6687" + gradientTransform="translate(-4,-19)"> + style="stop-color:#555555" + id="stop589" /> + + + + offset="0" + style="stop-color:#555555" + id="stop582" /> + style="stop-color:#888888" + id="stop584" /> - - - - - - - - - - - - image/svg+xml - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + id="g32" + transform="matrix(1.0851064,0,0,1.1142857,3.2340422,6.9714286)"> + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + id="g2644"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/launcher/resources/multimc/scalable/environment-variables.svg b/launcher/resources/multimc/scalable/environment-variables.svg new file mode 100644 index 000000000..5e136b202 --- /dev/null +++ b/launcher/resources/multimc/scalable/environment-variables.svg @@ -0,0 +1,346 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/launcher/resources/pe_blue/pe_blue.qrc b/launcher/resources/pe_blue/pe_blue.qrc index da45ef9a1..717d3972e 100644 --- a/launcher/resources/pe_blue/pe_blue.qrc +++ b/launcher/resources/pe_blue/pe_blue.qrc @@ -10,6 +10,7 @@ scalable/copy.svg scalable/coremods.svg scalable/custom-commands.svg + scalable/environment-variables.svg scalable/externaltools.svg scalable/help.svg scalable/instance-settings.svg diff --git a/launcher/resources/pe_blue/scalable/environment-variables.svg b/launcher/resources/pe_blue/scalable/environment-variables.svg new file mode 100644 index 000000000..61c63a4c5 --- /dev/null +++ b/launcher/resources/pe_blue/scalable/environment-variables.svg @@ -0,0 +1,345 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/launcher/resources/pe_colored/pe_colored.qrc b/launcher/resources/pe_colored/pe_colored.qrc index ba5bd44f9..023c81e74 100644 --- a/launcher/resources/pe_colored/pe_colored.qrc +++ b/launcher/resources/pe_colored/pe_colored.qrc @@ -10,6 +10,7 @@ scalable/copy.svg scalable/coremods.svg scalable/custom-commands.svg + scalable/environment-variables.svg scalable/externaltools.svg scalable/help.svg scalable/instance-settings.svg diff --git a/launcher/resources/pe_colored/scalable/environment-variables.svg b/launcher/resources/pe_colored/scalable/environment-variables.svg new file mode 100644 index 000000000..c1aab6bca --- /dev/null +++ b/launcher/resources/pe_colored/scalable/environment-variables.svg @@ -0,0 +1,347 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/launcher/resources/pe_dark/pe_dark.qrc b/launcher/resources/pe_dark/pe_dark.qrc index 2bfec42cb..c97fb469c 100644 --- a/launcher/resources/pe_dark/pe_dark.qrc +++ b/launcher/resources/pe_dark/pe_dark.qrc @@ -10,6 +10,7 @@ scalable/copy.svg scalable/coremods.svg scalable/custom-commands.svg + scalable/environment-variables.svg scalable/externaltools.svg scalable/help.svg scalable/instance-settings.svg diff --git a/launcher/resources/pe_dark/scalable/environment-variables.svg b/launcher/resources/pe_dark/scalable/environment-variables.svg new file mode 100644 index 000000000..46a3445f4 --- /dev/null +++ b/launcher/resources/pe_dark/scalable/environment-variables.svg @@ -0,0 +1,345 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/launcher/resources/pe_light/pe_light.qrc b/launcher/resources/pe_light/pe_light.qrc index 25d5da73b..b590dd2c6 100644 --- a/launcher/resources/pe_light/pe_light.qrc +++ b/launcher/resources/pe_light/pe_light.qrc @@ -10,6 +10,7 @@ scalable/copy.svg scalable/coremods.svg scalable/custom-commands.svg + scalable/environment-variables.svg scalable/externaltools.svg scalable/help.svg scalable/instance-settings.svg diff --git a/launcher/resources/pe_light/scalable/environment-variables.svg b/launcher/resources/pe_light/scalable/environment-variables.svg new file mode 100644 index 000000000..b8d562ffe --- /dev/null +++ b/launcher/resources/pe_light/scalable/environment-variables.svg @@ -0,0 +1,345 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/launcher/ui/pages/global/EnvironmentVariablesPage.cpp b/launcher/ui/pages/global/EnvironmentVariablesPage.cpp new file mode 100644 index 000000000..2c3b716b8 --- /dev/null +++ b/launcher/ui/pages/global/EnvironmentVariablesPage.cpp @@ -0,0 +1,64 @@ +// 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "EnvironmentVariablesPage.h" + +EnvironmentVariablesPage::EnvironmentVariablesPage(QWidget* parent) : QWidget(parent) +{ + auto verticalLayout = new QVBoxLayout(this); + verticalLayout->setContentsMargins(0, 0, 0, 0); + variables = new EnvironmentVariables(this); + variables->setContentsMargins(6, 6, 6, 6); + verticalLayout->addWidget(variables); + + variables->initialize(false, true, false, APPLICATION->settings()->get("Env").toMap()); +} + +QString EnvironmentVariablesPage::displayName() const +{ + return tr("Environment Variables"); +} + +QIcon EnvironmentVariablesPage::icon() const +{ + return APPLICATION->getThemedIcon("environment-variables"); +} + +QString EnvironmentVariablesPage::id() const +{ + return "environment-variables"; +} + +QString EnvironmentVariablesPage::helpPage() const +{ + return "Environment-variables"; +} + +bool EnvironmentVariablesPage::apply() +{ + APPLICATION->settings()->set("Env", variables->value()); + return true; +} + +void EnvironmentVariablesPage::retranslate() +{ + variables->retranslate(); +} diff --git a/launcher/ui/pages/global/EnvironmentVariablesPage.h b/launcher/ui/pages/global/EnvironmentVariablesPage.h new file mode 100644 index 000000000..6e80775ec --- /dev/null +++ b/launcher/ui/pages/global/EnvironmentVariablesPage.h @@ -0,0 +1,42 @@ +// 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +#include "ui/pages/BasePage.h" +#include "ui/widgets/EnvironmentVariables.h" + +class EnvironmentVariablesPage : public QWidget, public BasePage { + Q_OBJECT + + public: + explicit EnvironmentVariablesPage(QWidget* parent = nullptr); + + QString displayName() const override; + QIcon icon() const override; + QString id() const override; + QString helpPage() const override; + + bool apply() override; + void retranslate() override; + + private: + EnvironmentVariables* variables; +}; diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index 943ff17f1..5144e973c 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -84,6 +84,9 @@ void InstanceSettingsPage::globalSettingsButtonClicked(bool) case 2: APPLICATION->ShowGlobalSettings(this, "custom-commands"); return; + case 3: + APPLICATION->ShowGlobalSettings(this, "environment-variables"); + return; default: APPLICATION->ShowGlobalSettings(this, "minecraft-settings"); return; @@ -217,6 +220,11 @@ void InstanceSettingsPage::applySettings() m_settings->reset("PostExitCommand"); } + // Environment Variables + m_settings->set("UseEnv", ui->environmentVariables->checked()); + m_settings->set("OverrideEnv", ui->environmentVariables->override()); + m_settings->set("Env", ui->environmentVariables->value()); + // Workarounds bool workarounds = ui->nativeWorkaroundsGroupBox->isChecked(); m_settings->set("OverrideNativeWorkarounds", workarounds); @@ -345,6 +353,14 @@ void InstanceSettingsPage::loadSettings() m_settings->get("PostExitCommand").toString() ); + // Environment variables + ui->environmentVariables->initialize( + true, + m_settings->get("UseEnv").toBool(), + m_settings->get("OverrideEnv").toBool(), + m_settings->get("Env").toMap() + ); + // Workarounds ui->nativeWorkaroundsGroupBox->setChecked(m_settings->get("OverrideNativeWorkarounds").toBool()); ui->useNativeGLFWCheck->setChecked(m_settings->get("UseNativeGLFW").toBool()); @@ -492,6 +508,7 @@ void InstanceSettingsPage::retranslate() { ui->retranslateUi(this); ui->customCommands->retranslate(); // TODO: why is this seperate from the others? + ui->environmentVariables->retranslate(); } void InstanceSettingsPage::updateThresholds() diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index 8427965de..7adeb8785 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -35,13 +35,10 @@
- - QTabWidget::Rounded - 0 - + Java @@ -246,7 +243,7 @@ - + Game windows @@ -406,7 +403,7 @@ - + Custom commands @@ -416,6 +413,16 @@ + + + Environment variables + + + + + + + Workarounds @@ -669,6 +676,12 @@
ui/widgets/CustomCommands.h
1 + + EnvironmentVariables + QWidget +
ui/widgets/EnvironmentVariables.h
+ 1 +
openGlobalJavaSettingsButton diff --git a/launcher/ui/widgets/EnvironmentVariables.cpp b/launcher/ui/widgets/EnvironmentVariables.cpp new file mode 100644 index 000000000..a409f64fc --- /dev/null +++ b/launcher/ui/widgets/EnvironmentVariables.cpp @@ -0,0 +1,138 @@ +// 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "Application.h" +#include "EnvironmentVariables.h" +#include "ui/dialogs/CustomMessageBox.h" +#include "ui_EnvironmentVariables.h" + +EnvironmentVariables::EnvironmentVariables(QWidget* parent) : QWidget(parent), ui(new Ui::EnvironmentVariables) +{ + ui->setupUi(this); + ui->list->installEventFilter(this); + + ui->list->sortItems(0, Qt::AscendingOrder); + ui->list->setSortingEnabled(true); + ui->list->header()->resizeSections(QHeaderView::Interactive); + ui->list->header()->resizeSection(0, 200); + + connect(ui->add, &QPushButton::clicked, this, [this] { + auto item = new QTreeWidgetItem(ui->list); + item->setText(0, "ENV_VAR"); + item->setText(1, "value"); + item->setFlags(item->flags() | Qt::ItemIsEditable); + ui->list->addTopLevelItem(item); + ui->list->selectionModel()->select(ui->list->model()->index(ui->list->indexOfTopLevelItem(item), 0), + QItemSelectionModel::ClearAndSelect | QItemSelectionModel::SelectionFlag::Rows); + ui->list->editItem(item); + }); + + connect(ui->remove, &QPushButton::clicked, this, [this] { + for (QTreeWidgetItem* item : ui->list->selectedItems()) + ui->list->takeTopLevelItem(ui->list->indexOfTopLevelItem(item)); + }); + + connect(ui->clear, &QPushButton::clicked, this, [this] { ui->list->clear(); }); + + connect(ui->globalOverride, &QCheckBox::clicked, this, [this](bool state) { + if (!state) + return; + + auto global = APPLICATION->settings()->get("Env").toMap(); + if (global.isEmpty()) + return; + + auto response = CustomMessageBox::selectable( + this, tr("Reset"), + tr("You have chosen to ignore global settings.\n\nWould you like to clear the current variables and copy " + "the global variables over?"), + QMessageBox::Question, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + + if (response == QMessageBox::Yes) + initialize(true, checked(), override(), global); + }); +} + +EnvironmentVariables::~EnvironmentVariables() +{ + delete ui; +} + +void EnvironmentVariables::initialize(bool instance, bool checked, bool override, const QMap& value) +{ + // update widgets to settings + ui->groupBox->setCheckable(instance); + ui->groupBox->setChecked(checked); + ui->globalOverride->setVisible(instance); + ui->globalOverride->setChecked(override); + + // populate + ui->list->clear(); + for (auto iter = value.begin(); iter != value.end(); iter++) { + auto item = new QTreeWidgetItem(ui->list); + item->setText(0, iter.key()); + item->setText(1, iter.value().toString()); + item->setFlags(item->flags() | Qt::ItemIsEditable); + ui->list->addTopLevelItem(item); + } +} + +bool EnvironmentVariables::eventFilter(QObject* watched, QEvent* event) +{ + if (watched == ui->list && event->type() == QEvent::KeyPress) { + const QKeyEvent* keyEvent = (QKeyEvent*)event; + if (keyEvent->key() == Qt::Key_Delete) { + emit ui->remove->clicked(); + return true; + } + } + + return QObject::eventFilter(watched, event); +} + +void EnvironmentVariables::retranslate() +{ + ui->retranslateUi(this); +} + +bool EnvironmentVariables::checked() const +{ + if (!ui->groupBox->isCheckable()) + return true; + return ui->groupBox->isChecked(); +} + +bool EnvironmentVariables::override() const +{ + if (!ui->globalOverride->isVisible()) + return false; + return ui->globalOverride->isChecked(); +} + +QMap EnvironmentVariables::value() const +{ + QMap result; + QTreeWidgetItem* item = ui->list->topLevelItem(0); + for (int i = 1; item != nullptr; item = ui->list->topLevelItem(i++)) + result[item->text(0)] = item->text(1); + + return result; +} diff --git a/launcher/ui/widgets/EnvironmentVariables.h b/launcher/ui/widgets/EnvironmentVariables.h new file mode 100644 index 000000000..ba4329dd9 --- /dev/null +++ b/launcher/ui/widgets/EnvironmentVariables.h @@ -0,0 +1,44 @@ +// 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. + * + * 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 EnvironmentVariables; +} + +class EnvironmentVariables : public QWidget { + Q_OBJECT + + public: + explicit EnvironmentVariables(QWidget* state = nullptr); + ~EnvironmentVariables() override; + void initialize(bool instance, bool checked, bool override, const QMap& value); + bool eventFilter(QObject* watched, QEvent* event) override; + + void retranslate(); + bool checked() const; + bool override() const; + QMap value() const; + + private: + Ui::EnvironmentVariables* ui; +}; diff --git a/launcher/ui/widgets/EnvironmentVariables.ui b/launcher/ui/widgets/EnvironmentVariables.ui new file mode 100644 index 000000000..895b40d01 --- /dev/null +++ b/launcher/ui/widgets/EnvironmentVariables.ui @@ -0,0 +1,122 @@ + + + EnvironmentVariables + + + + 0 + 0 + 565 + 410 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + &Environment Variables + + + true + + + + + + &Ignore global settings + + + + + + + true + + + QAbstractItemView::ExtendedSelection + + + false + + + false + + + true + + + false + + + + Name + + + + + Value + + + + + + + + + + &Add + + + + + + + &Remove + + + + + + + &Clear + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + From cebb4dd17ae9bfab35210250ab7a5484c644abb0 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 14 Jul 2023 20:39:53 +0300 Subject: [PATCH 094/247] 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 095/247] 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 096/247] 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 9d0175b81ab702467cd5c707669c12b6bbcb913e Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 16 Jul 2023 17:46:00 +0300 Subject: [PATCH 097/247] Added welcome screen Signed-off-by: Trial97 --- launcher/ui/instanceview/InstanceView.cpp | 73 +++++++++++++++++------ 1 file changed, 56 insertions(+), 17 deletions(-) diff --git a/launcher/ui/instanceview/InstanceView.cpp b/launcher/ui/instanceview/InstanceView.cpp index 1911dd59a..a1acf5875 100644 --- a/launcher/ui/instanceview/InstanceView.cpp +++ b/launcher/ui/instanceview/InstanceView.cpp @@ -35,32 +35,32 @@ #include "InstanceView.h" -#include +#include #include -#include -#include -#include -#include +#include #include +#include +#include #include -#include +#include +#include +#include #include -#include +#include +#include +#include #include "VisualGroup.h" #include "ui/themes/ThemeManager.h" -#include #include #include - -template bool listsIntersect(const QList &l1, const QList t2) +template +bool listsIntersect(const QList& l1, const QList t2) { - for (auto &item : l1) - { - if (t2.contains(item)) - { + for (auto& item : l1) { + if (t2.contains(item)) { return true; } } @@ -536,11 +536,50 @@ void InstanceView::paintEvent(QPaintEvent* event) #endif option.widget = this; + if (model()->rowCount() == 0) { + painter.save(); + const QString line1 = "Welcome!"; + const QString line2 = "Add an instance to get started."; + auto rect = this->viewport()->rect(); + auto font = option.font; + font.setPointSize(64); + painter.setFont(font); + auto fm = painter.fontMetrics(); + + if (rect.height() <= (fm.height() * 5) || rect.width() <= fm.horizontalAdvance(line2)) { + auto s = rect.height() / (5. * fm.height()); + auto sx = rect.width() * 1. / fm.horizontalAdvance(line2); + if (s >= sx) + s = sx; + auto ps = font.pointSize() * s; + if (ps <= 0) + ps = 1; + font.setPointSize(ps); + painter.setFont(font); + fm = painter.fontMetrics(); + } + // about icon + auto about = QIcon::fromTheme("about"); + QRect aboutRect(0, 0, fm.height() * .75, fm.height() * .75); + aboutRect.moveCenter(rect.center()); + aboutRect.moveTop(fm.height() * .75); + auto px = about.pixmap(about.actualSize(aboutRect.size())); + if (!px.isNull()) + painter.drawPixmap(aboutRect, px); + + // text + rect.setTop(rect.top() + fm.height() * 1.5); + painter.drawText(rect, Qt::AlignHCenter, line1); + rect.setTop(rect.top() + fm.height()); + painter.drawText(rect, Qt::AlignHCenter, line2); + painter.restore(); + return; + } + int wpWidth = viewport()->width(); option.rect.setWidth(wpWidth); - for (int i = 0; i < m_groups.size(); ++i) - { - VisualGroup *category = m_groups.at(i); + for (int i = 0; i < m_groups.size(); ++i) { + VisualGroup* category = m_groups.at(i); int y = category->verticalPosition(); y -= verticalOffset(); QRect backup = option.rect; From 7441974d9d22ad8596a52cf9fd70ae5e2e0e721a Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 16 Jul 2023 21:23:22 +0300 Subject: [PATCH 098/247] Removed icon Signed-off-by: Trial97 --- launcher/ui/instanceview/InstanceView.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/launcher/ui/instanceview/InstanceView.cpp b/launcher/ui/instanceview/InstanceView.cpp index a1acf5875..9b0022229 100644 --- a/launcher/ui/instanceview/InstanceView.cpp +++ b/launcher/ui/instanceview/InstanceView.cpp @@ -542,7 +542,7 @@ void InstanceView::paintEvent(QPaintEvent* event) const QString line2 = "Add an instance to get started."; auto rect = this->viewport()->rect(); auto font = option.font; - font.setPointSize(64); + font.setPointSize(53); painter.setFont(font); auto fm = painter.fontMetrics(); @@ -558,14 +558,6 @@ void InstanceView::paintEvent(QPaintEvent* event) painter.setFont(font); fm = painter.fontMetrics(); } - // about icon - auto about = QIcon::fromTheme("about"); - QRect aboutRect(0, 0, fm.height() * .75, fm.height() * .75); - aboutRect.moveCenter(rect.center()); - aboutRect.moveTop(fm.height() * .75); - auto px = about.pixmap(about.actualSize(aboutRect.size())); - if (!px.isNull()) - painter.drawPixmap(aboutRect, px); // text rect.setTop(rect.top() + fm.height() * 1.5); From 20e2c70464059dec07f875b9cb703bacdcfeee57 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sun, 16 Jul 2023 23:23:33 +0100 Subject: [PATCH 099/247] 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 @@ 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); bool trashInstance(const InstanceId &id); bool trashedSomething(); void undoTrashInstance(); @@ -187,7 +208,8 @@ private slots: 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) - return; - QAction* action = qobject_cast(obj); - if (!action) + 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; - auto map = action->data().toMap(); - if (!map.contains("group")) + + 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; - 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/247] 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 slots: 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/247] 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 f5891940e0d8b52b2250b3e5b80532c71c39c7e8 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 17 Jul 2023 17:22:35 +0300 Subject: [PATCH 102/247] translate strings Signed-off-by: Trial97 --- launcher/ui/instanceview/InstanceView.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/ui/instanceview/InstanceView.cpp b/launcher/ui/instanceview/InstanceView.cpp index 9b0022229..025382cb7 100644 --- a/launcher/ui/instanceview/InstanceView.cpp +++ b/launcher/ui/instanceview/InstanceView.cpp @@ -538,8 +538,8 @@ void InstanceView::paintEvent(QPaintEvent* event) if (model()->rowCount() == 0) { painter.save(); - const QString line1 = "Welcome!"; - const QString line2 = "Add an instance to get started."; + const QString line1 = tr("Welcome!"); + const QString line2 = tr("Add an instance to get started."); auto rect = this->viewport()->rect(); auto font = option.font; font.setPointSize(53); From 5eec0f5901a0b11c9c01fd7b086968951b792fb6 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 18 Jul 2023 23:40:03 +0300 Subject: [PATCH 103/247] 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 104/247] 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 105/247] 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 106/247] 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 107/247] 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 72de2d5254babb209b478ed5e9d7c4e1019d7459 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 5 Aug 2023 21:08:16 +0300 Subject: [PATCH 108/247] Added missing header Signed-off-by: Trial97 --- launcher/meta/BaseEntity.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/launcher/meta/BaseEntity.cpp b/launcher/meta/BaseEntity.cpp index 38e7f600d..18686acf6 100644 --- a/launcher/meta/BaseEntity.cpp +++ b/launcher/meta/BaseEntity.cpp @@ -15,6 +15,7 @@ #include "BaseEntity.h" +#include "Json.h" #include "net/ApiDownload.h" #include "net/HttpMetaCache.h" #include "net/NetJob.h" From f7e8ec1855f26052420694f873ee7e3c167f7545 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 5 Aug 2023 21:17:38 +0300 Subject: [PATCH 109/247] format files Signed-off-by: Trial97 --- launcher/modplatform/flame/FlameAPI.cpp | 6 +++--- .../modplatform/modrinth/ModrinthInstanceCreationTask.cpp | 2 +- launcher/modplatform/technic/SolderPackInstallTask.cpp | 2 +- launcher/ui/pages/modplatform/flame/FlamePage.cpp | 3 ++- launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp | 2 +- launcher/ui/widgets/VariableSizedImageObject.cpp | 2 +- 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/launcher/modplatform/flame/FlameAPI.cpp b/launcher/modplatform/flame/FlameAPI.cpp index 7231b4664..73ed10112 100644 --- a/launcher/modplatform/flame/FlameAPI.cpp +++ b/launcher/modplatform/flame/FlameAPI.cpp @@ -8,9 +8,9 @@ #include "Application.h" #include "BuildConfig.h" #include "Json.h" +#include "net/ApiDownload.h" #include "net/ApiUpload.h" #include "net/NetJob.h" -#include "net/ApiDownload.h" #include "net/Upload.h" Task::Ptr FlameAPI::matchFingerprints(const QList& fingerprints, std::shared_ptr response) @@ -75,8 +75,8 @@ auto FlameAPI::getModDescription(int modId) -> QString auto netJob = makeShared(QString("Flame::ModDescription"), APPLICATION->network()); auto response = std::make_shared(); - netJob->addNetAction( - Net::ApiDownload::makeByteArray(QString("https://api.curseforge.com/v1/mods/%1/description").arg(QString::number(modId)), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray( + QString("https://api.curseforge.com/v1/mods/%1/description").arg(QString::number(modId)), response)); QObject::connect(netJob.get(), &NetJob::succeeded, [&netJob, response, &description] { QJsonParseError parse_error{}; diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp index e0873ce7e..cdbbd42d0 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp @@ -11,8 +11,8 @@ #include "net/ChecksumValidator.h" -#include "net/NetJob.h" #include "net/ApiDownload.h" +#include "net/NetJob.h" #include "settings/INISettingsObject.h" #include "ui/dialogs/CustomMessageBox.h" diff --git a/launcher/modplatform/technic/SolderPackInstallTask.cpp b/launcher/modplatform/technic/SolderPackInstallTask.cpp index b24604983..c162d6253 100644 --- a/launcher/modplatform/technic/SolderPackInstallTask.cpp +++ b/launcher/modplatform/technic/SolderPackInstallTask.cpp @@ -42,8 +42,8 @@ #include "SolderPackManifest.h" #include "TechnicPackProcessor.h" -#include "net/ChecksumValidator.h" #include "net/ApiDownload.h" +#include "net/ChecksumValidator.h" Technic::SolderPackInstallTask::SolderPackInstallTask(shared_qobject_ptr network, const QUrl& solderUrl, diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.cpp b/launcher/ui/pages/modplatform/flame/FlamePage.cpp index a87ff8668..9f0e7085c 100644 --- a/launcher/ui/pages/modplatform/flame/FlamePage.cpp +++ b/launcher/ui/pages/modplatform/flame/FlamePage.cpp @@ -134,7 +134,8 @@ void FlamePage::onSelectionChanged(QModelIndex curr, QModelIndex prev) auto netJob = new NetJob(QString("Flame::PackVersions(%1)").arg(current.name), APPLICATION->network()); auto response = std::make_shared(); int addonId = current.addonId; - netJob->addNetAction(Net::ApiDownload::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 176728ee5..f600e5a78 100644 --- a/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp +++ b/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp @@ -35,9 +35,9 @@ #include "ListModel.h" #include "Application.h" +#include "net/ApiDownload.h" #include "net/HttpMetaCache.h" #include "net/NetJob.h" -#include "net/ApiDownload.h" #include #include "StringUtils.h" diff --git a/launcher/ui/widgets/VariableSizedImageObject.cpp b/launcher/ui/widgets/VariableSizedImageObject.cpp index 51f2966b1..f655fc38d 100644 --- a/launcher/ui/widgets/VariableSizedImageObject.cpp +++ b/launcher/ui/widgets/VariableSizedImageObject.cpp @@ -25,8 +25,8 @@ #include "Application.h" -#include "net/NetJob.h" #include "net/ApiDownload.h" +#include "net/NetJob.h" enum FormatProperties { ImageData = QTextFormat::UserProperty + 1 }; From e2d77f21bab3354a75e68df4c4a124984dfd3fa1 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 11 Aug 2023 00:02:27 +0100 Subject: [PATCH 110/247] =?UTF-8?q?More=20system=20properties=20because=20?= =?UTF-8?q?yes!!=20=F0=9F=8E=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: TheKodeToad --- launcher/BaseInstance.cpp | 2 +- launcher/minecraft/MinecraftInstance.cpp | 6 ++++++ .../org/prismlauncher/EntryPoint.java | 20 ++++++++++++++++--- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/launcher/BaseInstance.cpp b/launcher/BaseInstance.cpp index 70c6da6b3..c48710003 100644 --- a/launcher/BaseInstance.cpp +++ b/launcher/BaseInstance.cpp @@ -385,7 +385,7 @@ QString BaseInstance::name() const QString BaseInstance::windowTitle() const { - return BuildConfig.LAUNCHER_DISPLAYNAME + ": " + name().replace(QRegularExpression("\\s+"), " "); + return BuildConfig.LAUNCHER_DISPLAYNAME + ": " + name(); } // FIXME: why is this here? move it to MinecraftInstance!!! diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 305bff67b..f9b0d05ff 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -634,6 +634,12 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS launchScript += "windowParams " + windowParams + "\n"; } + // instance info + { + launchScript += "instanceName " + name() + "\n"; + launchScript += "instanceIconKey " + iconKey() + "\n"; + } + // legacy auth if (session) { launchScript += "userName " + session->player_name + "\n"; diff --git a/libraries/launcher/org/prismlauncher/EntryPoint.java b/libraries/launcher/org/prismlauncher/EntryPoint.java index 4b59c1da6..4fc57c8e5 100644 --- a/libraries/launcher/org/prismlauncher/EntryPoint.java +++ b/libraries/launcher/org/prismlauncher/EntryPoint.java @@ -105,11 +105,25 @@ private static ExitCode listen() { return ExitCode.ABORT; } + // populate properties to provide mods with more info + String name = params.getString("instanceName", ""); + String iconKey = params.getString("instanceIconKey", "default"); + + System.setProperty("org.prismlauncher.instance.name", name); + System.setProperty("org.prismlauncher.instance.icon.id", iconKey); + System.setProperty("org.prismlauncher.instance.icon.path", "icon.png"); + + // set multimc properties for compatibility + System.setProperty("multimc.instance.title", name); + System.setProperty("multimc.instance.icon", iconKey); + + // launch the game + String launcherType = params.getString("launcher"); + try { Launcher launcher; - String type = params.getString("launcher"); - switch (type) { + switch (launcherType) { case "standard": launcher = new StandardLauncher(params); break; @@ -119,7 +133,7 @@ private static ExitCode listen() { break; default: - throw new IllegalArgumentException("Invalid launcher type: " + type); + throw new IllegalArgumentException("Invalid launcher type: " + launcherType); } launcher.launch(); From f6d8c9659cefd133ec0234fb5a72207088f542dd Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 11 Aug 2023 00:37:27 +0100 Subject: [PATCH 111/247] Rework Signed-off-by: TheKodeToad --- launcher/minecraft/MinecraftInstance.cpp | 5 ++- .../org/prismlauncher/EntryPoint.java | 39 +++++++++++++------ .../launcher/impl/AbstractLauncher.java | 2 +- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index f9b0d05ff..f31fc4001 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -626,7 +626,7 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS { QString windowParams; if (settings()->get("LaunchMaximized").toBool()) - windowParams = "max"; + windowParams = "maximized"; else windowParams = QString("%1x%2").arg(settings()->get("MinecraftWinWidth").toInt()).arg(settings()->get("MinecraftWinHeight").toInt()); @@ -637,7 +637,8 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS // instance info { launchScript += "instanceName " + name() + "\n"; - launchScript += "instanceIconKey " + iconKey() + "\n"; + launchScript += "instanceIconKey " + name() + "\n"; + launchScript += "instanceIconPath icon.png\n"; // we already save a copy here } // legacy auth diff --git a/libraries/launcher/org/prismlauncher/EntryPoint.java b/libraries/launcher/org/prismlauncher/EntryPoint.java index 4fc57c8e5..9a8a7ee1d 100644 --- a/libraries/launcher/org/prismlauncher/EntryPoint.java +++ b/libraries/launcher/org/prismlauncher/EntryPoint.java @@ -105,19 +105,8 @@ private static ExitCode listen() { return ExitCode.ABORT; } - // populate properties to provide mods with more info - String name = params.getString("instanceName", ""); - String iconKey = params.getString("instanceIconKey", "default"); + setProperties(params); - System.setProperty("org.prismlauncher.instance.name", name); - System.setProperty("org.prismlauncher.instance.icon.id", iconKey); - System.setProperty("org.prismlauncher.instance.icon.path", "icon.png"); - - // set multimc properties for compatibility - System.setProperty("multimc.instance.title", name); - System.setProperty("multimc.instance.icon", iconKey); - - // launch the game String launcherType = params.getString("launcher"); try { @@ -150,6 +139,32 @@ private static ExitCode listen() { } } + private static void setProperties(Parameters params) { + String name = params.getString("instanceName", null); + String iconId = params.getString("instanceIconKey", null); + String iconPath = params.getString("instanceIconPath", null); + String windowTitle = params.getString("windowTitle", null); + String windowDimensions = params.getString("windowParams", null); + + // set useful properties for mods + if (name != null) + System.setProperty("org.prismlauncher.instance.name", name); + if (iconId != null) + System.setProperty("org.prismlauncher.instance.icon.id", iconId); + if (iconPath != null) + System.setProperty("org.prismlauncher.instance.icon.path", iconPath); + if (windowTitle != null) + System.setProperty("org.prismlauncher.window.title", windowTitle); + if (windowDimensions != null) + System.setProperty("org.prismlauncher.window.dimensions", windowDimensions); + + // set multimc properties for compatibility + if (name != null) + System.setProperty("multimc.instance.title", name); + if (iconId != null) + System.setProperty("multimc.instance.icon", iconId); + } + private static PreLaunchAction parseLine(String input, Parameters params) throws ParseException { switch (input) { case "": diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java index 761837041..de28a0401 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java @@ -83,7 +83,7 @@ protected AbstractLauncher(Parameters params) { String windowParams = params.getString("windowParams", null); - if ("max".equals(windowParams) || windowParams == null) { + if ("maximized".equals(windowParams) || windowParams == null) { maximize = windowParams != null; width = DEFAULT_WINDOW_WIDTH; From 71890707c034a0503a92128335c13773e51dc43b Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 12 Aug 2023 12:54:10 +0300 Subject: [PATCH 112/247] 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 6d070e8607cb7bd48c06dfac7e613b4805bed7a6 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sat, 12 Aug 2023 21:50:02 +0100 Subject: [PATCH 113/247] "Format" 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 f31fc4001..abdc4ea39 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -638,7 +638,7 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS { launchScript += "instanceName " + name() + "\n"; launchScript += "instanceIconKey " + name() + "\n"; - launchScript += "instanceIconPath icon.png\n"; // we already save a copy here + launchScript += "instanceIconPath icon.png\n"; // we already save a copy here } // legacy auth From 589d8b6923531a53cbccabb8b79ed9861d0fe27c Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 16 Aug 2023 19:53:39 +0300 Subject: [PATCH 114/247] 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 115/247] 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 116/247] 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 117/247] removed warning for one mod selected Signed-off-by: Trial97 --- launcher/ui/pages/instance/ModFolderPage.cpp | 29 +++++++++----------- 1 file changed, 13 insertions(+), 16 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()); - - auto response = CustomMessageBox::selectable(this, tr("Confirm Removal"), text, QMessageBox::Warning, - QMessageBox::Yes | QMessageBox::No, QMessageBox::No) - ->exec(); - - if (response == QMessageBox::Yes) - m_model->deleteModsMetadata(selection); + 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(); + + if (response != QMessageBox::Yes) + return; + } + + m_model->deleteModsMetadata(selection); } From d3acac16e664b5b36d5f7eb0c857bb400fdbdac8 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 18 Aug 2023 13:10:13 +0300 Subject: [PATCH 118/247] 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 fecc1e087aefd276a18d7941d123f1f261a6d0d2 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 18 Aug 2023 13:35:50 +0300 Subject: [PATCH 119/247] Add Trial97 to contribuitors list Signed-off-by: Trial97 --- launcher/ui/dialogs/AboutDialog.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/launcher/ui/dialogs/AboutDialog.cpp b/launcher/ui/dialogs/AboutDialog.cpp index 3c6f6ef16..f5eaff7a1 100644 --- a/launcher/ui/dialogs/AboutDialog.cpp +++ b/launcher/ui/dialogs/AboutDialog.cpp @@ -85,6 +85,7 @@ QString getCreditsHtml() stream << QString("

TayouVR %1

\n").arg(getGitHub("TayouVR")); stream << QString("

TheKodeToad %1

\n").arg(getGitHub("TheKodeToad")); stream << QString("

getchoo %1

\n").arg(getGitHub("getchoo")); + stream << QString("

Alexandru Tripon (Trial97) %1

\n").arg(getGitHub("Trial97")); stream << "
\n"; // TODO: possibly retrieve from git history at build time? From d25e89a4c1304092f99b3516e91a5b989a110212 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 20 Aug 2023 00:48:46 +0300 Subject: [PATCH 120/247] 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 254444470f020b086648ac496ebfffb7d3e9ce05 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 20 Aug 2023 16:40:58 +0300 Subject: [PATCH 121/247] 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 8e5be6f4203f777ba10d61608c2d5b80c05cc0d4 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 21 Aug 2023 00:09:46 +0300 Subject: [PATCH 122/247] 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 02264f67f35b47a6074ea7cdb925e94ea8ac590d Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 21 Aug 2023 14:15:51 +0300 Subject: [PATCH 123/247] 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 ea384d59fbff194f26f4fc678ce2388f01b2f484 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 24 Aug 2023 12:41:37 +0300 Subject: [PATCH 124/247] 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 125/247] 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; + } + + selectedOptionalMods = optionalModDialog.getResult(); + } + for (const auto& result : results) { + auto relpath = FS::PathCombine(result.targetFolder, result.fileName); + if (!result.required && !selectedOptionalMods.contains(relpath)) { + relpath += ".disabled"; } - auto relpath = FS::PathCombine("minecraft", result.targetFolder, filename); + 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 126/247] 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 127/247] 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 a2d44744fe71af431420f93b178a074a48b9232d Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 26 Aug 2023 23:36:46 +0300 Subject: [PATCH 128/247] 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 129/247] Rewrite optional mod dialog Signed-off-by: TheKodeToad --- .../pages/modplatform/OptionalModDialog.cpp | 142 ++++------------- .../ui/pages/modplatform/OptionalModDialog.h | 41 +---- .../ui/pages/modplatform/OptionalModDialog.ui | 148 +++++++++--------- 3 files changed, 111 insertions(+), 220 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) +OptionalModDialog::OptionalModDialog(QWidget* parent, const QStringList& mods) : QDialog(parent), ui(new Ui::OptionalModDialog) { - for (const auto& mod : m_mods) { - m_selected[mod] = enabled; + 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); } - emit dataChanged(OptionalModListModel::index(0, EnabledColumn), OptionalModListModel::index(m_mods.size() - 1, EnabledColumn)); + 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); + }); } -void OptionalModListModel::toggleMod(QString mod, int index) +OptionalModDialog::~OptionalModDialog() { - auto enable = !m_selected.value(mod, false); - - setMod(mod, index, enable); + delete ui; } -void OptionalModListModel::setMod(QString mod, int index, bool enable, bool shouldEmit) +QStringList OptionalModDialog::getResult() { - if (m_selected.value(mod, false) == enable) - return; - - m_selected[mod] = enable; - - if (shouldEmit) { - emit dataChanged(OptionalModListModel::index(index, EnabledColumn), OptionalModListModel::index(index, EnabledColumn)); + 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()); } -} - -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; + 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 - - + + + + + Select All + + + + + + + Deselect All + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Unchecked mods will be disabled. + + + + - - - 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 79652799bd88299f1e1bdb1b16449fae56c59913 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 27 Aug 2023 21:30:46 +0300 Subject: [PATCH 130/247] Made text smaller Signed-off-by: Trial97 --- launcher/ui/instanceview/InstanceView.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/ui/instanceview/InstanceView.cpp b/launcher/ui/instanceview/InstanceView.cpp index 11402e58f..7530fdfba 100644 --- a/launcher/ui/instanceview/InstanceView.cpp +++ b/launcher/ui/instanceview/InstanceView.cpp @@ -482,10 +482,10 @@ void InstanceView::paintEvent([[maybe_unused]] QPaintEvent* event) if (model()->rowCount() == 0) { painter.save(); const QString line1 = tr("Welcome!"); - const QString line2 = tr("Add an instance to get started."); + const QString line2 = tr("Click \"Add Instance\" to get started."); auto rect = this->viewport()->rect(); auto font = option.font; - font.setPointSize(53); + font.setPointSize(37); painter.setFont(font); auto fm = painter.fontMetrics(); From 07d8598638247d8dfb2fcd2e38c4498e90570f6e Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 27 Aug 2023 23:40:32 +0300 Subject: [PATCH 131/247] 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 5d70f4dbca4405704e03e8428a7829b6a7cc32eb Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 28 Aug 2023 08:50:10 +0300 Subject: [PATCH 132/247] 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 311e36b5d6dfaed449a3bbdada3d2e5b87f2b6d9 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 28 Aug 2023 11:52:26 +0300 Subject: [PATCH 133/247] 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 211865a1e1098245228aea75ee70d7e7b60ebd05 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 28 Aug 2023 13:31:19 +0300 Subject: [PATCH 134/247] handle gracefully the upload abort Signed-off-by: Trial97 --- .../ui/pages/instance/ScreenshotsPage.cpp | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/launcher/ui/pages/instance/ScreenshotsPage.cpp b/launcher/ui/pages/instance/ScreenshotsPage.cpp index da56112c1..25f978cea 100644 --- a/launcher/ui/pages/instance/ScreenshotsPage.cpp +++ b/launcher/ui/pages/instance/ScreenshotsPage.cpp @@ -393,11 +393,18 @@ void ScreenshotsPage::on_actionUpload_triggered() auto screenshot = std::make_shared(info); job->addNetAction(ImgurUpload::make(screenshot)); + connect(job.get(), &Task::failed, [this](QString reason) { + CustomMessageBox::selectable(this, tr("Failed to upload screenshots!"), reason, QMessageBox::Critical)->show(); + }); + connect(job.get(), &Task::aborted, [this] { + CustomMessageBox::selectable(this, tr("Screenshots upload aborted"), tr("The task has been aborted by the user."), + QMessageBox::Information) + ->show(); + }); + m_uploadActive = true; - if (dialog.execWithTask(job.get()) != QDialog::Accepted) { - CustomMessageBox::selectable(this, tr("Failed to upload screenshots!"), tr("Unknown error"), QMessageBox::Warning)->exec(); - } else { + if (dialog.execWithTask(job.get()) == QDialog::Accepted) { auto link = screenshot->m_url; QClipboard* clipboard = QApplication::clipboard(); qDebug() << "ImgurUpload link" << link; @@ -426,18 +433,31 @@ void ScreenshotsPage::on_actionUpload_triggered() albumTask->addNetAction(imgurAlbum); task.addTask(job); task.addTask(albumTask); - m_uploadActive = true; - if (dialog.execWithTask(&task) != QDialog::Accepted || imgurResult->id.isEmpty()) { - CustomMessageBox::selectable(this, tr("Failed to upload screenshots!"), tr("Unknown error"), QMessageBox::Warning)->exec(); - } else { - auto link = QString("https://imgur.com/a/%1").arg(imgurResult->id); - qDebug() << "ImgurUpload link" << link; - QClipboard* clipboard = QApplication::clipboard(); - clipboard->setText(link); - CustomMessageBox::selectable(this, tr("Upload finished"), - tr("The link to the uploaded album has been placed in your clipboard.").arg(link), + + connect(&task, &Task::failed, [this](QString reason) { + CustomMessageBox::selectable(this, tr("Failed to upload screenshots!"), reason, QMessageBox::Critical)->show(); + }); + connect(&task, &Task::aborted, [this] { + CustomMessageBox::selectable(this, tr("Screenshots upload aborted"), tr("The task has been aborted by the user."), QMessageBox::Information) - ->exec(); + ->show(); + }); + + m_uploadActive = true; + if (dialog.execWithTask(&task) == QDialog::Accepted) { + if (imgurResult->id.isEmpty()) { + CustomMessageBox::selectable(this, tr("Failed to upload screenshots!"), tr("Unknown error"), QMessageBox::Warning)->exec(); + } else { + auto link = QString("https://imgur.com/a/%1").arg(imgurResult->id); + qDebug() << "ImgurUpload link" << link; + QClipboard* clipboard = QApplication::clipboard(); + clipboard->setText(link); + CustomMessageBox::selectable( + this, tr("Upload finished"), + tr("The link to the uploaded album has been placed in your clipboard.").arg(link), + QMessageBox::Information) + ->exec(); + } } m_uploadActive = false; } From bdc2fca711d3ad110d7ef2a2c1ae99537144a00e Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Mon, 28 Aug 2023 10:26:03 +0200 Subject: [PATCH 135/247] 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 8c30cb370676970f62a56e9f5615b22f646eb2e3 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Tue, 29 Aug 2023 13:27:06 +0100 Subject: [PATCH 136/247] 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 137/247] 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 138/247] 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 139/247] 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 140/247] 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 141/247] 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 142/247] 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 17f696bffcf83ff9d40964137c9743b590161e77 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 1 Sep 2023 22:25:15 +0300 Subject: [PATCH 143/247] 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 144/247] 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 145/247] 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 @@ public void launch() throws Throwable { 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 @@ private static Field findMinecraftGameDirField(Class clazz) { return null; } - -======= ->>>>>>> upstream/develop:libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java } From bbf4e3b04d98eac840134b1687aba63ec180ead0 Mon Sep 17 00:00:00 2001 From: seth Date: Sun, 3 Sep 2023 18:11:23 -0400 Subject: [PATCH 146/247] 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 147/247] 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 148/247] 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 149/247] 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 150/247] 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 151/247] 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 152/247] 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 153/247] 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 154/247] 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 155/247] 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 156/247] 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 157/247] 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 158/247] 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 159/247] 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 160/247] 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 161/247] 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 162/247] 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 163/247] 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 164/247] 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 165/247] 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 166/247] 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 167/247] 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 168/247] 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 169/247] 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 170/247] 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 171/247] 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 172/247] 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 173/247] 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 174/247] 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 175/247] 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 176/247] 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 177/247] 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 178/247] 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 179/247] 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 180/247] 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 181/247] 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 182/247] 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 183/247] 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 184/247] 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 185/247] 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 186/247] 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 187/247] 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 188/247] 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 189/247] 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 4f1ee854249f62c36dd9e8e1d9fbbc0b20bc09ec Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 6 Oct 2023 00:21:37 +0300 Subject: [PATCH 190/247] check for minecraft java runtime Signed-off-by: Trial97 --- launcher/java/JavaUtils.cpp | 43 +++++++++++++++++++++++++++++++++++++ launcher/java/JavaUtils.h | 1 + 2 files changed, 44 insertions(+) diff --git a/launcher/java/JavaUtils.cpp b/launcher/java/JavaUtils.cpp index cca1ed6d4..9f682e086 100644 --- a/launcher/java/JavaUtils.cpp +++ b/launcher/java/JavaUtils.cpp @@ -335,6 +335,7 @@ QList JavaUtils::FindJavaPaths() } } + javas.append(getMinecraftJavaBundle()); candidates = addJavasFromEnv(candidates); candidates.removeDuplicates(); return candidates; @@ -360,6 +361,7 @@ QList JavaUtils::FindJavaPaths() javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java"); javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java"); } + javas.append(getMinecraftJavaBundle()); javas = addJavasFromEnv(javas); javas.removeDuplicates(); return javas; @@ -411,6 +413,7 @@ QList JavaUtils::FindJavaPaths() // javas downloaded by sdkman scanJavaDirs(FS::PathCombine(home, ".sdkman/candidates/java")); + javas.append(getMinecraftJavaBundle()); javas = addJavasFromEnv(javas); javas.removeDuplicates(); return javas; @@ -423,6 +426,7 @@ QList JavaUtils::FindJavaPaths() QList javas; javas.append(this->GetDefaultJava()->path); + javas.append(getMinecraftJavaBundle()); return addJavasFromEnv(javas); } #endif @@ -431,3 +435,42 @@ QString JavaUtils::getJavaCheckPath() { return APPLICATION->getJarPath("JavaCheck.jar"); } + +QStringList getMinecraftJavaBundle() +{ + QString partialPath; + QString executable = "java"; +#if defined(Q_OS_OSX) + partialPath = FS::PathCombine(QDir::homePath(), "Library/Application Support"); +#elif defined(Q_OS_WIN32) + partialPath = QProcessEnvironment::systemEnvironment().value("LOCALAPPDATA", ""); + executable += "w.exe"; +#else + partialPath = QDir::homePath(); +#endif + auto minecraftPath = FS::PathCombine(partialPath, ".minecraft", "runtime"); + QStringList javas; + QStringList processpaths{ minecraftPath }; + + while (!processpaths.isEmpty()) { + auto dirPath = processpaths.takeFirst(); + QDir dir(dirPath); + if (!dir.exists()) + continue; + auto entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); + auto binFound = false; + for (auto& entry : entries) { + if (entry.baseName() == "bin") { + javas.append(FS::PathCombine(entry.canonicalFilePath(), executable)); + binFound = true; + break; + } + } + if (!binFound) { + for (auto& entry : entries) { + processpaths << entry.canonicalFilePath(); + } + } + } + return javas; +} diff --git a/launcher/java/JavaUtils.h b/launcher/java/JavaUtils.h index 616179706..2fb03af7a 100644 --- a/launcher/java/JavaUtils.h +++ b/launcher/java/JavaUtils.h @@ -26,6 +26,7 @@ QString stripVariableEntries(QString name, QString target, QString remove); QProcessEnvironment CleanEnviroment(); +QStringList getMinecraftJavaBundle(); class JavaUtils : public QObject { Q_OBJECT From 82461b1113b01a4305046b5da69dbf60e885f756 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 6 Oct 2023 08:51:45 +0300 Subject: [PATCH 191/247] rename for windows Signed-off-by: Trial97 --- launcher/java/JavaUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/java/JavaUtils.cpp b/launcher/java/JavaUtils.cpp index 9f682e086..10bbb1f1b 100644 --- a/launcher/java/JavaUtils.cpp +++ b/launcher/java/JavaUtils.cpp @@ -335,7 +335,7 @@ QList JavaUtils::FindJavaPaths() } } - javas.append(getMinecraftJavaBundle()); + candidates.append(getMinecraftJavaBundle()); candidates = addJavasFromEnv(candidates); candidates.removeDuplicates(); return candidates; From d49f81d132f57089361dd7f8af0e0ef1781028e0 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 6 Oct 2023 11:27:04 +0100 Subject: [PATCH 192/247] 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 193/247] 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 8d0a53273f6fbd75c563b184f5bc326adfaf02d2 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 6 Oct 2023 18:42:54 +0300 Subject: [PATCH 194/247] Add no_color env variable Signed-off-by: Trial97 --- launcher/minecraft/MinecraftInstance.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index f9833b972..b536951a8 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -536,6 +536,7 @@ QMap MinecraftInstance::getVariables() out.insert("INST_MC_DIR", QDir::toNativeSeparators(QDir(gameRoot()).absolutePath())); out.insert("INST_JAVA", settings()->get("JavaPath").toString()); out.insert("INST_JAVA_ARGS", javaArguments().join(' ')); + out.insert("NO_COLOR", "1"); return out; } From dc74ea73825e7997538bc2aa381b284260a40551 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 6 Oct 2023 19:27:26 +0300 Subject: [PATCH 195/247] fixed wrong account selection Signed-off-by: Trial97 --- launcher/LaunchController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp index 21a146062..9fb385777 100644 --- a/launcher/LaunchController.cpp +++ b/launcher/LaunchController.cpp @@ -106,7 +106,7 @@ void LaunchController::decideAccount() // Select the account to use. If the instance has a specific account set, that will be used. Otherwise, the default account will be used auto instanceAccountId = m_instance->settings()->get("InstanceAccountId").toString(); auto instanceAccountIndex = accounts->findAccountByProfileId(instanceAccountId); - if (instanceAccountIndex == -1) { + if (instanceAccountIndex == -1 || instanceAccountId.isEmpty()) { m_accountToUse = accounts->defaultAccount(); } else { m_accountToUse = accounts->at(instanceAccountIndex); From 05caa874bc3550c370ea6f278c1849cc481b8125 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 8 Oct 2023 00:18:30 +0000 Subject: [PATCH 196/247] 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 197/247] 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 198/247] 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 199/247] 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 200/247] 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 201/247] 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 202/247] 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 203/247] 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 204/247] =?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 205/247] 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 206/247] 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: '' From b1bd0ceadef1eeab7e4f5d10f26ba1865f46525e Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 17 Oct 2023 09:29:47 +0300 Subject: [PATCH 207/247] fixed code signing for appImage Signed-off-by: Trial97 --- .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 7e542d4e5..8077ea59a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -501,7 +501,7 @@ jobs: export SIGN=1 export SIGN_KEY=${{ secrets.GPG_PRIVATE_KEY_ID }} mkdir -p ~/.gnupg/ - printf "$GPG_PRIVATE_KEY" | base64 --decode > ~/.gnupg/private.key + echo "$GPG_PRIVATE_KEY" > ~/.gnupg/private.key gpg --import ~/.gnupg/private.key else echo ":warning: Skipped code signing for Linux AppImage, as gpg key was not present." >> $GITHUB_STEP_SUMMARY From d348f20dd994c7c60510e33d9ec0496d961f45d1 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Tue, 17 Oct 2023 10:00:17 +0200 Subject: [PATCH 208/247] fix: allow loading themes with missing resources folder Signed-off-by: Sefa Eyeoglu --- launcher/ui/themes/CustomTheme.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/launcher/ui/themes/CustomTheme.cpp b/launcher/ui/themes/CustomTheme.cpp index 29ecf6254..4859983c6 100644 --- a/launcher/ui/themes/CustomTheme.cpp +++ b/launcher/ui/themes/CustomTheme.cpp @@ -165,11 +165,15 @@ CustomTheme::CustomTheme(ITheme* baseTheme, QFileInfo& fileInfo, bool isManifest QString path = FS::PathCombine("themes", m_id); QString pathResources = FS::PathCombine("themes", m_id, "resources"); - if (!FS::ensureFolderPathExists(path) || !FS::ensureFolderPathExists(pathResources)) { - themeWarningLog() << "couldn't create folder for theme!"; + if (!FS::ensureFolderPathExists(path)) { + themeWarningLog() << "Theme directory for" << m_id << "could not be created. This theme might be invalid"; return; } + if (!FS::ensureFolderPathExists(pathResources)) { + themeWarningLog() << "Resources directory for" << m_id << "could not be created"; + } + auto themeFilePath = FS::PathCombine(path, themeFile); bool jsonDataIncomplete = false; @@ -230,7 +234,11 @@ CustomTheme::CustomTheme(ITheme* baseTheme, QFileInfo& fileInfo, bool isManifest QStringList CustomTheme::searchPaths() { - return { FS::PathCombine("themes", m_id, "resources") }; + QString pathResources = FS::PathCombine("themes", m_id, "resources"); + if (QFileInfo::exists(pathResources)) + return { pathResources }; + + return {}; } QString CustomTheme::id() From 069bc887f1f74b9ea29130d33042bafc82d97f57 Mon Sep 17 00:00:00 2001 From: seth Date: Tue, 17 Oct 2023 19:05:05 -0400 Subject: [PATCH 209/247] fix: don't always build updater on platforms besides mac Signed-off-by: seth --- CMakeLists.txt | 5 +++++ launcher/CMakeLists.txt | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index be443fe31..faeb1c44e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -347,6 +347,11 @@ add_subdirectory(program_info) ####################################### Install layout ####################################### set(Launcher_ENABLE_UPDATER NO) +set(Launcher_BUILD_UPDATER NO) + +if (NOT APPLE AND (NOT Launcher_UPDATER_GITHUB_REPO STREQUAL "" AND NOT Launcher_BUILD_ARTIFACT STREQUAL "")) + set(Launcher_BUILD_UPDATER YES) +endif() if(NOT (UNIX AND APPLE)) # Install "portable.txt" if selected component is "portable" diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index d15dc85de..19b837a2f 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -1306,7 +1306,7 @@ install(TARGETS ${Launcher_Name} FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime ) -if(NOT APPLE OR (DEFINED Launcher_BUILD_UPDATER AND Launcher_BUILD_UPDATER)) +if(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}) From 1f2483c39ee980f317aa8a4bcc5ccebe3a02045f Mon Sep 17 00:00:00 2001 From: Indrale Dnyaneshwar <118615488+Dnyanu76@users.noreply.github.com> Date: Fri, 20 Oct 2023 00:31:47 +0530 Subject: [PATCH 210/247] Remove typo Signed-off-by: Indrale Dnyaneshwar <118615488+Dnyanu76@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 093bc94da..61bf4c20b 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ For **Arch**, **Debian**, **Fedora**, **OpenSUSE (Tumbleweed)** and **Gentoo**, [![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. +These packages are also available to all the distributions based on the ones mentioned above. ## Community & Support From e9fd02baca57db1af85dd4f8f36a4c0fed2de8ff Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 20 Oct 2023 13:12:16 +0100 Subject: [PATCH 211/247] Fix code style 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 dd4a56596..cc33c05af 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -187,7 +187,7 @@ void MinecraftInstance::loadSpecificSettings() // Legacy-related options auto legacySettings = m_settings->registerSetting("OverrideLegacySettings", false); m_settings->registerOverride(global_settings->getSetting("OnlineFixes"), legacySettings); - + m_settings->registerSetting("UseEnv", false); m_settings->registerSetting("OverrideEnv", false); m_settings->registerSetting("Env", QVariant(QMap())); From 80723eeca174afbbb5af31bf422fe7b2a8e8cad3 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 20 Oct 2023 16:41:32 +0300 Subject: [PATCH 212/247] Fixed FTBApp import icon Signed-off-by: Trial97 --- launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp index 43ff1c0fe..d46f16b4d 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp +++ b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp @@ -90,7 +90,7 @@ void ImportFTBPage::suggestCurrent() } dialog->setSuggestedPack(selected.name, new PackInstallTask(selected)); - QString editedLogoName = QString("ftb_%1_%2,jpg").arg(selected.name, selected.id); + QString editedLogoName = QString("ftb_%1_%2.jpg").arg(selected.name, QString::number(selected.id)); dialog->setSuggestedIconFromFile(FS::PathCombine(selected.path, "folder.jpg"), editedLogoName); } From 7b62d146838967903addc80247cf6e7db9862967 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 20 Oct 2023 17:15:12 +0300 Subject: [PATCH 213/247] fixed squished mod icons Signed-off-by: Trial97 --- launcher/minecraft/mod/ModFolderModel.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index a5f1489dd..b1e35f405 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -127,7 +127,18 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const if (column == NAME_COLUMN && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink())) return APPLICATION->getThemedIcon("status-yellow"); if (column == ImageColumn) { - return at(row)->icon({ 32, 32 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding); + QSize size(32, 32); + auto icon = at(row)->icon(size, Qt::AspectRatioMode::KeepAspectRatioByExpanding); + if (icon.isNull()) { + QPixmap transparent_image(size); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + transparent_image.fill(QColorConstants::Transparent); +#else + transparent_image.fill(QColor(0, 0, 0, 0)); +#endif + return transparent_image; + } + return icon; } return {}; } From 5d926582c7dcd4a288b9539a4a15338c1bfce21e Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 20 Oct 2023 19:08:56 +0300 Subject: [PATCH 214/247] added Image column size hint Signed-off-by: Trial97 --- launcher/minecraft/mod/ModFolderModel.cpp | 18 ++++++------------ .../minecraft/mod/ResourcePackFolderModel.cpp | 5 +++++ .../minecraft/mod/TexturePackFolderModel.cpp | 5 +++++ 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index b1e35f405..11f7cd0f1 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -127,21 +127,15 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const if (column == NAME_COLUMN && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink())) return APPLICATION->getThemedIcon("status-yellow"); if (column == ImageColumn) { - QSize size(32, 32); - auto icon = at(row)->icon(size, Qt::AspectRatioMode::KeepAspectRatioByExpanding); - if (icon.isNull()) { - QPixmap transparent_image(size); -#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) - transparent_image.fill(QColorConstants::Transparent); -#else - transparent_image.fill(QColor(0, 0, 0, 0)); -#endif - return transparent_image; - } - return icon; + return at(row)->icon({ 32, 32 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding); } return {}; } + case Qt::SizeHintRole: + if (column == ImageColumn) { + return QSize(32, 32); + } + return {}; case Qt::CheckStateRole: switch (column) { case ActiveColumn: diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp index f27431576..efe1cc5dd 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp +++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp @@ -117,6 +117,11 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const } return m_resources[row]->internal_id(); } + case Qt::SizeHintRole: + if (column == ImageColumn) { + return QSize(32, 32); + } + return {}; case Qt::CheckStateRole: switch (column) { case ActiveColumn: diff --git a/launcher/minecraft/mod/TexturePackFolderModel.cpp b/launcher/minecraft/mod/TexturePackFolderModel.cpp index 5c5f2b7c1..e39be1fb9 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.cpp +++ b/launcher/minecraft/mod/TexturePackFolderModel.cpp @@ -104,6 +104,11 @@ QVariant TexturePackFolderModel::data(const QModelIndex& index, int role) const } return {}; } + case Qt::SizeHintRole: + if (column == ImageColumn) { + return QSize(32, 32); + } + return {}; case Qt::CheckStateRole: if (column == ActiveColumn) { return m_resources[row]->enabled() ? Qt::Checked : Qt::Unchecked; From e7e80e704f06c4fd73231228fa04d583d427f635 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 22 Oct 2023 00:18:23 +0000 Subject: [PATCH 215/247] 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/01441e14af5e29c9d27ace398e6dd0b293e25a54' (2023-10-11) → 'github:nixos/nixpkgs/44881e03af1c730cbb1d72a4d41274a2c957813a' (2023-10-21) • Updated input 'pre-commit-hooks': 'github:cachix/pre-commit-hooks.nix/42e1b6095ef80a51f79595d9951eb38e91c4e6ca' (2023-10-09) → 'github:cachix/pre-commit-hooks.nix/8cc349bfd082da8782b989cad2158c9ad5bd70fd' (2023-10-19) --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index ad18ff615..d71403358 100644 --- a/flake.lock +++ b/flake.lock @@ -106,11 +106,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1697009197, - "narHash": "sha256-viVRhBTFT8fPJTb1N3brQIpFZnttmwo3JVKNuWRVc3s=", + "lastModified": 1697886341, + "narHash": "sha256-AdE67xPty9M9wn36nPVp6aDntIdigrs7UbyaGv1VAaM=", "owner": "nixos", "repo": "nixpkgs", - "rev": "01441e14af5e29c9d27ace398e6dd0b293e25a54", + "rev": "44881e03af1c730cbb1d72a4d41274a2c957813a", "type": "github" }, "original": { @@ -153,11 +153,11 @@ ] }, "locked": { - "lastModified": 1696846637, - "narHash": "sha256-0hv4kbXxci2+pxhuXlVgftj/Jq79VSmtAyvfabCCtYk=", + "lastModified": 1697746376, + "narHash": "sha256-gu77VkgdfaHgNCVufeb6WP9oqFLjwK4jHcoPZmBVF3E=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "42e1b6095ef80a51f79595d9951eb38e91c4e6ca", + "rev": "8cc349bfd082da8782b989cad2158c9ad5bd70fd", "type": "github" }, "original": { From db19362a970d2ffb4b25d1fbc2a5b1a1139205b9 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 22 Oct 2023 21:19:44 +0200 Subject: [PATCH 216/247] feat: add launcher brand and version props Signed-off-by: Sefa Eyeoglu --- launcher/minecraft/MinecraftInstance.cpp | 6 ++++++ libraries/launcher/org/prismlauncher/EntryPoint.java | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index f324ce87a..a4571e626 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -718,6 +718,12 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS launchScript += "windowParams " + windowParams + "\n"; } + // launcher info + { + launchScript += "launcherBrand " + BuildConfig.LAUNCHER_NAME + "\n"; + launchScript += "launcherVersion " + BuildConfig.printableVersionString() + "\n"; + } + // instance info { launchScript += "instanceName " + name() + "\n"; diff --git a/libraries/launcher/org/prismlauncher/EntryPoint.java b/libraries/launcher/org/prismlauncher/EntryPoint.java index eec2df9a9..699adfe30 100644 --- a/libraries/launcher/org/prismlauncher/EntryPoint.java +++ b/libraries/launcher/org/prismlauncher/EntryPoint.java @@ -142,12 +142,19 @@ private static ExitCode listen() { } private static void setProperties(Parameters params) { + String launcherBrand = params.getString("launcherBrand", null); + String launcherVersion = params.getString("launcherVersion", null); String name = params.getString("instanceName", null); String iconId = params.getString("instanceIconKey", null); String iconPath = params.getString("instanceIconPath", null); String windowTitle = params.getString("windowTitle", null); String windowDimensions = params.getString("windowParams", null); + if (launcherBrand != null) + System.setProperty("minecraft.launcher.brand", launcherBrand); + if (launcherVersion != null) + System.setProperty("minecraft.launcher.version", launcherVersion); + // set useful properties for mods if (name != null) System.setProperty("org.prismlauncher.instance.name", name); From 7d5206818b70cede91aad1bf0dbcec92e5acde5a Mon Sep 17 00:00:00 2001 From: Tayou Date: Sun, 22 Oct 2023 21:00:52 +0200 Subject: [PATCH 217/247] fix switch/case fallthrough Signed-off-by: Tayou --- launcher/minecraft/mod/tasks/LocalModParseTask.cpp | 2 ++ launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp | 2 ++ launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp | 2 ++ 3 files changed, 6 insertions(+) diff --git a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp index 7ec00c0c6..e9e12d86a 100644 --- a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp @@ -688,6 +688,7 @@ bool loadIconFile(const Mod& mod) return png_invalid(); // icon invalid } } + return false; } case ResourceType::ZIPFILE: { QuaZip zip(mod.fileinfo().filePath()); @@ -714,6 +715,7 @@ bool loadIconFile(const Mod& mod) } else { return png_invalid(); // could not set icon as current file. } + return false; } case ResourceType::LITEMOD: { return false; // can lightmods even have icons? diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 7b9f4f594..7e02fd51c 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -232,6 +232,7 @@ bool processPackPNG(const ResourcePack& pack) } else { return png_invalid(); // pack.png does not exists or is not a valid file. } + return false; // not processed correctly; https://github.com/PrismLauncher/PrismLauncher/issues/1740 } case ResourceType::ZIPFILE: { Q_ASSERT(pack.type() == ResourceType::ZIPFILE); @@ -259,6 +260,7 @@ bool processPackPNG(const ResourcePack& pack) } else { return png_invalid(); // could not set pack.mcmeta as current file. } + return false; // not processed correctly; https://github.com/PrismLauncher/PrismLauncher/issues/1740 } default: qWarning() << "Invalid type for resource pack parse task!"; diff --git a/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp index 887a1062e..13f157dff 100644 --- a/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp @@ -186,6 +186,7 @@ bool processPackPNG(const TexturePack& pack) } else { return png_invalid(); // pack.png does not exists or is not a valid file. } + return false; } case ResourceType::ZIPFILE: { Q_ASSERT(pack.type() == ResourceType::ZIPFILE); @@ -215,6 +216,7 @@ bool processPackPNG(const TexturePack& pack) zip.close(); return png_invalid(); // could not set pack.mcmeta as current file. } + return false; } default: qWarning() << "Invalid type for resource pack parse task!"; From 0fe3241df61e014a0b427282258d156546880019 Mon Sep 17 00:00:00 2001 From: Tayou Date: Sat, 21 Oct 2023 20:05:14 +0200 Subject: [PATCH 218/247] remove now unneeded Q_ASSERTs Signed-off-by: Tayou --- launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp | 2 -- launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp | 2 -- 2 files changed, 4 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 7e02fd51c..26bc07637 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -235,8 +235,6 @@ bool processPackPNG(const ResourcePack& pack) return false; // not processed correctly; https://github.com/PrismLauncher/PrismLauncher/issues/1740 } case ResourceType::ZIPFILE: { - Q_ASSERT(pack.type() == ResourceType::ZIPFILE); - QuaZip zip(pack.fileinfo().filePath()); if (!zip.open(QuaZip::mdUnzip)) return false; // can't open zip file diff --git a/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp index 13f157dff..d7e61ca90 100644 --- a/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp @@ -189,8 +189,6 @@ bool processPackPNG(const TexturePack& pack) return false; } case ResourceType::ZIPFILE: { - Q_ASSERT(pack.type() == ResourceType::ZIPFILE); - QuaZip zip(pack.fileinfo().filePath()); if (!zip.open(QuaZip::mdUnzip)) return false; // can't open zip file From 87f2f88d4cdba8070e8cd259cee37f40ff2a8e2a Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 23 Oct 2023 23:38:03 +0300 Subject: [PATCH 219/247] fix: java memory not set on initial setup Signed-off-by: Trial97 --- launcher/ui/setupwizard/JavaWizardPage.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/launcher/ui/setupwizard/JavaWizardPage.cpp b/launcher/ui/setupwizard/JavaWizardPage.cpp index e2c444373..abe4860da 100644 --- a/launcher/ui/setupwizard/JavaWizardPage.cpp +++ b/launcher/ui/setupwizard/JavaWizardPage.cpp @@ -64,8 +64,7 @@ bool JavaWizardPage::validatePage() } case JavaSettingsWidget::ValidationStatus::AllOK: { settings->set("JavaPath", m_java_widget->javaPath()); - return true; - } + } /* fallthrough */ case JavaSettingsWidget::ValidationStatus::JavaBad: { // Memory auto s = APPLICATION->settings(); From 5be80df1ee0e17f4db22daff4ea327cb969bfaef Mon Sep 17 00:00:00 2001 From: LocalSpook <56512186+LocalSpook@users.noreply.github.com> Date: Sun, 22 Oct 2023 21:34:11 -0700 Subject: [PATCH 220/247] Replace `typedef` with `using` Signed-off-by: LocalSpook <56512186+LocalSpook@users.noreply.github.com> --- .clang-tidy | 4 ++++ launcher/BaseInstance.h | 2 +- launcher/BaseVersionList.h | 2 +- launcher/FileSystem.cpp | 21 +++++++++++++------ launcher/MMCZip.h | 2 +- launcher/VersionProxyModel.h | 2 +- launcher/java/JavaChecker.h | 4 ++-- launcher/java/JavaCheckerJob.h | 2 +- launcher/java/JavaInstall.h | 2 +- launcher/minecraft/Agent.h | 2 +- launcher/minecraft/Component.h | 2 +- launcher/minecraft/Library.h | 2 +- launcher/minecraft/MinecraftInstance.h | 2 +- launcher/minecraft/MojangDownloadInfo.h | 6 +++--- launcher/minecraft/ProfileUtils.h | 2 +- launcher/minecraft/auth/AuthSession.h | 2 +- launcher/minecraft/auth/MinecraftAccount.h | 2 +- .../minecraft/launch/MinecraftServerTarget.h | 2 +- launcher/minecraft/services/SkinDelete.h | 2 +- launcher/minecraft/services/SkinUpload.h | 2 +- launcher/modplatform/import_ftb/PackHelpers.h | 2 +- launcher/modplatform/legacy_ftb/PackHelpers.h | 2 +- launcher/net/NetRequest.h | 2 +- launcher/news/NewsEntry.h | 2 +- launcher/pathmatcher/IPathMatcher.h | 2 +- launcher/settings/SettingsObject.h | 4 ++-- launcher/tasks/Task.h | 2 +- launcher/ui/dialogs/ExportInstanceDialog.h | 2 +- .../instanceview/AccessibleInstanceView_p.h | 2 +- launcher/ui/pages/BasePage.h | 2 +- launcher/ui/pages/BasePageProvider.h | 2 +- .../ui/pages/instance/ScreenshotsPage.cpp | 4 ++-- .../modplatform/atlauncher/AtlListModel.h | 4 ++-- .../ui/pages/modplatform/flame/FlameModel.h | 4 ++-- .../pages/modplatform/legacy_ftb/ListModel.h | 4 ++-- .../pages/modplatform/technic/TechnicModel.h | 2 +- libraries/.clang-tidy | 2 ++ 37 files changed, 63 insertions(+), 48 deletions(-) create mode 100644 .clang-tidy create mode 100644 libraries/.clang-tidy diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 000000000..11ddc9cfc --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,4 @@ +Checks: + - modernize-use-using + +SystemHeaders: false diff --git a/launcher/BaseInstance.h b/launcher/BaseInstance.h index 47fff5957..f4ed9113c 100644 --- a/launcher/BaseInstance.h +++ b/launcher/BaseInstance.h @@ -64,7 +64,7 @@ class LaunchTask; class BaseInstance; // pointer for lazy people -typedef std::shared_ptr InstancePtr; +using InstancePtr = std::shared_ptr; /*! * \brief Base class for instances. diff --git a/launcher/BaseVersionList.h b/launcher/BaseVersionList.h index fe1550c21..231887c4e 100644 --- a/launcher/BaseVersionList.h +++ b/launcher/BaseVersionList.h @@ -51,7 +51,7 @@ class BaseVersionList : public QAbstractListModel { ArchitectureRole, SortRole }; - typedef QList RoleList; + using RoleList = QList; explicit BaseVersionList(QObject* parent = 0); diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 652ba2995..c7d5f85fa 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -123,26 +123,35 @@ namespace fs = ghc::filesystem; #if defined(__MINGW32__) -typedef struct _DUPLICATE_EXTENTS_DATA { +struct _DUPLICATE_EXTENTS_DATA { HANDLE FileHandle; LARGE_INTEGER SourceFileOffset; LARGE_INTEGER TargetFileOffset; LARGE_INTEGER ByteCount; -} DUPLICATE_EXTENTS_DATA, *PDUPLICATE_EXTENTS_DATA; +}; -typedef struct _FSCTL_GET_INTEGRITY_INFORMATION_BUFFER { +using DUPLICATE_EXTENTS_DATA = _DUPLICATE_EXTENTS_DATA; +using PDUPLICATE_EXTENTS_DATA = _DUPLICATE_EXTENTS_DATA*; + +struct _FSCTL_GET_INTEGRITY_INFORMATION_BUFFER { WORD ChecksumAlgorithm; // Checksum algorithm. e.g. CHECKSUM_TYPE_UNCHANGED, CHECKSUM_TYPE_NONE, CHECKSUM_TYPE_CRC32 WORD Reserved; // Must be 0 DWORD Flags; // FSCTL_INTEGRITY_FLAG_xxx DWORD ChecksumChunkSizeInBytes; DWORD ClusterSizeInBytes; -} FSCTL_GET_INTEGRITY_INFORMATION_BUFFER, *PFSCTL_GET_INTEGRITY_INFORMATION_BUFFER; +}; + +using FSCTL_GET_INTEGRITY_INFORMATION_BUFFER = _FSCTL_GET_INTEGRITY_INFORMATION_BUFFER; +using PFSCTL_GET_INTEGRITY_INFORMATION_BUFFER = _FSCTL_GET_INTEGRITY_INFORMATION_BUFFER*; -typedef struct _FSCTL_SET_INTEGRITY_INFORMATION_BUFFER { +struct _FSCTL_SET_INTEGRITY_INFORMATION_BUFFER { WORD ChecksumAlgorithm; // Checksum algorithm. e.g. CHECKSUM_TYPE_UNCHANGED, CHECKSUM_TYPE_NONE, CHECKSUM_TYPE_CRC32 WORD Reserved; // Must be 0 DWORD Flags; // FSCTL_INTEGRITY_FLAG_xxx -} FSCTL_SET_INTEGRITY_INFORMATION_BUFFER, *PFSCTL_SET_INTEGRITY_INFORMATION_BUFFER; +}; + +using FSCTL_SET_INTEGRITY_INFORMATION_BUFFER = _FSCTL_SET_INTEGRITY_INFORMATION_BUFFER; +using PFSCTL_SET_INTEGRITY_INFORMATION_BUFFER = _FSCTL_SET_INTEGRITY_INFORMATION_BUFFER*; #endif diff --git a/launcher/MMCZip.h b/launcher/MMCZip.h index 1c6ebe34a..45b1df0b3 100644 --- a/launcher/MMCZip.h +++ b/launcher/MMCZip.h @@ -172,7 +172,7 @@ class ExportToZipTask : public Task { void setExcludeFiles(QStringList excludeFiles) { m_exclude_files = excludeFiles; } void addExtraFile(QString fileName, QByteArray data) { m_extra_files.insert(fileName, data); } - typedef std::optional ZipResult; + using ZipResult = std::optional; protected: virtual void executeTask() override; diff --git a/launcher/VersionProxyModel.h b/launcher/VersionProxyModel.h index c7d5fd94e..57c711d58 100644 --- a/launcher/VersionProxyModel.h +++ b/launcher/VersionProxyModel.h @@ -10,7 +10,7 @@ class VersionProxyModel : public QAbstractProxyModel { Q_OBJECT public: enum Column { Name, ParentVersion, Branch, Type, Architecture, Path, Time }; - typedef QHash> FilterMap; + using FilterMap = QHash>; public: VersionProxyModel(QObject* parent = 0); diff --git a/launcher/java/JavaChecker.h b/launcher/java/JavaChecker.h index 743f49417..7111f8522 100644 --- a/launcher/java/JavaChecker.h +++ b/launcher/java/JavaChecker.h @@ -22,8 +22,8 @@ struct JavaCheckResult { enum class Validity { Errored, ReturnedInvalidData, Valid } validity = Validity::Errored; }; -typedef shared_qobject_ptr QProcessPtr; -typedef shared_qobject_ptr JavaCheckerPtr; +using QProcessPtr = shared_qobject_ptr; +using JavaCheckerPtr = shared_qobject_ptr; class JavaChecker : public QObject { Q_OBJECT public: diff --git a/launcher/java/JavaCheckerJob.h b/launcher/java/JavaCheckerJob.h index 009687917..ddf827968 100644 --- a/launcher/java/JavaCheckerJob.h +++ b/launcher/java/JavaCheckerJob.h @@ -20,7 +20,7 @@ #include "tasks/Task.h" class JavaCheckerJob; -typedef shared_qobject_ptr JavaCheckerJobPtr; +using JavaCheckerJobPtr = shared_qobject_ptr; // FIXME: this just seems horribly redundant class JavaCheckerJob : public Task { diff --git a/launcher/java/JavaInstall.h b/launcher/java/JavaInstall.h index 49e9ed06e..8c2743a00 100644 --- a/launcher/java/JavaInstall.h +++ b/launcher/java/JavaInstall.h @@ -42,4 +42,4 @@ struct JavaInstall : public BaseVersion { bool recommended = false; }; -typedef std::shared_ptr JavaInstallPtr; +using JavaInstallPtr = std::shared_ptr; diff --git a/launcher/minecraft/Agent.h b/launcher/minecraft/Agent.h index 8958521e5..bc385a74e 100644 --- a/launcher/minecraft/Agent.h +++ b/launcher/minecraft/Agent.h @@ -6,7 +6,7 @@ class Agent; -typedef std::shared_ptr AgentPtr; +using AgentPtr = std::shared_ptr; class Agent { public: diff --git a/launcher/minecraft/Component.h b/launcher/minecraft/Component.h index 3474a22e0..fdb61c45e 100644 --- a/launcher/minecraft/Component.h +++ b/launcher/minecraft/Component.h @@ -105,4 +105,4 @@ class Component : public QObject, public ProblemProvider { bool m_loaded = false; }; -typedef shared_qobject_ptr ComponentPtr; +using ComponentPtr = shared_qobject_ptr; diff --git a/launcher/minecraft/Library.h b/launcher/minecraft/Library.h index f8816a97b..adb89c4c6 100644 --- a/launcher/minecraft/Library.h +++ b/launcher/minecraft/Library.h @@ -52,7 +52,7 @@ class Library; class MinecraftInstance; -typedef std::shared_ptr LibraryPtr; +using LibraryPtr = std::shared_ptr; class Library { friend class OneSixVersionFormat; diff --git a/launcher/minecraft/MinecraftInstance.h b/launcher/minecraft/MinecraftInstance.h index a816a3e60..b1f305201 100644 --- a/launcher/minecraft/MinecraftInstance.h +++ b/launcher/minecraft/MinecraftInstance.h @@ -174,4 +174,4 @@ class MinecraftInstance : public BaseInstance { mutable std::shared_ptr m_game_options; }; -typedef std::shared_ptr MinecraftInstancePtr; +using MinecraftInstancePtr = std::shared_ptr; diff --git a/launcher/minecraft/MojangDownloadInfo.h b/launcher/minecraft/MojangDownloadInfo.h index 855dbe005..eb64f95b7 100644 --- a/launcher/minecraft/MojangDownloadInfo.h +++ b/launcher/minecraft/MojangDownloadInfo.h @@ -5,7 +5,7 @@ struct MojangDownloadInfo { // types - typedef std::shared_ptr Ptr; + using Ptr = std::shared_ptr; // data /// Local filesystem path. WARNING: not used, only here so we can pass through mojang files unmolested! @@ -23,7 +23,7 @@ struct MojangLibraryDownloadInfo { MojangLibraryDownloadInfo() {} // types - typedef std::shared_ptr Ptr; + using Ptr = std::shared_ptr; // methods MojangDownloadInfo* getDownloadInfo(QString classifier) @@ -42,7 +42,7 @@ struct MojangLibraryDownloadInfo { struct MojangAssetIndexInfo : public MojangDownloadInfo { // types - typedef std::shared_ptr Ptr; + using Ptr = std::shared_ptr; // methods MojangAssetIndexInfo() {} diff --git a/launcher/minecraft/ProfileUtils.h b/launcher/minecraft/ProfileUtils.h index 98a7ff739..edabe0bf0 100644 --- a/launcher/minecraft/ProfileUtils.h +++ b/launcher/minecraft/ProfileUtils.h @@ -38,7 +38,7 @@ #include "VersionFile.h" namespace ProfileUtils { -typedef QStringList PatchOrder; +using PatchOrder = QStringList; /// Read and parse a OneSix format order file bool readOverrideOrders(QString path, PatchOrder& order); diff --git a/launcher/minecraft/auth/AuthSession.h b/launcher/minecraft/auth/AuthSession.h index 074b2d6e3..cec238033 100644 --- a/launcher/minecraft/auth/AuthSession.h +++ b/launcher/minecraft/auth/AuthSession.h @@ -43,4 +43,4 @@ struct AuthSession { bool demo = false; }; -typedef std::shared_ptr AuthSessionPtr; +using AuthSessionPtr = std::shared_ptr; diff --git a/launcher/minecraft/auth/MinecraftAccount.h b/launcher/minecraft/auth/MinecraftAccount.h index 910f4a28a..c4e9756a9 100644 --- a/launcher/minecraft/auth/MinecraftAccount.h +++ b/launcher/minecraft/auth/MinecraftAccount.h @@ -54,7 +54,7 @@ class Task; class AccountTask; class MinecraftAccount; -typedef shared_qobject_ptr MinecraftAccountPtr; +using MinecraftAccountPtr = shared_qobject_ptr; Q_DECLARE_METATYPE(MinecraftAccountPtr) /** diff --git a/launcher/minecraft/launch/MinecraftServerTarget.h b/launcher/minecraft/launch/MinecraftServerTarget.h index af8d6550b..2edd8a30d 100644 --- a/launcher/minecraft/launch/MinecraftServerTarget.h +++ b/launcher/minecraft/launch/MinecraftServerTarget.h @@ -26,4 +26,4 @@ struct MinecraftServerTarget { static MinecraftServerTarget parse(const QString& fullAddress); }; -typedef std::shared_ptr MinecraftServerTargetPtr; +using MinecraftServerTargetPtr = std::shared_ptr; diff --git a/launcher/minecraft/services/SkinDelete.h b/launcher/minecraft/services/SkinDelete.h index d5b2e63db..44e30453f 100644 --- a/launcher/minecraft/services/SkinDelete.h +++ b/launcher/minecraft/services/SkinDelete.h @@ -4,7 +4,7 @@ #include #include "tasks/Task.h" -typedef shared_qobject_ptr SkinDeletePtr; +using SkinDeletePtr = shared_qobject_ptr; class SkinDelete : public Task { Q_OBJECT diff --git a/launcher/minecraft/services/SkinUpload.h b/launcher/minecraft/services/SkinUpload.h index 5716aa996..016367ff8 100644 --- a/launcher/minecraft/services/SkinUpload.h +++ b/launcher/minecraft/services/SkinUpload.h @@ -5,7 +5,7 @@ #include #include "tasks/Task.h" -typedef shared_qobject_ptr SkinUploadPtr; +using SkinUploadPtr = shared_qobject_ptr; class SkinUpload : public Task { Q_OBJECT diff --git a/launcher/modplatform/import_ftb/PackHelpers.h b/launcher/modplatform/import_ftb/PackHelpers.h index 5400252b6..221eb5bf6 100644 --- a/launcher/modplatform/import_ftb/PackHelpers.h +++ b/launcher/modplatform/import_ftb/PackHelpers.h @@ -45,7 +45,7 @@ struct Modpack { QIcon icon; }; -typedef QList ModpackList; +using ModpackList = QList; Modpack parseDirectory(QString path); diff --git a/launcher/modplatform/legacy_ftb/PackHelpers.h b/launcher/modplatform/legacy_ftb/PackHelpers.h index 4fb535530..f2d18f802 100644 --- a/launcher/modplatform/legacy_ftb/PackHelpers.h +++ b/launcher/modplatform/legacy_ftb/PackHelpers.h @@ -31,7 +31,7 @@ struct Modpack { QString packCode; }; -typedef QList ModpackList; +using ModpackList = QList; } // namespace LegacyFTB diff --git a/launcher/net/NetRequest.h b/launcher/net/NetRequest.h index ee47ab2a6..0b307b4f6 100644 --- a/launcher/net/NetRequest.h +++ b/launcher/net/NetRequest.h @@ -87,7 +87,7 @@ class NetRequest : public NetAction { std::unique_ptr m_sink; Options m_options; - typedef const QLoggingCategory& (*logCatFunc)(); + using logCatFunc = const QLoggingCategory& (*)(); logCatFunc logCat = taskUploadLogC; std::chrono::steady_clock m_clock; diff --git a/launcher/news/NewsEntry.h b/launcher/news/NewsEntry.h index 2d409a9fb..ab717ec89 100644 --- a/launcher/news/NewsEntry.h +++ b/launcher/news/NewsEntry.h @@ -51,4 +51,4 @@ class NewsEntry : public QObject { QString link; }; -typedef std::shared_ptr NewsEntryPtr; +using NewsEntryPtr = std::shared_ptr; diff --git a/launcher/pathmatcher/IPathMatcher.h b/launcher/pathmatcher/IPathMatcher.h index cd6121979..f3b01e8cf 100644 --- a/launcher/pathmatcher/IPathMatcher.h +++ b/launcher/pathmatcher/IPathMatcher.h @@ -4,7 +4,7 @@ class IPathMatcher { public: - typedef std::shared_ptr Ptr; + using Ptr = std::shared_ptr; public: virtual ~IPathMatcher() {} diff --git a/launcher/settings/SettingsObject.h b/launcher/settings/SettingsObject.h index 75631f247..f133f2f7f 100644 --- a/launcher/settings/SettingsObject.h +++ b/launcher/settings/SettingsObject.h @@ -26,8 +26,8 @@ class Setting; class SettingsObject; -typedef std::shared_ptr SettingsObjectPtr; -typedef std::weak_ptr SettingsObjectWeakPtr; +using SettingsObjectPtr = std::shared_ptr; +using SettingsObjectWeakPtr = std::weak_ptr; /*! * \brief The SettingsObject handles communicating settings between the application and a diff --git a/launcher/tasks/Task.h b/launcher/tasks/Task.h index 94b4089f3..883408c97 100644 --- a/launcher/tasks/Task.h +++ b/launcher/tasks/Task.h @@ -77,7 +77,7 @@ struct TaskStepProgress { Q_DECLARE_METATYPE(TaskStepProgress) -typedef QList> TaskStepProgressList; +using TaskStepProgressList = QList>; class Task : public QObject, public QRunnable { Q_OBJECT diff --git a/launcher/ui/dialogs/ExportInstanceDialog.h b/launcher/ui/dialogs/ExportInstanceDialog.h index 02f38f63d..183681f57 100644 --- a/launcher/ui/dialogs/ExportInstanceDialog.h +++ b/launcher/ui/dialogs/ExportInstanceDialog.h @@ -43,7 +43,7 @@ #include "FileIgnoreProxy.h" class BaseInstance; -typedef std::shared_ptr InstancePtr; +using InstancePtr = std::shared_ptr; namespace Ui { class ExportInstanceDialog; diff --git a/launcher/ui/instanceview/AccessibleInstanceView_p.h b/launcher/ui/instanceview/AccessibleInstanceView_p.h index e99f85069..1a3a62d9f 100644 --- a/launcher/ui/instanceview/AccessibleInstanceView_p.h +++ b/launcher/ui/instanceview/AccessibleInstanceView_p.h @@ -59,7 +59,7 @@ class AccessibleInstanceView : public QAccessibleTableInterface, public QAccessi protected: // maybe vector - typedef QHash ChildCache; + using ChildCache = QHash; mutable ChildCache childToId; virtual ~AccessibleInstanceView(); diff --git a/launcher/ui/pages/BasePage.h b/launcher/ui/pages/BasePage.h index d35206a08..cb3a7633d 100644 --- a/launcher/ui/pages/BasePage.h +++ b/launcher/ui/pages/BasePage.h @@ -77,4 +77,4 @@ class BasePage { bool isOpened = false; }; -typedef std::shared_ptr BasePagePtr; +using BasePagePtr = std::shared_ptr; diff --git a/launcher/ui/pages/BasePageProvider.h b/launcher/ui/pages/BasePageProvider.h index 4c3ecd6c1..422891e6b 100644 --- a/launcher/ui/pages/BasePageProvider.h +++ b/launcher/ui/pages/BasePageProvider.h @@ -26,7 +26,7 @@ class BasePageProvider { }; class GenericPageProvider : public BasePageProvider { - typedef std::function PageCreator; + using PageCreator = std::function; public: explicit GenericPageProvider(const QString& dialogTitle) : m_dialogTitle(dialogTitle) {} diff --git a/launcher/ui/pages/instance/ScreenshotsPage.cpp b/launcher/ui/pages/instance/ScreenshotsPage.cpp index 25f978cea..83fb0d5ff 100644 --- a/launcher/ui/pages/instance/ScreenshotsPage.cpp +++ b/launcher/ui/pages/instance/ScreenshotsPage.cpp @@ -68,8 +68,8 @@ #include #include "RWStorage.h" -typedef RWStorage SharedIconCache; -typedef std::shared_ptr SharedIconCachePtr; +using SharedIconCache = RWStorage; +using SharedIconCachePtr = std::shared_ptr; class ThumbnailingResult : public QObject { Q_OBJECT diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlListModel.h b/launcher/ui/pages/modplatform/atlauncher/AtlListModel.h index ed1fdc9f9..bcadd7c91 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlListModel.h +++ b/launcher/ui/pages/modplatform/atlauncher/AtlListModel.h @@ -24,8 +24,8 @@ namespace Atl { -typedef QMap LogoMap; -typedef std::function LogoCallback; +using LogoMap = QMap; +using LogoCallback = std::function; class ListModel : public QAbstractListModel { Q_OBJECT diff --git a/launcher/ui/pages/modplatform/flame/FlameModel.h b/launcher/ui/pages/modplatform/flame/FlameModel.h index fd8496dfb..5a07ef6bb 100644 --- a/launcher/ui/pages/modplatform/flame/FlameModel.h +++ b/launcher/ui/pages/modplatform/flame/FlameModel.h @@ -19,8 +19,8 @@ namespace Flame { -typedef QMap LogoMap; -typedef std::function LogoCallback; +using LogoMap = QMap; +using LogoCallback = std::function; class ListModel : public QAbstractListModel { Q_OBJECT diff --git a/launcher/ui/pages/modplatform/legacy_ftb/ListModel.h b/launcher/ui/pages/modplatform/legacy_ftb/ListModel.h index c802a4b56..f35012078 100644 --- a/launcher/ui/pages/modplatform/legacy_ftb/ListModel.h +++ b/launcher/ui/pages/modplatform/legacy_ftb/ListModel.h @@ -13,8 +13,8 @@ namespace LegacyFTB { -typedef QMap FTBLogoMap; -typedef std::function LogoCallback; +using FTBLogoMap = QMap; +using LogoCallback = std::function; class FilterModel : public QSortFilterProxyModel { Q_OBJECT diff --git a/launcher/ui/pages/modplatform/technic/TechnicModel.h b/launcher/ui/pages/modplatform/technic/TechnicModel.h index aeb4f3084..09e9294bb 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicModel.h +++ b/launcher/ui/pages/modplatform/technic/TechnicModel.h @@ -42,7 +42,7 @@ namespace Technic { -typedef std::function LogoCallback; +using LogoCallback = std::function; class ListModel : public QAbstractListModel { Q_OBJECT diff --git a/libraries/.clang-tidy b/libraries/.clang-tidy new file mode 100644 index 000000000..358b093b9 --- /dev/null +++ b/libraries/.clang-tidy @@ -0,0 +1,2 @@ +# We don't care about linting third-party code. +Checks: -* From 12d567a9b83142b102598a944c650849d4a0f94e Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 26 Oct 2023 22:51:38 +0300 Subject: [PATCH 221/247] made env vars behave like the rest of the settings Signed-off-by: Trial97 --- launcher/minecraft/MinecraftInstance.cpp | 10 +++--- .../pages/global/EnvironmentVariablesPage.cpp | 2 +- .../pages/instance/InstanceSettingsPage.cpp | 12 ++++--- launcher/ui/widgets/EnvironmentVariables.cpp | 36 +++---------------- launcher/ui/widgets/EnvironmentVariables.h | 3 +- launcher/ui/widgets/EnvironmentVariables.ui | 7 ---- 6 files changed, 17 insertions(+), 53 deletions(-) diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index cc33c05af..b371a3751 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -188,9 +188,8 @@ void MinecraftInstance::loadSpecificSettings() auto legacySettings = m_settings->registerSetting("OverrideLegacySettings", false); m_settings->registerOverride(global_settings->getSetting("OnlineFixes"), legacySettings); - m_settings->registerSetting("UseEnv", false); - m_settings->registerSetting("OverrideEnv", false); - m_settings->registerSetting("Env", QVariant(QMap())); + auto envSetting = m_settings->registerSetting("OverrideEnv", false); + m_settings->registerOverride(global_settings->getSetting("Env"), envSetting); m_settings->set("InstanceType", "OneSix"); } @@ -612,12 +611,11 @@ QProcessEnvironment MinecraftInstance::createLaunchEnvironment() env.insert(iter.key(), iter.value().toString()); }; - bool useEnv = settings()->get("UseEnv").toBool(); - bool overrideEnv = useEnv && settings()->get("OverrideEnv").toBool(); + bool overrideEnv = settings()->get("OverrideEnv").toBool(); if (!overrideEnv) insertEnv(APPLICATION->settings()->get("Env").toMap()); - if (useEnv) + else insertEnv(settings()->get("Env").toMap()); return env; diff --git a/launcher/ui/pages/global/EnvironmentVariablesPage.cpp b/launcher/ui/pages/global/EnvironmentVariablesPage.cpp index 2c3b716b8..2b76df362 100644 --- a/launcher/ui/pages/global/EnvironmentVariablesPage.cpp +++ b/launcher/ui/pages/global/EnvironmentVariablesPage.cpp @@ -29,7 +29,7 @@ EnvironmentVariablesPage::EnvironmentVariablesPage(QWidget* parent) : QWidget(pa variables->setContentsMargins(6, 6, 6, 6); verticalLayout->addWidget(variables); - variables->initialize(false, true, false, APPLICATION->settings()->get("Env").toMap()); + variables->initialize(false, false, APPLICATION->settings()->get("Env").toMap()); } QString EnvironmentVariablesPage::displayName() const diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index 9f01cab42..74785f97e 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -203,9 +203,12 @@ void InstanceSettingsPage::applySettings() } // Environment Variables - m_settings->set("UseEnv", ui->environmentVariables->checked()); - m_settings->set("OverrideEnv", ui->environmentVariables->override()); - m_settings->set("Env", ui->environmentVariables->value()); + auto env = ui->environmentVariables->override(); + m_settings->set("OverrideEnv", env); + if (env) + m_settings->set("Env", ui->environmentVariables->value()); + else + m_settings->reset("Env"); // Workarounds bool workarounds = ui->nativeWorkaroundsGroupBox->isChecked(); @@ -327,8 +330,7 @@ void InstanceSettingsPage::loadSettings() m_settings->get("WrapperCommand").toString(), m_settings->get("PostExitCommand").toString()); // Environment variables - ui->environmentVariables->initialize(true, m_settings->get("UseEnv").toBool(), m_settings->get("OverrideEnv").toBool(), - m_settings->get("Env").toMap()); + ui->environmentVariables->initialize(true, m_settings->get("OverrideEnv").toBool(), m_settings->get("Env").toMap()); // Workarounds ui->nativeWorkaroundsGroupBox->setChecked(m_settings->get("OverrideNativeWorkarounds").toBool()); diff --git a/launcher/ui/widgets/EnvironmentVariables.cpp b/launcher/ui/widgets/EnvironmentVariables.cpp index a409f64fc..633fc6122 100644 --- a/launcher/ui/widgets/EnvironmentVariables.cpp +++ b/launcher/ui/widgets/EnvironmentVariables.cpp @@ -50,25 +50,6 @@ EnvironmentVariables::EnvironmentVariables(QWidget* parent) : QWidget(parent), u }); connect(ui->clear, &QPushButton::clicked, this, [this] { ui->list->clear(); }); - - connect(ui->globalOverride, &QCheckBox::clicked, this, [this](bool state) { - if (!state) - return; - - auto global = APPLICATION->settings()->get("Env").toMap(); - if (global.isEmpty()) - return; - - auto response = CustomMessageBox::selectable( - this, tr("Reset"), - tr("You have chosen to ignore global settings.\n\nWould you like to clear the current variables and copy " - "the global variables over?"), - QMessageBox::Question, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) - ->exec(); - - if (response == QMessageBox::Yes) - initialize(true, checked(), override(), global); - }); } EnvironmentVariables::~EnvironmentVariables() @@ -76,13 +57,11 @@ EnvironmentVariables::~EnvironmentVariables() delete ui; } -void EnvironmentVariables::initialize(bool instance, bool checked, bool override, const QMap& value) +void EnvironmentVariables::initialize(bool instance, bool override, const QMap& value) { // update widgets to settings ui->groupBox->setCheckable(instance); - ui->groupBox->setChecked(checked); - ui->globalOverride->setVisible(instance); - ui->globalOverride->setChecked(override); + ui->groupBox->setChecked(override); // populate ui->list->clear(); @@ -113,18 +92,11 @@ void EnvironmentVariables::retranslate() ui->retranslateUi(this); } -bool EnvironmentVariables::checked() const -{ - if (!ui->groupBox->isCheckable()) - return true; - return ui->groupBox->isChecked(); -} - bool EnvironmentVariables::override() const { - if (!ui->globalOverride->isVisible()) + if (!ui->groupBox->isCheckable()) return false; - return ui->globalOverride->isChecked(); + return ui->groupBox->isChecked(); } QMap EnvironmentVariables::value() const diff --git a/launcher/ui/widgets/EnvironmentVariables.h b/launcher/ui/widgets/EnvironmentVariables.h index ba4329dd9..092d586bd 100644 --- a/launcher/ui/widgets/EnvironmentVariables.h +++ b/launcher/ui/widgets/EnvironmentVariables.h @@ -31,11 +31,10 @@ class EnvironmentVariables : public QWidget { public: explicit EnvironmentVariables(QWidget* state = nullptr); ~EnvironmentVariables() override; - void initialize(bool instance, bool checked, bool override, const QMap& value); + void initialize(bool instance, bool override, const QMap& value); bool eventFilter(QObject* watched, QEvent* event) override; void retranslate(); - bool checked() const; bool override() const; QMap value() const; diff --git a/launcher/ui/widgets/EnvironmentVariables.ui b/launcher/ui/widgets/EnvironmentVariables.ui index 895b40d01..ded5b2ded 100644 --- a/launcher/ui/widgets/EnvironmentVariables.ui +++ b/launcher/ui/widgets/EnvironmentVariables.ui @@ -35,13 +35,6 @@ true - - - - &Ignore global settings - - - From bd11b93a0cb917e67dceac2f8eb03562f0f7bdb6 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Thu, 26 Oct 2023 21:01:24 +0100 Subject: [PATCH 222/247] Use hidden tab bar like other pages Signed-off-by: TheKodeToad --- launcher/ui/pages/global/EnvironmentVariablesPage.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/launcher/ui/pages/global/EnvironmentVariablesPage.cpp b/launcher/ui/pages/global/EnvironmentVariablesPage.cpp index 2b76df362..2d44ed624 100644 --- a/launcher/ui/pages/global/EnvironmentVariablesPage.cpp +++ b/launcher/ui/pages/global/EnvironmentVariablesPage.cpp @@ -17,6 +17,7 @@ */ #include +#include #include #include "EnvironmentVariablesPage.h" @@ -24,10 +25,16 @@ EnvironmentVariablesPage::EnvironmentVariablesPage(QWidget* parent) : QWidget(parent) { auto verticalLayout = new QVBoxLayout(this); + verticalLayout->setObjectName(QStringLiteral("verticalLayout")); verticalLayout->setContentsMargins(0, 0, 0, 0); + + auto tabWidget = new QTabWidget(this); + tabWidget->setObjectName(QStringLiteral("tabWidget")); variables = new EnvironmentVariables(this); variables->setContentsMargins(6, 6, 6, 6); - verticalLayout->addWidget(variables); + tabWidget->addTab(variables, "Foo"); + tabWidget->tabBar()->hide(); + verticalLayout->addWidget(tabWidget); variables->initialize(false, false, APPLICATION->settings()->get("Env").toMap()); } From 364cb4ff6afccb2d9696a5a2363df0623beee0da Mon Sep 17 00:00:00 2001 From: IThundxr Date: Sat, 28 Oct 2023 07:39:32 -0400 Subject: [PATCH 223/247] chore: remove windows 7/8 support With 8.0 windows legacy support was dropped, this just removes it from the manifest Signed-off-by: IThundxr --- program_info/prismlauncher.manifest.in | 6 ------ 1 file changed, 6 deletions(-) diff --git a/program_info/prismlauncher.manifest.in b/program_info/prismlauncher.manifest.in index 6f4467c7d..fb28afc17 100644 --- a/program_info/prismlauncher.manifest.in +++ b/program_info/prismlauncher.manifest.in @@ -16,12 +16,6 @@ Custom Minecraft launcher for managing multiple installs. - - - - - - From 9dcb7e975921374274740e67632ca44dc0fe09da Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 28 Oct 2023 16:03:11 +0000 Subject: [PATCH 224/247] chore(deps): update korthout/backport-action action to v2 --- .github/workflows/backport.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index c705ff7b0..f9cf4fb7d 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -24,7 +24,7 @@ jobs: with: ref: ${{ github.event.pull_request.head.sha }} - name: Create backport PRs - uses: korthout/backport-action@v1.4.0 + uses: korthout/backport-action@v2.0.0 with: # Config README: https://github.com/korthout/backport-action#backport-action pull_description: |- From 54305397e45ec9f2c14b84eb782b541633b9dc3d Mon Sep 17 00:00:00 2001 From: LocalSpook <56512186+LocalSpook@users.noreply.github.com> Date: Sat, 28 Oct 2023 12:50:40 -0700 Subject: [PATCH 225/247] Localize sorting options for resource and shader packs Signed-off-by: LocalSpook <56512186+LocalSpook@users.noreply.github.com> --- launcher/modplatform/flame/FlameAPI.cpp | 20 +++++++++---------- launcher/modplatform/modrinth/ModrinthAPI.cpp | 14 ++++++------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/launcher/modplatform/flame/FlameAPI.cpp b/launcher/modplatform/flame/FlameAPI.cpp index e99ce3a56..bb4f18983 100644 --- a/launcher/modplatform/flame/FlameAPI.cpp +++ b/launcher/modplatform/flame/FlameAPI.cpp @@ -208,17 +208,15 @@ Task::Ptr FlameAPI::getFile(const QString& addonId, const QString& fileId, std:: return netJob; } -// https://docs.curseforge.com/?python#tocS_ModsSearchSortField -static QList s_sorts = { { 1, "Featured", QObject::tr("Sort by Featured") }, - { 2, "Popularity", QObject::tr("Sort by Popularity") }, - { 3, "LastUpdated", QObject::tr("Sort by Last Updated") }, - { 4, "Name", QObject::tr("Sort by Name") }, - { 5, "Author", QObject::tr("Sort by Author") }, - { 6, "TotalDownloads", QObject::tr("Sort by Downloads") }, - { 7, "Category", QObject::tr("Sort by Category") }, - { 8, "GameVersion", QObject::tr("Sort by Game Version") } }; - QList FlameAPI::getSortingMethods() const { - return s_sorts; + // https://docs.curseforge.com/?python#tocS_ModsSearchSortField + return { { 1, "Featured", QObject::tr("Sort by Featured") }, + { 2, "Popularity", QObject::tr("Sort by Popularity") }, + { 3, "LastUpdated", QObject::tr("Sort by Last Updated") }, + { 4, "Name", QObject::tr("Sort by Name") }, + { 5, "Author", QObject::tr("Sort by Author") }, + { 6, "TotalDownloads", QObject::tr("Sort by Downloads") }, + { 7, "Category", QObject::tr("Sort by Category") }, + { 8, "GameVersion", QObject::tr("Sort by Game Version") } }; } diff --git a/launcher/modplatform/modrinth/ModrinthAPI.cpp b/launcher/modplatform/modrinth/ModrinthAPI.cpp index f453f5cb9..8be963ecf 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.cpp +++ b/launcher/modplatform/modrinth/ModrinthAPI.cpp @@ -111,14 +111,12 @@ Task::Ptr ModrinthAPI::getProjects(QStringList addonIds, std::shared_ptr s_sorts = { { 1, "relevance", QObject::tr("Sort by Relevance") }, - { 2, "downloads", QObject::tr("Sort by Downloads") }, - { 3, "follows", QObject::tr("Sort by Follows") }, - { 4, "newest", QObject::tr("Sort by Last Updated") }, - { 5, "updated", QObject::tr("Sort by Newest") } }; - QList ModrinthAPI::getSortingMethods() const { - return s_sorts; + // https://docs.modrinth.com/api-spec/#tag/projects/operation/searchProjects + return { { 1, "relevance", QObject::tr("Sort by Relevance") }, + { 2, "downloads", QObject::tr("Sort by Downloads") }, + { 3, "follows", QObject::tr("Sort by Follows") }, + { 4, "newest", QObject::tr("Sort by Last Updated") }, + { 5, "updated", QObject::tr("Sort by Newest") } }; } From 55ac17c45b40f678f831b189a6d25a1f5344f73e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 29 Oct 2023 00:18:11 +0000 Subject: [PATCH 226/247] 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/44881e03af1c730cbb1d72a4d41274a2c957813a' (2023-10-21) → 'github:nixos/nixpkgs/808c0d8c53c7ae50f82aca8e7df263225cf235bf' (2023-10-26) • Updated input 'pre-commit-hooks': 'github:cachix/pre-commit-hooks.nix/8cc349bfd082da8782b989cad2158c9ad5bd70fd' (2023-10-19) → 'github:cachix/pre-commit-hooks.nix/bd38df3d508dfcdff52cd243d297f218ed2257bf' (2023-10-25) --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index d71403358..7cf901f77 100644 --- a/flake.lock +++ b/flake.lock @@ -106,11 +106,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1697886341, - "narHash": "sha256-AdE67xPty9M9wn36nPVp6aDntIdigrs7UbyaGv1VAaM=", + "lastModified": 1698336494, + "narHash": "sha256-sO72WDBKyijYD1GcKPlGsycKbMBiTJMBCnmOxLAs880=", "owner": "nixos", "repo": "nixpkgs", - "rev": "44881e03af1c730cbb1d72a4d41274a2c957813a", + "rev": "808c0d8c53c7ae50f82aca8e7df263225cf235bf", "type": "github" }, "original": { @@ -153,11 +153,11 @@ ] }, "locked": { - "lastModified": 1697746376, - "narHash": "sha256-gu77VkgdfaHgNCVufeb6WP9oqFLjwK4jHcoPZmBVF3E=", + "lastModified": 1698227354, + "narHash": "sha256-Fi5H9jbaQLmLw9qBi/mkR33CoFjNbobo5xWdX4tKz1Q=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "8cc349bfd082da8782b989cad2158c9ad5bd70fd", + "rev": "bd38df3d508dfcdff52cd243d297f218ed2257bf", "type": "github" }, "original": { From 5a54d80c6ee2ec05bcca0044d3936d5fa0e8d13d Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Mon, 30 Oct 2023 09:26:38 +0000 Subject: [PATCH 227/247] Fix group issues Signed-off-by: TheKodeToad --- launcher/InstanceList.cpp | 11 ++++++----- launcher/InstanceList.h | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/launcher/InstanceList.cpp b/launcher/InstanceList.cpp index 756ff93dd..111f8d8a4 100644 --- a/launcher/InstanceList.cpp +++ b/launcher/InstanceList.cpp @@ -290,8 +290,7 @@ void InstanceList::deleteGroup(const GroupId& name) qDebug() << "Remove" << instID << "from group" << name; removed = true; auto idx = getInstIndex(instance.get()); - if (idx > 0) - emit dataChanged(index(idx), index(idx), { GroupRole }); + emit dataChanged(index(idx), index(idx), { GroupRole }); } } if (removed) @@ -315,8 +314,7 @@ void InstanceList::renameGroup(const QString& src, const QString& dst) qDebug() << "Set" << instID << "group to" << dst; modified = true; auto idx = getInstIndex(instance.get()); - if (idx > 0) - emit dataChanged(index(idx), index(idx), { GroupRole }); + emit dataChanged(index(idx), index(idx), { GroupRole }); } } if (modified) @@ -947,9 +945,12 @@ QString InstanceList::getStagedInstancePath() bool InstanceList::commitStagedInstance(const QString& path, InstanceName const& instanceName, - const QString& groupName, + QString groupName, InstanceTask const& commiting) { + if (groupName.isEmpty() && !groupName.isNull()) + groupName = QString(); + QDir dir; QString instID; InstancePtr inst; diff --git a/launcher/InstanceList.h b/launcher/InstanceList.h index 6b0bcd810..5ddddee95 100644 --- a/launcher/InstanceList.h +++ b/launcher/InstanceList.h @@ -130,7 +130,7 @@ class InstanceList : public QAbstractListModel { * should_override is used when another similar instance already exists, and we want to override it * - for instance, when updating it. */ - bool commitStagedInstance(const QString& keyPath, const InstanceName& instanceName, const QString& groupName, const InstanceTask&); + bool commitStagedInstance(const QString& keyPath, const InstanceName& instanceName, QString groupName, const InstanceTask&); /** * Destroy a previously created staging area given by @keyPath - used when creation fails. From 2526275c5e124996b7a6b0465bad8b2bc9a1dffe Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Mon, 30 Oct 2023 09:51:37 +0000 Subject: [PATCH 228/247] Better check Signed-off-by: TheKodeToad --- launcher/InstanceList.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/launcher/InstanceList.cpp b/launcher/InstanceList.cpp index 111f8d8a4..2b8f34293 100644 --- a/launcher/InstanceList.cpp +++ b/launcher/InstanceList.cpp @@ -290,7 +290,8 @@ void InstanceList::deleteGroup(const GroupId& name) qDebug() << "Remove" << instID << "from group" << name; removed = true; auto idx = getInstIndex(instance.get()); - emit dataChanged(index(idx), index(idx), { GroupRole }); + if (idx >= 0) + emit dataChanged(index(idx), index(idx), { GroupRole }); } } if (removed) @@ -314,7 +315,8 @@ void InstanceList::renameGroup(const QString& src, const QString& dst) qDebug() << "Set" << instID << "group to" << dst; modified = true; auto idx = getInstIndex(instance.get()); - emit dataChanged(index(idx), index(idx), { GroupRole }); + if (idx >= 0) + emit dataChanged(index(idx), index(idx), { GroupRole }); } } if (modified) From 2349f29be01e1f6777f60a0397eab69650454c0d Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 2 Nov 2023 01:19:18 +0200 Subject: [PATCH 229/247] jsut a overflow protection Signed-off-by: Trial97 --- launcher/MTPixmapCache.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/launcher/MTPixmapCache.h b/launcher/MTPixmapCache.h index 1a3e52160..a14882097 100644 --- a/launcher/MTPixmapCache.h +++ b/launcher/MTPixmapCache.h @@ -113,7 +113,13 @@ class PixmapCache final : public QObject { if (m_consecutive_fast_evicitons >= m_consecutive_fast_evicitons_threshold) { // double the cache size auto newSize = _cacheLimit() * 2; - qDebug() << m_consecutive_fast_evicitons << "pixmap cache misses by eviction happened too fast, doubling cache size to" + if (newSize <= 0) { // double it until you overflow :D + qDebug() << m_consecutive_fast_evicitons + << tr("pixmap cache misses by eviction happened too fast, doing nothing as the cache size reached it's limit"); + m_consecutive_fast_evicitons = 0; + return true; + } + qDebug() << m_consecutive_fast_evicitons << tr("pixmap cache misses by eviction happened too fast, doubling cache size to") << newSize; _setCacheLimit(newSize); m_consecutive_fast_evicitons = 0; From 220a1de99a564602b9d043732b83b5d7c5545c25 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 2 Nov 2023 22:41:56 +0200 Subject: [PATCH 230/247] made sure that we do not relay for undefined behavior Signed-off-by: Trial97 --- launcher/MTPixmapCache.h | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/launcher/MTPixmapCache.h b/launcher/MTPixmapCache.h index a14882097..5f8212d74 100644 --- a/launcher/MTPixmapCache.h +++ b/launcher/MTPixmapCache.h @@ -5,6 +5,7 @@ #include #include #include +#include #define GET_TYPE() \ Qt::ConnectionType type; \ @@ -112,16 +113,16 @@ class PixmapCache final : public QObject { m_last_cache_miss_by_eviciton = now; if (m_consecutive_fast_evicitons >= m_consecutive_fast_evicitons_threshold) { // double the cache size - auto newSize = _cacheLimit() * 2; - if (newSize <= 0) { // double it until you overflow :D + auto newSize = _cacheLimit() * 2.L; + if (newSize >= std::numeric_limits::max()) { // double it until you overflow :D + newSize = std::numeric_limits::max(); qDebug() << m_consecutive_fast_evicitons << tr("pixmap cache misses by eviction happened too fast, doing nothing as the cache size reached it's limit"); - m_consecutive_fast_evicitons = 0; - return true; + } else { + qDebug() << m_consecutive_fast_evicitons << tr("pixmap cache misses by eviction happened too fast, doubling cache size to") + << static_cast(newSize); } - qDebug() << m_consecutive_fast_evicitons << tr("pixmap cache misses by eviction happened too fast, doubling cache size to") - << newSize; - _setCacheLimit(newSize); + _setCacheLimit(static_cast(newSize)); m_consecutive_fast_evicitons = 0; return true; } From 127a31ba3a468f8ad9fcedc29befea78ad381657 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 3 Nov 2023 10:31:40 +0200 Subject: [PATCH 231/247] Added warning for remove account Signed-off-by: Trial97 --- launcher/ui/pages/global/AccountListPage.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/launcher/ui/pages/global/AccountListPage.cpp b/launcher/ui/pages/global/AccountListPage.cpp index 50f3ff2b6..6fd6112d4 100644 --- a/launcher/ui/pages/global/AccountListPage.cpp +++ b/launcher/ui/pages/global/AccountListPage.cpp @@ -42,22 +42,17 @@ #include -#include "net/NetJob.h" - #include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/MSALoginDialog.h" #include "ui/dialogs/OfflineLoginDialog.h" #include "ui/dialogs/ProgressDialog.h" #include "ui/dialogs/SkinUploadDialog.h" -#include "minecraft/auth/AccountTask.h" #include "minecraft/services/SkinDelete.h" #include "tasks/Task.h" #include "Application.h" -#include "BuildConfig.h" - AccountListPage::AccountListPage(QWidget* parent) : QMainWindow(parent), ui(new Ui::AccountListPage) { ui->setupUi(this); @@ -172,6 +167,12 @@ void AccountListPage::on_actionAddOffline_triggered() void AccountListPage::on_actionRemove_triggered() { + auto response = CustomMessageBox::selectable(this, tr("Remove account?"), tr("Do you realy want to delete this account?"), + QMessageBox::Question, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + if (response != QMessageBox::Yes) { + return; + } QModelIndexList selection = ui->listView->selectionModel()->selectedIndexes(); if (selection.size() > 0) { QModelIndex selected = selection.first(); From b18082376ddc90eab4176ff88a10683d3b38ecff Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 3 Nov 2023 11:04:13 +0200 Subject: [PATCH 232/247] Fixed visual bug with Modlist export Signed-off-by: Trial97 --- launcher/ui/dialogs/ExportToModListDialog.cpp | 7 +++--- launcher/ui/dialogs/ExportToModListDialog.ui | 23 +++++++++++++++++-- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/launcher/ui/dialogs/ExportToModListDialog.cpp b/launcher/ui/dialogs/ExportToModListDialog.cpp index c811bfe6a..a343f555a 100644 --- a/launcher/ui/dialogs/ExportToModListDialog.cpp +++ b/launcher/ui/dialogs/ExportToModListDialog.cpp @@ -214,10 +214,11 @@ void ExportToModListDialog::addExtra(ExportToModList::OptionalData option) void ExportToModListDialog::enableCustom(bool enabled) { ui->authorsCheckBox->setHidden(enabled); - ui->versionCheckBox->setHidden(enabled); - ui->urlCheckBox->setHidden(enabled); - ui->authorsButton->setHidden(!enabled); + + ui->versionCheckBox->setHidden(enabled); ui->versionButton->setHidden(!enabled); + + ui->urlCheckBox->setHidden(enabled); ui->urlButton->setHidden(!enabled); } diff --git a/launcher/ui/dialogs/ExportToModListDialog.ui b/launcher/ui/dialogs/ExportToModListDialog.ui index 25eb43429..4f8ab52b5 100644 --- a/launcher/ui/dialogs/ExportToModListDialog.ui +++ b/launcher/ui/dialogs/ExportToModListDialog.ui @@ -7,7 +7,7 @@ 0 0 650 - 446 + 522 @@ -61,18 +61,37 @@ + + + 0 + 0 + + Template - + + + + 0 + 0 + + + + + + 0 + 0 + + Optional Info From 1bd69ecbb286df9d14a76d2dcb8a635d7895f154 Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Fri, 3 Nov 2023 11:42:40 +0200 Subject: [PATCH 233/247] Update launcher/ui/pages/global/AccountListPage.cpp Co-authored-by: TheKodeToad Signed-off-by: Alexandru Ionut Tripon --- launcher/ui/pages/global/AccountListPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/pages/global/AccountListPage.cpp b/launcher/ui/pages/global/AccountListPage.cpp index 6fd6112d4..3a21cdd9b 100644 --- a/launcher/ui/pages/global/AccountListPage.cpp +++ b/launcher/ui/pages/global/AccountListPage.cpp @@ -167,7 +167,7 @@ void AccountListPage::on_actionAddOffline_triggered() void AccountListPage::on_actionRemove_triggered() { - auto response = CustomMessageBox::selectable(this, tr("Remove account?"), tr("Do you realy want to delete this account?"), + auto response = CustomMessageBox::selectable(this, tr("Remove account?"), tr("Do you really want to delete this account?"), QMessageBox::Question, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) ->exec(); if (response != QMessageBox::Yes) { From 0f95bf1e424e3ebd7f1450461d554f54c71c5f33 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 4 Nov 2023 12:08:49 +0200 Subject: [PATCH 234/247] Updated readme Signed-off-by: Trial97 --- README.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 61bf4c20b..e3457c7ed 100644 --- a/README.md +++ b/README.md @@ -18,11 +18,18 @@
- All downloads and instructions for Prism Launcher can be found on our [Website](https://prismlauncher.org/download). -- Last build status can be found in the [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions). +- Last build status can be found in the [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions) (this also includes the pull requests status). ### Development Builds -There are development builds available [here](https://github.com/PrismLauncher/PrismLauncher/actions). These have debug information in the binaries, so their file sizes are relatively larger. +Please understand that these builds are not intended for most users. There may be bugs, and other instabilities. You have been warned. + +There are development builds available on: + +- [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions) includes builds from pull requests opened by contribuitors. +- [nightly.link](https://nightly.link/PrismLauncher/PrismLauncher/workflows/trigger_builds/develop) this will allways point only to the latest develop. + +These have debug information in the binaries, so their file sizes are relatively larger. Prebuilt Development builds are provided for **Linux**, **Windows** and **macOS**. From 40ebae394afe7ba426a67615e7a658f07ea76297 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 5 Nov 2023 00:18:56 +0000 Subject: [PATCH 235/247] 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/c9afaba3dfa4085dbd2ccb38dfade5141e33d9d4' (2023-10-03) → 'github:hercules-ci/flake-parts/8c9fa2545007b49a5db5f650ae91f227672c3877' (2023-11-01) • Updated input 'flake-parts/nixpkgs-lib': 'github:NixOS/nixpkgs/f5892ddac112a1e9b3612c39af1b72987ee5783a?dir=lib' (2023-09-29) → 'github:NixOS/nixpkgs/0cbe9f69c234a7700596e943bfae7ef27a31b735?dir=lib' (2023-10-29) • Updated input 'nixpkgs': 'github:nixos/nixpkgs/808c0d8c53c7ae50f82aca8e7df263225cf235bf' (2023-10-26) → 'github:nixos/nixpkgs/9d5d25bbfe8c0297ebe85324addcb5020ed1a454' (2023-11-04) • Updated input 'pre-commit-hooks': 'github:cachix/pre-commit-hooks.nix/bd38df3d508dfcdff52cd243d297f218ed2257bf' (2023-10-25) → 'github:cachix/pre-commit-hooks.nix/dec10399e5b56aa95fcd530e0338be72ad6462a0' (2023-11-01) --- flake.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/flake.lock b/flake.lock index 7cf901f77..a520c27e5 100644 --- a/flake.lock +++ b/flake.lock @@ -21,11 +21,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1696343447, - "narHash": "sha256-B2xAZKLkkeRFG5XcHHSXXcP7To9Xzr59KXeZiRf4vdQ=", + "lastModified": 1698882062, + "narHash": "sha256-HkhafUayIqxXyHH1X8d9RDl1M2CkFgZLjKD3MzabiEo=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "c9afaba3dfa4085dbd2ccb38dfade5141e33d9d4", + "rev": "8c9fa2545007b49a5db5f650ae91f227672c3877", "type": "github" }, "original": { @@ -106,11 +106,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1698336494, - "narHash": "sha256-sO72WDBKyijYD1GcKPlGsycKbMBiTJMBCnmOxLAs880=", + "lastModified": 1699094435, + "narHash": "sha256-YLZ5/KKZ1PyLrm2MO8UxRe4H3M0/oaYqNhSlq6FDeeA=", "owner": "nixos", "repo": "nixpkgs", - "rev": "808c0d8c53c7ae50f82aca8e7df263225cf235bf", + "rev": "9d5d25bbfe8c0297ebe85324addcb5020ed1a454", "type": "github" }, "original": { @@ -123,11 +123,11 @@ "nixpkgs-lib": { "locked": { "dir": "lib", - "lastModified": 1696019113, - "narHash": "sha256-X3+DKYWJm93DRSdC5M6K5hLqzSya9BjibtBsuARoPco=", + "lastModified": 1698611440, + "narHash": "sha256-jPjHjrerhYDy3q9+s5EAsuhyhuknNfowY6yt6pjn9pc=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "f5892ddac112a1e9b3612c39af1b72987ee5783a", + "rev": "0cbe9f69c234a7700596e943bfae7ef27a31b735", "type": "github" }, "original": { @@ -153,11 +153,11 @@ ] }, "locked": { - "lastModified": 1698227354, - "narHash": "sha256-Fi5H9jbaQLmLw9qBi/mkR33CoFjNbobo5xWdX4tKz1Q=", + "lastModified": 1698852633, + "narHash": "sha256-Hsc/cCHud8ZXLvmm8pxrXpuaPEeNaaUttaCvtdX/Wug=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "bd38df3d508dfcdff52cd243d297f218ed2257bf", + "rev": "dec10399e5b56aa95fcd530e0338be72ad6462a0", "type": "github" }, "original": { From cf4144cb5022db62c97d35b9917ade9947110d16 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 4 Nov 2023 15:22:43 -0400 Subject: [PATCH 236/247] Fixed link to Fulmine's website Signed-off-by: Alex --- launcher/ui/dialogs/AboutDialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/dialogs/AboutDialog.cpp b/launcher/ui/dialogs/AboutDialog.cpp index f5eaff7a1..cee01e821 100644 --- a/launcher/ui/dialogs/AboutDialog.cpp +++ b/launcher/ui/dialogs/AboutDialog.cpp @@ -101,7 +101,7 @@ QString getCreditsHtml() stream << "

" << QObject::tr("With thanks to", "About Credits") << "

\n"; stream << QString("

Boba %1

\n").arg(getWebsite("https://bobaonline.neocities.org/")); stream << QString("

Davi Rafael %1

\n").arg(getWebsite("https://auti.one/")); - stream << QString("

Fulmine %1

\n").arg(getWebsite("https://www.fulmine.xyz/")); + stream << QString("

Fulmine %1

\n").arg(getWebsite("https://fulmine.xyz/")); stream << QString("

ely %1

\n").arg(getGitHub("elyrodso")); stream << QString("

gon sawa %1

\n").arg(getGitHub("gonsawa")); stream << QString("

Pankakes

\n"); From 64bbcb2834a7974d2c329180add799a728a856f9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 5 Nov 2023 15:57:06 +0000 Subject: [PATCH 237/247] chore(deps): update korthout/backport-action action to v2.1.0 --- .github/workflows/backport.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index f9cf4fb7d..6f2025551 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -24,7 +24,7 @@ jobs: with: ref: ${{ github.event.pull_request.head.sha }} - name: Create backport PRs - uses: korthout/backport-action@v2.0.0 + uses: korthout/backport-action@v2.1.0 with: # Config README: https://github.com/korthout/backport-action#backport-action pull_description: |- From d2a85cb580f47ab181d65d13d18de2a96e297471 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgars=20C=C4=ABrulis?= Date: Sun, 5 Nov 2023 17:41:53 +0000 Subject: [PATCH 238/247] visual: Fix spacing. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Edgars Cīrulis --- launcher/LaunchController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp index 9fb385777..f59cdeda8 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 which owns Minecraft 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 710a48fcafdfd70c13077ea26a57dfb1aa375079 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 5 Nov 2023 21:41:10 +0200 Subject: [PATCH 239/247] changed type form double long to long long Signed-off-by: Trial97 --- launcher/MTPixmapCache.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/MTPixmapCache.h b/launcher/MTPixmapCache.h index 5f8212d74..a1447fd5a 100644 --- a/launcher/MTPixmapCache.h +++ b/launcher/MTPixmapCache.h @@ -113,7 +113,7 @@ class PixmapCache final : public QObject { m_last_cache_miss_by_eviciton = now; if (m_consecutive_fast_evicitons >= m_consecutive_fast_evicitons_threshold) { // double the cache size - auto newSize = _cacheLimit() * 2.L; + auto newSize = _cacheLimit() * 2ll; if (newSize >= std::numeric_limits::max()) { // double it until you overflow :D newSize = std::numeric_limits::max(); qDebug() << m_consecutive_fast_evicitons From 9a8667e99cdfd9608f8aa543b432bacc672a45e4 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 5 Nov 2023 22:22:49 +0200 Subject: [PATCH 240/247] Added Global Dependenicies toggle Signed-off-by: Trial97 --- launcher/Application.cpp | 1 + launcher/ui/dialogs/ModUpdateDialog.cpp | 2 +- launcher/ui/dialogs/ResourceDownloadDialog.cpp | 14 ++++++++------ launcher/ui/pages/global/LauncherPage.cpp | 2 ++ launcher/ui/pages/global/LauncherPage.ui | 10 ++++++++++ launcher/ui/pages/instance/ModFolderPage.cpp | 4 ++++ 6 files changed, 26 insertions(+), 7 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 661c6c5be..be252f1c5 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -644,6 +644,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) // Minecraft mods m_settings->registerSetting("ModMetadataDisabled", false); + m_settings->registerSetting("ModDependenciesDisabled", false); // Minecraft offline player name m_settings->registerSetting("LastOfflinePlayerName", ""); diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index 1a70ea59a..298bdad97 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -186,7 +186,7 @@ void ModUpdateDialog::checkCandidates() } } - { // dependencies + if (!APPLICATION->settings()->get("ModDependenciesDisabled").toBool()) { // dependencies auto depTask = makeShared(this, m_instance, m_mod_model.get(), selectedVers); connect(depTask.get(), &Task::failed, this, diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp index dc7cfff06..1431ea92c 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp +++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp @@ -270,13 +270,15 @@ QList ModDownloadDialog::getPages() GetModDependenciesTask::Ptr ModDownloadDialog::getModDependenciesTask() { - if (auto model = dynamic_cast(getBaseModel().get()); model) { - QList> selectedVers; - for (auto& selected : getTasks()) { - selectedVers.append(std::make_shared(selected->getPack(), selected->getVersion())); - } + if (!APPLICATION->settings()->get("ModDependenciesDisabled").toBool()) { // dependencies + if (auto model = dynamic_cast(getBaseModel().get()); model) { + QList> selectedVers; + for (auto& selected : getTasks()) { + selectedVers.append(std::make_shared(selected->getPack(), selected->getVersion())); + } - return makeShared(this, m_instance, model, selectedVers); + return makeShared(this, m_instance, model, selectedVers); + } } return nullptr; } diff --git a/launcher/ui/pages/global/LauncherPage.cpp b/launcher/ui/pages/global/LauncherPage.cpp index 6d8c65ec5..d15883db5 100644 --- a/launcher/ui/pages/global/LauncherPage.cpp +++ b/launcher/ui/pages/global/LauncherPage.cpp @@ -223,6 +223,7 @@ void LauncherPage::applySettings() // Mods s->set("ModMetadataDisabled", ui->metadataDisableBtn->isChecked()); + s->set("ModDependenciesDisabled", ui->dependenciesDisableBtn->isChecked()); } void LauncherPage::loadSettings() { @@ -278,6 +279,7 @@ void LauncherPage::loadSettings() // Mods ui->metadataDisableBtn->setChecked(s->get("ModMetadataDisabled").toBool()); ui->metadataWarningLabel->setHidden(!ui->metadataDisableBtn->isChecked()); + ui->dependenciesDisableBtn->setChecked(s->get("ModDependenciesDisabled").toBool()); } void LauncherPage::refreshFontPreview() diff --git a/launcher/ui/pages/global/LauncherPage.ui b/launcher/ui/pages/global/LauncherPage.ui index 250a8bc88..637b2cd81 100644 --- a/launcher/ui/pages/global/LauncherPage.ui +++ b/launcher/ui/pages/global/LauncherPage.ui @@ -186,6 +186,16 @@ + + + + Disable mod dependencies checking. + + + Disable mod dependencies checking + + + diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index 625d37933..8d99256b9 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -214,6 +214,10 @@ void ModFolderPage::updateMods() QMessageBox::critical(this, tr("Error"), tr("Please install a mod loader first!")); return; } + if (APPLICATION->settings()->get("ModMetadataDisabled").toBool()) { + QMessageBox::critical(this, tr("Error"), tr("The mod update is disabled when the metadata is disabled!")); + return; + } auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); auto mods_list = m_model->selectedMods(selection); From bd9d5e09907071d2a2576add9c99314e6c52ed20 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sun, 5 Nov 2023 20:31:48 +0000 Subject: [PATCH 241/247] Reword Signed-off-by: TheKodeToad --- launcher/ui/pages/global/LauncherPage.ui | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/ui/pages/global/LauncherPage.ui b/launcher/ui/pages/global/LauncherPage.ui index 637b2cd81..32d89ee51 100644 --- a/launcher/ui/pages/global/LauncherPage.ui +++ b/launcher/ui/pages/global/LauncherPage.ui @@ -189,10 +189,10 @@ - Disable mod dependencies checking. + Disable automatically checking and installation of mod dependencies. - Disable mod dependencies checking + Do not install mod dependencies From 902e861fc68b658d999476f273f4a15fe4207672 Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Sun, 5 Nov 2023 23:41:26 +0200 Subject: [PATCH 242/247] Apply suggestions from code review Co-authored-by: seth Signed-off-by: Alexandru Ionut Tripon --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e3457c7ed..b32132d49 100644 --- a/README.md +++ b/README.md @@ -18,16 +18,16 @@ - All downloads and instructions for Prism Launcher can be found on our [Website](https://prismlauncher.org/download). -- Last build status can be found in the [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions) (this also includes the pull requests status). +- Last build status can be found in the [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions) tab (this also includes the pull requests status). ### Development Builds Please understand that these builds are not intended for most users. There may be bugs, and other instabilities. You have been warned. -There are development builds available on: +There are development builds available through: -- [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions) includes builds from pull requests opened by contribuitors. -- [nightly.link](https://nightly.link/PrismLauncher/PrismLauncher/workflows/trigger_builds/develop) this will allways point only to the latest develop. +- [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions) (includes builds from pull requests opened by contribuitors) +- [nightly.link](https://nightly.link/PrismLauncher/PrismLauncher/workflows/trigger_builds/develop) (this will always point only to the latest version of develop) These have debug information in the binaries, so their file sizes are relatively larger. From 3e7f9083cab97489aab958d3bbc649b24d58abb6 Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Sun, 5 Nov 2023 23:41:54 +0200 Subject: [PATCH 243/247] Update launcher/ui/pages/instance/ModFolderPage.cpp Co-authored-by: seth Signed-off-by: Alexandru Ionut Tripon --- launcher/ui/pages/instance/ModFolderPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index 8d99256b9..f45f8b4b3 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -215,7 +215,7 @@ void ModFolderPage::updateMods() return; } if (APPLICATION->settings()->get("ModMetadataDisabled").toBool()) { - QMessageBox::critical(this, tr("Error"), tr("The mod update is disabled when the metadata is disabled!")); + QMessageBox::critical(this, tr("Error"), tr("Mod updates are unavailable when metadata is disabled!")); return; } auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); From 867e6223ce5601ea32e8fc471d5dc3e13e32e276 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 5 Nov 2023 23:48:26 +0200 Subject: [PATCH 244/247] Fixed mangoHub loading Signed-off-by: Trial97 --- launcher/minecraft/MinecraftInstance.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 170561f11..7cb4100ea 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -575,15 +575,20 @@ QProcessEnvironment MinecraftInstance::createLaunchEnvironment() #ifdef Q_OS_LINUX if (settings()->get("EnableMangoHud").toBool() && APPLICATION->capabilities() & Application::SupportsMangoHud) { - auto preloadList = env.value("LD_PRELOAD").split(QLatin1String(":")); - auto libPaths = env.value("LD_LIBRARY_PATH").split(QLatin1String(":")); + QStringList preloadList; + if (auto value = env.value("LD_PRELOAD"); !value.isEmpty()) + preloadList = value.split(QLatin1String(":")); + QStringList libPaths; + if (auto value = env.value("LD_LIBRARY_PATH"); !value.isEmpty()) + libPaths = value.split(QLatin1String(":")); auto mangoHudLibString = MangoHud::getLibraryString(); if (!mangoHudLibString.isEmpty()) { QFileInfo mangoHudLib(mangoHudLibString); // dlsym variant is only needed for OpenGL and not included in the vulkan layer - preloadList << "libMangoHud_dlsym.so" << mangoHudLib.fileName(); + preloadList << "libMangoHud_dlsym.so" + << "libMangoHud_opengl.so" << mangoHudLib.fileName(); libPaths << mangoHudLib.absolutePath(); } From cc291219f9c937ec69402f7b88b79d1f33927ba3 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 6 Nov 2023 10:51:34 +0200 Subject: [PATCH 245/247] apply suggested changes Signed-off-by: Trial97 --- launcher/MTPixmapCache.h | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/launcher/MTPixmapCache.h b/launcher/MTPixmapCache.h index a1447fd5a..da9500788 100644 --- a/launcher/MTPixmapCache.h +++ b/launcher/MTPixmapCache.h @@ -101,10 +101,14 @@ class PixmapCache final : public QObject { */ bool _markCacheMissByEviciton() { + static constexpr int maxInt = std::numeric_limits::max(); + static constexpr uint oneMB = 1000; + static constexpr int oneSecond = 1000; + auto now = QTime::currentTime(); if (!m_last_cache_miss_by_eviciton.isNull()) { auto diff = m_last_cache_miss_by_eviciton.msecsTo(now); - if (diff < 1000) { // less than a second ago + if (diff < oneSecond) { // less than a second ago ++m_consecutive_fast_evicitons; } else { m_consecutive_fast_evicitons = 0; @@ -112,10 +116,10 @@ class PixmapCache final : public QObject { } m_last_cache_miss_by_eviciton = now; if (m_consecutive_fast_evicitons >= m_consecutive_fast_evicitons_threshold) { - // double the cache size - auto newSize = _cacheLimit() * 2ll; - if (newSize >= std::numeric_limits::max()) { // double it until you overflow :D - newSize = std::numeric_limits::max(); + // increase the cache size + uint newSize = _cacheLimit() + oneMB; + if (newSize >= maxInt) { // increase it until you overflow :D + newSize = maxInt; qDebug() << m_consecutive_fast_evicitons << tr("pixmap cache misses by eviction happened too fast, doing nothing as the cache size reached it's limit"); } else { From 670e91cb6072d42f74e501cfaeadbb49a8f171b4 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 6 Nov 2023 11:08:18 +0200 Subject: [PATCH 246/247] Increased the step to around 10MB Signed-off-by: Trial97 --- launcher/MTPixmapCache.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/MTPixmapCache.h b/launcher/MTPixmapCache.h index da9500788..eab6a70ec 100644 --- a/launcher/MTPixmapCache.h +++ b/launcher/MTPixmapCache.h @@ -102,7 +102,7 @@ class PixmapCache final : public QObject { bool _markCacheMissByEviciton() { static constexpr int maxInt = std::numeric_limits::max(); - static constexpr uint oneMB = 1000; + static constexpr uint step = 10240; static constexpr int oneSecond = 1000; auto now = QTime::currentTime(); @@ -117,7 +117,7 @@ class PixmapCache final : public QObject { m_last_cache_miss_by_eviciton = now; if (m_consecutive_fast_evicitons >= m_consecutive_fast_evicitons_threshold) { // increase the cache size - uint newSize = _cacheLimit() + oneMB; + uint newSize = _cacheLimit() + step; if (newSize >= maxInt) { // increase it until you overflow :D newSize = maxInt; qDebug() << m_consecutive_fast_evicitons From ca226d2ab5c3fb88d2e4a69ae94d2393960e9713 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 6 Nov 2023 11:24:19 +0200 Subject: [PATCH 247/247] Fixed comparation warning Signed-off-by: Trial97 --- launcher/MTPixmapCache.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/launcher/MTPixmapCache.h b/launcher/MTPixmapCache.h index eab6a70ec..b6bd13045 100644 --- a/launcher/MTPixmapCache.h +++ b/launcher/MTPixmapCache.h @@ -101,7 +101,7 @@ class PixmapCache final : public QObject { */ bool _markCacheMissByEviciton() { - static constexpr int maxInt = std::numeric_limits::max(); + static constexpr uint maxInt = static_cast(std::numeric_limits::max()); static constexpr uint step = 10240; static constexpr int oneSecond = 1000; @@ -123,8 +123,8 @@ class PixmapCache final : public QObject { qDebug() << m_consecutive_fast_evicitons << tr("pixmap cache misses by eviction happened too fast, doing nothing as the cache size reached it's limit"); } else { - qDebug() << m_consecutive_fast_evicitons << tr("pixmap cache misses by eviction happened too fast, doubling cache size to") - << static_cast(newSize); + qDebug() << m_consecutive_fast_evicitons + << tr("pixmap cache misses by eviction happened too fast, increasing cache size to") << static_cast(newSize); } _setCacheLimit(static_cast(newSize)); m_consecutive_fast_evicitons = 0;