From 8de63b60b1a9d0ba16f5d45f3198c13637151749 Mon Sep 17 00:00:00 2001 From: icelimetea Date: Mon, 2 May 2022 22:36:55 +0100 Subject: [PATCH 01/67] Refactor some parts of NewLaunch (part 2) --- libraries/launcher/CMakeLists.txt | 13 +- .../launcher/net/minecraft/Launcher.java | 92 +++---- .../launcher/org/multimc/EntryPoint.java | 55 ++-- libraries/launcher/org/multimc/Launcher.java | 7 +- .../launcher/org/multimc/LauncherFactory.java | 34 +++ .../launcher/org/multimc/LegacyFrame.java | 176 ------------ libraries/launcher/org/multimc/Utils.java | 86 ------ .../org/multimc/applet/LegacyFrame.java | 167 ++++++++++++ .../ParameterNotFoundException.java} | 10 +- .../{ => exception}/ParseException.java | 12 +- .../org/multimc/impl/OneSixLauncher.java | 183 +++++++++++++ .../org/multimc/onesix/OneSixLauncher.java | 256 ------------------ .../org/multimc/{ => utils}/ParamBucket.java | 36 +-- .../launcher/org/multimc/utils/Utils.java | 49 ++++ 14 files changed, 542 insertions(+), 634 deletions(-) create mode 100644 libraries/launcher/org/multimc/LauncherFactory.java delete mode 100644 libraries/launcher/org/multimc/LegacyFrame.java delete mode 100644 libraries/launcher/org/multimc/Utils.java create mode 100644 libraries/launcher/org/multimc/applet/LegacyFrame.java rename libraries/launcher/org/multimc/{NotFoundException.java => exception/ParameterNotFoundException.java} (73%) rename libraries/launcher/org/multimc/{ => exception}/ParseException.java (81%) create mode 100644 libraries/launcher/org/multimc/impl/OneSixLauncher.java delete mode 100644 libraries/launcher/org/multimc/onesix/OneSixLauncher.java rename libraries/launcher/org/multimc/{ => utils}/ParamBucket.java (68%) create mode 100644 libraries/launcher/org/multimc/utils/Utils.java diff --git a/libraries/launcher/CMakeLists.txt b/libraries/launcher/CMakeLists.txt index 0eccae8b..e0149482 100644 --- a/libraries/launcher/CMakeLists.txt +++ b/libraries/launcher/CMakeLists.txt @@ -9,12 +9,13 @@ set(CMAKE_JAVA_COMPILE_FLAGS -target 8 -source 8 -Xlint:deprecation -Xlint:unche set(SRC org/multimc/EntryPoint.java org/multimc/Launcher.java - org/multimc/LegacyFrame.java - org/multimc/NotFoundException.java - org/multimc/ParamBucket.java - org/multimc/ParseException.java - org/multimc/Utils.java - org/multimc/onesix/OneSixLauncher.java + org/multimc/LauncherFactory.java + org/multimc/impl/OneSixLauncher.java + org/multimc/applet/LegacyFrame.java + org/multimc/exception/ParameterNotFoundException.java + org/multimc/exception/ParseException.java + org/multimc/utils/ParamBucket.java + org/multimc/utils/Utils.java net/minecraft/Launcher.java ) add_jar(NewLaunch ${SRC}) diff --git a/libraries/launcher/net/minecraft/Launcher.java b/libraries/launcher/net/minecraft/Launcher.java index b6b0a574..04201047 100644 --- a/libraries/launcher/net/minecraft/Launcher.java +++ b/libraries/launcher/net/minecraft/Launcher.java @@ -16,29 +16,28 @@ package net.minecraft; -import java.util.TreeMap; -import java.util.Map; -import java.net.URL; -import java.awt.Dimension; -import java.awt.BorderLayout; -import java.awt.Graphics; import java.applet.Applet; import java.applet.AppletStub; +import java.awt.*; import java.net.MalformedURLException; +import java.net.URL; +import java.util.Map; +import java.util.TreeMap; + +public class Launcher extends Applet implements AppletStub { + + private final Map params = new TreeMap<>(); + + private boolean active = false; -public class Launcher extends Applet implements AppletStub -{ private Applet wrappedApplet; private URL documentBase; - private boolean active = false; - private final Map params; - - public Launcher(Applet applet, URL documentBase) - { - params = new TreeMap(); + public Launcher(Applet applet, URL documentBase) { this.setLayout(new BorderLayout()); + this.add(applet, "Center"); + this.wrappedApplet = applet; this.documentBase = documentBase; } @@ -48,8 +47,7 @@ public class Launcher extends Applet implements AppletStub params.put(name, value); } - public void replace(Applet applet) - { + public void replace(Applet applet) { this.wrappedApplet = applet; applet.setStub(this); @@ -65,67 +63,60 @@ public class Launcher extends Applet implements AppletStub } @Override - public String getParameter(String name) - { + public String getParameter(String name) { String param = params.get(name); + if (param != null) return param; - try - { + + try { return super.getParameter(name); - } catch (Exception ignore){} + } catch (Exception ignore) {} + return null; } @Override - public boolean isActive() - { + public boolean isActive() { return active; } @Override - public void appletResize(int width, int height) - { + public void appletResize(int width, int height) { wrappedApplet.resize(width, height); } @Override - public void resize(int width, int height) - { + public void resize(int width, int height) { wrappedApplet.resize(width, height); } @Override - public void resize(Dimension d) - { + public void resize(Dimension d) { wrappedApplet.resize(d); } @Override - public void init() - { + public void init() { if (wrappedApplet != null) - { wrappedApplet.init(); - } } @Override - public void start() - { + public void start() { wrappedApplet.start(); + active = true; } @Override - public void stop() - { + public void stop() { wrappedApplet.stop(); + active = false; } - public void destroy() - { + public void destroy() { wrappedApplet.destroy(); } @@ -136,34 +127,35 @@ public class Launcher extends Applet implements AppletStub } catch (MalformedURLException e) { e.printStackTrace(); } + return null; } @Override - public URL getDocumentBase() - { + public URL getDocumentBase() { try { // Special case only for Classic versions if (wrappedApplet.getClass().getCanonicalName().startsWith("com.mojang")) { return new URL("http", "www.minecraft.net", 80, "/game/", null); } + return new URL("http://www.minecraft.net/game/"); } catch (MalformedURLException e) { e.printStackTrace(); } + return null; } @Override - public void setVisible(boolean b) - { + public void setVisible(boolean b) { super.setVisible(b); + wrappedApplet.setVisible(b); } - public void update(Graphics paramGraphics) - { - } - public void paint(Graphics paramGraphics) - { - } -} \ No newline at end of file + + public void update(Graphics paramGraphics) {} + + public void paint(Graphics paramGraphics) {} + +} diff --git a/libraries/launcher/org/multimc/EntryPoint.java b/libraries/launcher/org/multimc/EntryPoint.java index b626d095..be06d1b4 100644 --- a/libraries/launcher/org/multimc/EntryPoint.java +++ b/libraries/launcher/org/multimc/EntryPoint.java @@ -14,7 +14,8 @@ package org.multimc;/* * limitations under the License. */ -import org.multimc.onesix.OneSixLauncher; +import org.multimc.exception.ParseException; +import org.multimc.utils.ParamBucket; import java.io.BufferedReader; import java.io.IOException; @@ -23,31 +24,27 @@ import java.nio.charset.StandardCharsets; import java.util.logging.Level; import java.util.logging.Logger; -public class EntryPoint -{ +public final class EntryPoint { private static final Logger LOGGER = Logger.getLogger("EntryPoint"); private final ParamBucket params = new ParamBucket(); - private org.multimc.Launcher launcher; + private String launcherType; - public static void main(String[] args) - { + public static void main(String[] args) { EntryPoint listener = new EntryPoint(); int retCode = listener.listen(); - if (retCode != 0) - { + if (retCode != 0) { LOGGER.info("Exiting with " + retCode); System.exit(retCode); } } - private Action parseLine(String inData) throws ParseException - { + private Action parseLine(String inData) throws ParseException { String[] tokens = inData.split("\\s+", 2); if (tokens.length == 0) @@ -66,15 +63,9 @@ public class EntryPoint if (tokens.length != 2) throw new ParseException("Expected 2 tokens, got " + tokens.length); - if (tokens[1].equals("onesix")) { - launcher = new OneSixLauncher(); + launcherType = tokens[1]; - LOGGER.info("Using onesix launcher."); - - return Action.Proceed; - } else { - throw new ParseException("Invalid launcher type: " + tokens[1]); - } + return Action.Proceed; } default: { @@ -88,8 +79,7 @@ public class EntryPoint } } - public int listen() - { + public int listen() { Action action = Action.Proceed; try (BufferedReader reader = new BufferedReader(new InputStreamReader( @@ -112,16 +102,31 @@ public class EntryPoint } // Main loop - if (action == Action.Abort) - { + if (action == Action.Abort) { LOGGER.info("Launch aborted by the launcher."); return 1; } - if (launcher != null) - { - return launcher.launch(params); + if (launcherType != null) { + try { + Launcher launcher = + LauncherFactory + .getInstance() + .createLauncher(launcherType, params); + + launcher.launch(); + + return 0; + } catch (IllegalArgumentException e) { + LOGGER.log(Level.SEVERE, "Wrong argument.", e); + + return 1; + } catch (Exception e) { + LOGGER.log(Level.SEVERE, "Exception caught from launcher.", e); + + return 1; + } } LOGGER.log(Level.SEVERE, "No valid launcher implementation specified."); diff --git a/libraries/launcher/org/multimc/Launcher.java b/libraries/launcher/org/multimc/Launcher.java index c5e8fbc1..bc0b525e 100644 --- a/libraries/launcher/org/multimc/Launcher.java +++ b/libraries/launcher/org/multimc/Launcher.java @@ -16,7 +16,8 @@ package org.multimc; -public interface Launcher -{ - int launch(ParamBucket params); +public interface Launcher { + + void launch() throws Exception; + } diff --git a/libraries/launcher/org/multimc/LauncherFactory.java b/libraries/launcher/org/multimc/LauncherFactory.java new file mode 100644 index 00000000..b5d0dd5b --- /dev/null +++ b/libraries/launcher/org/multimc/LauncherFactory.java @@ -0,0 +1,34 @@ +package org.multimc; + +import org.multimc.impl.OneSixLauncher; +import org.multimc.utils.ParamBucket; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +public final class LauncherFactory { + + private static final LauncherFactory INSTANCE = new LauncherFactory(); + + private final Map> launcherRegistry = new HashMap<>(); + + private LauncherFactory() { + launcherRegistry.put("onesix", OneSixLauncher::new); + } + + public Launcher createLauncher(String name, ParamBucket parameters) { + Function launcherCreator = + launcherRegistry.get(name); + + if (launcherCreator == null) + throw new IllegalArgumentException("Invalid launcher type: " + name); + + return launcherCreator.apply(parameters); + } + + public static LauncherFactory getInstance() { + return INSTANCE; + } + +} diff --git a/libraries/launcher/org/multimc/LegacyFrame.java b/libraries/launcher/org/multimc/LegacyFrame.java deleted file mode 100644 index 985a10e6..00000000 --- a/libraries/launcher/org/multimc/LegacyFrame.java +++ /dev/null @@ -1,176 +0,0 @@ -package org.multimc;/* - * Copyright 2012-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import net.minecraft.Launcher; - -import javax.imageio.ImageIO; -import java.applet.Applet; -import java.awt.*; -import java.awt.event.WindowEvent; -import java.awt.event.WindowListener; -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Scanner; - -public class LegacyFrame extends Frame implements WindowListener -{ - private Launcher appletWrap = null; - public LegacyFrame(String title) - { - super ( title ); - BufferedImage image; - try { - image = ImageIO.read ( new File ( "icon.png" ) ); - setIconImage ( image ); - } catch ( IOException e ) { - e.printStackTrace(); - } - this.addWindowListener ( this ); - } - - public void start ( - Applet mcApplet, - String user, - String session, - int winSizeW, - int winSizeH, - boolean maximize, - String serverAddress, - String serverPort - ) - { - try { - appletWrap = new Launcher( mcApplet, new URL ( "http://www.minecraft.net/game" ) ); - } catch ( MalformedURLException ignored ) {} - - // Implements support for launching in to multiplayer on classic servers using a mpticket - // file generated by an external program and stored in the instance's root folder. - File mpticketFile = null; - Scanner fileReader = null; - try { - mpticketFile = new File(System.getProperty("user.dir") + "/../mpticket").getCanonicalFile(); - fileReader = new Scanner(new FileInputStream(mpticketFile), "ascii"); - String[] mpticketParams = new String[3]; - - for(int i=0;i<3;i++) { - if(fileReader.hasNextLine()) { - mpticketParams[i] = fileReader.nextLine(); - } else { - throw new IllegalArgumentException(); - } - } - - // Assumes parameters are valid and in the correct order - appletWrap.setParameter("server", mpticketParams[0]); - appletWrap.setParameter("port", mpticketParams[1]); - appletWrap.setParameter("mppass", mpticketParams[2]); - - fileReader.close(); - mpticketFile.delete(); - } - catch (FileNotFoundException e) {} - catch (IllegalArgumentException e) { - - fileReader.close(); - File mpticketFileCorrupt = new File(System.getProperty("user.dir") + "/../mpticket.corrupt"); - if(mpticketFileCorrupt.exists()) { - mpticketFileCorrupt.delete(); - } - mpticketFile.renameTo(mpticketFileCorrupt); - - System.err.println("Malformed mpticket file, missing argument."); - e.printStackTrace(System.err); - System.exit(-1); - } - catch (Exception e) { - e.printStackTrace(System.err); - System.exit(-1); - } - - if (serverAddress != null) - { - appletWrap.setParameter("server", serverAddress); - appletWrap.setParameter("port", serverPort); - } - - appletWrap.setParameter ( "username", user ); - appletWrap.setParameter ( "sessionid", session ); - appletWrap.setParameter ( "stand-alone", "true" ); // Show the quit button. - appletWrap.setParameter ( "haspaid", "true" ); // Some old versions need this for world saves to work. - appletWrap.setParameter ( "demo", "false" ); - appletWrap.setParameter ( "fullscreen", "false" ); - mcApplet.setStub(appletWrap); - this.add ( appletWrap ); - appletWrap.setPreferredSize ( new Dimension (winSizeW, winSizeH) ); - this.pack(); - this.setLocationRelativeTo ( null ); - this.setResizable ( true ); - if ( maximize ) { - this.setExtendedState ( MAXIMIZED_BOTH ); - } - validate(); - appletWrap.init(); - appletWrap.start(); - setVisible ( true ); - } - - @Override - public void windowActivated ( WindowEvent e ) {} - - @Override - public void windowClosed ( WindowEvent e ) {} - - @Override - public void windowClosing ( WindowEvent e ) - { - new Thread() { - public void run() { - try { - Thread.sleep ( 30000L ); - } catch ( InterruptedException localInterruptedException ) { - localInterruptedException.printStackTrace(); - } - System.out.println ( "FORCING EXIT!" ); - System.exit ( 0 ); - } - } - .start(); - - if ( appletWrap != null ) { - appletWrap.stop(); - appletWrap.destroy(); - } - // old minecraft versions can hang without this >_< - System.exit ( 0 ); - } - - @Override - public void windowDeactivated ( WindowEvent e ) {} - - @Override - public void windowDeiconified ( WindowEvent e ) {} - - @Override - public void windowIconified ( WindowEvent e ) {} - - @Override - public void windowOpened ( WindowEvent e ) {} -} diff --git a/libraries/launcher/org/multimc/Utils.java b/libraries/launcher/org/multimc/Utils.java deleted file mode 100644 index e48029c2..00000000 --- a/libraries/launcher/org/multimc/Utils.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2012-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.multimc; - -import java.io.File; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.List; - -public class Utils -{ - /** - * Combine two parts of a path. - * - * @param path1 - * @param path2 - * @return the paths, combined - */ - public static String combine(String path1, String path2) - { - File file1 = new File(path1); - File file2 = new File(file1, path2); - return file2.getPath(); - } - - /** - * Join a list of strings into a string using a separator! - * - * @param strings the string list to join - * @param separator the glue - * @return the result. - */ - public static String join(List strings, String separator) - { - StringBuilder sb = new StringBuilder(); - String sep = ""; - for (String s : strings) - { - sb.append(sep).append(s); - sep = separator; - } - return sb.toString(); - } - - /** - * Finds a field that looks like a Minecraft base folder in a supplied class - * - * @param mc the class to scan - */ - public static Field getMCPathField(Class mc) - { - Field[] fields = mc.getDeclaredFields(); - - for (Field f : fields) - { - if (f.getType() != File.class) - { - // Has to be File - continue; - } - if (f.getModifiers() != (Modifier.PRIVATE + Modifier.STATIC)) - { - // And Private Static. - continue; - } - return f; - } - return null; - } - -} - diff --git a/libraries/launcher/org/multimc/applet/LegacyFrame.java b/libraries/launcher/org/multimc/applet/LegacyFrame.java new file mode 100644 index 00000000..a5e6c170 --- /dev/null +++ b/libraries/launcher/org/multimc/applet/LegacyFrame.java @@ -0,0 +1,167 @@ +package org.multimc.applet;/* + * Copyright 2012-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import net.minecraft.Launcher; + +import javax.imageio.ImageIO; +import java.applet.Applet; +import java.awt.*; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.Scanner; +import java.util.logging.Level; +import java.util.logging.Logger; + +public final class LegacyFrame extends Frame { + + private static final Logger LOGGER = Logger.getLogger("LegacyFrame"); + + private Launcher appletWrap; + + public LegacyFrame(String title) { + super(title); + + try { + setIconImage(ImageIO.read(new File("icon.png"))); + } catch (IOException e) { + LOGGER.log(Level.WARNING, "Unable to read Minecraft icon!", e); + } + + this.addWindowListener(new ForceExitHandler()); + } + + public void start ( + Applet mcApplet, + String user, + String session, + int winSizeW, + int winSizeH, + boolean maximize, + String serverAddress, + String serverPort + ) { + try { + appletWrap = new Launcher(mcApplet, new URL("http://www.minecraft.net/game")); + } catch (MalformedURLException ignored) {} + + // Implements support for launching in to multiplayer on classic servers using a mpticket + // file generated by an external program and stored in the instance's root folder. + Path mpticketFile = Paths.get(System.getProperty("user.dir") + "/../mpticket"); + Path mpticketFileCorrupt = Paths.get(System.getProperty("user.dir") + "/../mpticket.corrupt"); + + if (Files.exists(mpticketFile)) { + try (Scanner fileScanner = new Scanner( + Files.newInputStream(mpticketFile), + StandardCharsets.US_ASCII.name() + )) { + String[] mpticketParams = new String[3]; + + for (int i = 0; i < mpticketParams.length; i++) { + if (fileScanner.hasNextLine()) { + mpticketParams[i] = fileScanner.nextLine(); + } else { + Files.move( + mpticketFile, + mpticketFileCorrupt, + StandardCopyOption.REPLACE_EXISTING + ); + + throw new IllegalArgumentException("Mpticket file is corrupted!"); + } + } + + Files.delete(mpticketFile); + + // Assumes parameters are valid and in the correct order + appletWrap.setParameter("server", mpticketParams[0]); + appletWrap.setParameter("port", mpticketParams[1]); + appletWrap.setParameter("mppass", mpticketParams[2]); + } catch (IOException e) { + LOGGER.log(Level.WARNING, "Unable to read mpticket file!", e); + } + } + + if (serverAddress != null) { + appletWrap.setParameter("server", serverAddress); + appletWrap.setParameter("port", serverPort); + } + + appletWrap.setParameter("username", user); + appletWrap.setParameter("sessionid", session); + appletWrap.setParameter("stand-alone", "true"); // Show the quit button. + appletWrap.setParameter("haspaid", "true"); // Some old versions need this for world saves to work. + appletWrap.setParameter("demo", "false"); + appletWrap.setParameter("fullscreen", "false"); + + mcApplet.setStub(appletWrap); + + add(appletWrap); + + appletWrap.setPreferredSize(new Dimension(winSizeW, winSizeH)); + + pack(); + + setLocationRelativeTo(null); + setResizable(true); + + if (maximize) + this.setExtendedState(MAXIMIZED_BOTH); + + validate(); + + appletWrap.init(); + appletWrap.start(); + + setVisible(true); + } + + private final class ForceExitHandler extends WindowAdapter { + + @Override + public void windowClosing(WindowEvent e) { + new Thread(() -> { + try { + Thread.sleep(30000L); + } catch (InterruptedException localInterruptedException) { + localInterruptedException.printStackTrace(); + } + + LOGGER.info("Forcing exit!"); + + System.exit(0); + }).start(); + + if (appletWrap != null) { + appletWrap.stop(); + appletWrap.destroy(); + } + + // old minecraft versions can hang without this >_< + System.exit(0); + } + + } + +} diff --git a/libraries/launcher/org/multimc/NotFoundException.java b/libraries/launcher/org/multimc/exception/ParameterNotFoundException.java similarity index 73% rename from libraries/launcher/org/multimc/NotFoundException.java rename to libraries/launcher/org/multimc/exception/ParameterNotFoundException.java index ba12951d..9edbb826 100644 --- a/libraries/launcher/org/multimc/NotFoundException.java +++ b/libraries/launcher/org/multimc/exception/ParameterNotFoundException.java @@ -14,8 +14,12 @@ * limitations under the License. */ -package org.multimc; +package org.multimc.exception; + +public final class ParameterNotFoundException extends IllegalArgumentException { + + public ParameterNotFoundException(String key) { + super("Unknown parameter name: " + key); + } -public class NotFoundException extends Exception -{ } diff --git a/libraries/launcher/org/multimc/ParseException.java b/libraries/launcher/org/multimc/exception/ParseException.java similarity index 81% rename from libraries/launcher/org/multimc/ParseException.java rename to libraries/launcher/org/multimc/exception/ParseException.java index 7ea44c1f..c9a4c856 100644 --- a/libraries/launcher/org/multimc/ParseException.java +++ b/libraries/launcher/org/multimc/exception/ParseException.java @@ -14,12 +14,16 @@ * limitations under the License. */ -package org.multimc; +package org.multimc.exception; + +public final class ParseException extends IllegalArgumentException { + + public ParseException() { + super(); + } -public class ParseException extends java.lang.Exception -{ - public ParseException() { super(); } public ParseException(String message) { super(message); } + } diff --git a/libraries/launcher/org/multimc/impl/OneSixLauncher.java b/libraries/launcher/org/multimc/impl/OneSixLauncher.java new file mode 100644 index 00000000..d2596a69 --- /dev/null +++ b/libraries/launcher/org/multimc/impl/OneSixLauncher.java @@ -0,0 +1,183 @@ +/* Copyright 2012-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.multimc.impl; + +import org.multimc.Launcher; +import org.multimc.applet.LegacyFrame; +import org.multimc.utils.ParamBucket; +import org.multimc.utils.Utils; + +import java.applet.Applet; +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +public final class OneSixLauncher implements Launcher { + + private static final int DEFAULT_WINDOW_WIDTH = 854; + private static final int DEFAULT_WINDOW_HEIGHT = 480; + + private static final Logger LOGGER = Logger.getLogger("OneSixLauncher"); + + // parameters, separated from ParamBucket + private final List mcParams; + private final List traits; + private final String appletClass; + private final String mainClass; + private final String userName, sessionId; + private final String windowTitle; + + // secondary parameters + private final int winSizeW; + private final int winSizeH; + private final boolean maximize; + private final String cwd; + + private final String serverAddress; + private final String serverPort; + + private final ClassLoader classLoader; + + public OneSixLauncher(ParamBucket params) { + classLoader = ClassLoader.getSystemClassLoader(); + + mcParams = params.allSafe("param", Collections.emptyList()); + mainClass = params.firstSafe("mainClass", "net.minecraft.client.Minecraft"); + appletClass = params.firstSafe("appletClass", "net.minecraft.client.MinecraftApplet"); + traits = params.allSafe("traits", Collections.emptyList()); + + userName = params.first("userName"); + sessionId = params.first("sessionId"); + windowTitle = params.firstSafe("windowTitle", "Minecraft"); + + serverAddress = params.firstSafe("serverAddress", null); + serverPort = params.firstSafe("serverPort", null); + + cwd = System.getProperty("user.dir"); + + String windowParams = params.firstSafe("windowParams", "854x480"); + + String[] dimStrings = windowParams.split("x"); + + if (windowParams.equalsIgnoreCase("max")) { + maximize = true; + + winSizeW = DEFAULT_WINDOW_WIDTH; + winSizeH = DEFAULT_WINDOW_HEIGHT; + } else if (dimStrings.length == 2) { + maximize = false; + + winSizeW = Integer.parseInt(dimStrings[0]); + winSizeH = Integer.parseInt(dimStrings[1]); + } else { + throw new IllegalArgumentException("Unexpected window size parameter value: " + windowParams); + } + } + + private void invokeMain(Class mainClass) throws Exception { + Method method = mainClass.getMethod("main", String[].class); + + method.invoke(null, (Object) mcParams.toArray(new String[0])); + } + + private void legacyLaunch() throws Exception { + // Get the Minecraft Class and set the base folder + Class minecraftClass = classLoader.loadClass(mainClass); + + Field baseDirField = Utils.getMinecraftBaseDirField(minecraftClass); + + if (baseDirField == null) { + LOGGER.warning("Could not find Minecraft path field."); + } else { + baseDirField.setAccessible(true); + + baseDirField.set(null, new File(cwd)); + } + + System.setProperty("minecraft.applet.TargetDirectory", cwd); + + if (!traits.contains("noapplet")) { + LOGGER.info("Launching with applet wrapper..."); + + try { + Class mcAppletClass = classLoader.loadClass(appletClass); + + Applet mcApplet = (Applet) mcAppletClass.getConstructor().newInstance(); + + LegacyFrame mcWindow = new LegacyFrame(windowTitle); + + mcWindow.start( + mcApplet, + userName, + sessionId, + winSizeW, + winSizeH, + maximize, + serverAddress, + serverPort + ); + + return; + } catch (Exception e) { + LOGGER.log(Level.SEVERE, "Applet wrapper failed: ", e); + + LOGGER.warning("Falling back to using main class."); + } + } + + invokeMain(minecraftClass); + } + + void launchWithMainClass() throws Exception { + // window size, title and state, onesix + + // FIXME: there is no good way to maximize the minecraft window in onesix. + // the following often breaks linux screen setups + // mcparams.add("--fullscreen"); + + if (!maximize) { + mcParams.add("--width"); + mcParams.add(Integer.toString(winSizeW)); + mcParams.add("--height"); + mcParams.add(Integer.toString(winSizeH)); + } + + if (serverAddress != null) { + mcParams.add("--server"); + mcParams.add(serverAddress); + mcParams.add("--port"); + mcParams.add(serverPort); + } + + invokeMain(classLoader.loadClass(mainClass)); + } + + @Override + public void launch() throws Exception { + if (traits.contains("legacyLaunch") || traits.contains("alphaLaunch")) { + // legacy launch uses the applet wrapper + legacyLaunch(); + } else { + // normal launch just calls main() + launchWithMainClass(); + } + } + +} diff --git a/libraries/launcher/org/multimc/onesix/OneSixLauncher.java b/libraries/launcher/org/multimc/onesix/OneSixLauncher.java deleted file mode 100644 index 0058bd43..00000000 --- a/libraries/launcher/org/multimc/onesix/OneSixLauncher.java +++ /dev/null @@ -1,256 +0,0 @@ -/* Copyright 2012-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.multimc.onesix; - -import org.multimc.*; - -import java.applet.Applet; -import java.io.File; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class OneSixLauncher implements Launcher -{ - - private static final Logger LOGGER = Logger.getLogger("OneSixLauncher"); - - // parameters, separated from ParamBucket - private List libraries; - private List mcparams; - private List mods; - private List jarmods; - private List coremods; - private List traits; - private String appletClass; - private String mainClass; - private String nativePath; - private String userName, sessionId; - private String windowTitle; - private String windowParams; - - // secondary parameters - private int winSizeW; - private int winSizeH; - private boolean maximize; - private String cwd; - - private String serverAddress; - private String serverPort; - - // the much abused system classloader, for convenience (for further abuse) - private ClassLoader cl; - - private void processParams(ParamBucket params) throws NotFoundException - { - libraries = params.all("cp"); - mcparams = params.allSafe("param", new ArrayList() ); - mainClass = params.firstSafe("mainClass", "net.minecraft.client.Minecraft"); - appletClass = params.firstSafe("appletClass", "net.minecraft.client.MinecraftApplet"); - traits = params.allSafe("traits", new ArrayList()); - nativePath = params.first("natives"); - - userName = params.first("userName"); - sessionId = params.first("sessionId"); - windowTitle = params.firstSafe("windowTitle", "Minecraft"); - windowParams = params.firstSafe("windowParams", "854x480"); - - serverAddress = params.firstSafe("serverAddress", null); - serverPort = params.firstSafe("serverPort", null); - - cwd = System.getProperty("user.dir"); - - winSizeW = 854; - winSizeH = 480; - maximize = false; - - String[] dimStrings = windowParams.split("x"); - - if (windowParams.equalsIgnoreCase("max")) - { - maximize = true; - } - else if (dimStrings.length == 2) - { - try - { - winSizeW = Integer.parseInt(dimStrings[0]); - winSizeH = Integer.parseInt(dimStrings[1]); - } catch (NumberFormatException ignored) {} - } - } - - int legacyLaunch() - { - // Get the Minecraft Class and set the base folder - Class mc; - try - { - mc = cl.loadClass(mainClass); - - Field f = Utils.getMCPathField(mc); - - if (f == null) - { - LOGGER.warning("Could not find Minecraft path field."); - } - else - { - f.setAccessible(true); - f.set(null, new File(cwd)); - } - } catch (Exception e) - { - LOGGER.log( - Level.SEVERE, - "Could not set base folder. Failed to find/access Minecraft main class:", - e - ); - - return -1; - } - - System.setProperty("minecraft.applet.TargetDirectory", cwd); - - if(!traits.contains("noapplet")) - { - LOGGER.info("Launching with applet wrapper..."); - try - { - Class MCAppletClass = cl.loadClass(appletClass); - Applet mcappl = (Applet) MCAppletClass.newInstance(); - LegacyFrame mcWindow = new LegacyFrame(windowTitle); - mcWindow.start(mcappl, userName, sessionId, winSizeW, winSizeH, maximize, serverAddress, serverPort); - return 0; - } catch (Exception e) - { - LOGGER.log(Level.SEVERE, "Applet wrapper failed:", e); - - LOGGER.warning("Falling back to using main class."); - } - } - - // init params for the main method to chomp on. - String[] paramsArray = mcparams.toArray(new String[mcparams.size()]); - try - { - mc.getMethod("main", String[].class).invoke(null, (Object) paramsArray); - return 0; - } catch (Exception e) - { - LOGGER.log(Level.SEVERE, "Failed to invoke the Minecraft main class:", e); - - return -1; - } - } - - int launchWithMainClass() - { - // window size, title and state, onesix - if (maximize) - { - // FIXME: there is no good way to maximize the minecraft window in onesix. - // the following often breaks linux screen setups - // mcparams.add("--fullscreen"); - } - else - { - mcparams.add("--width"); - mcparams.add(Integer.toString(winSizeW)); - mcparams.add("--height"); - mcparams.add(Integer.toString(winSizeH)); - } - - if (serverAddress != null) - { - mcparams.add("--server"); - mcparams.add(serverAddress); - mcparams.add("--port"); - mcparams.add(serverPort); - } - - // Get the Minecraft Class. - Class mc; - try - { - mc = cl.loadClass(mainClass); - } catch (ClassNotFoundException e) - { - LOGGER.log(Level.SEVERE, "Failed to find Minecraft main class:", e); - - return -1; - } - - // get the main method. - Method meth; - try - { - meth = mc.getMethod("main", String[].class); - } catch (NoSuchMethodException e) - { - LOGGER.log(Level.SEVERE, "Failed to acquire the main method:", e); - - return -1; - } - - // init params for the main method to chomp on. - String[] paramsArray = mcparams.toArray(new String[mcparams.size()]); - try - { - // static method doesn't have an instance - meth.invoke(null, (Object) paramsArray); - } catch (Exception e) - { - LOGGER.log(Level.SEVERE, "Failed to start Minecraft:", e); - - return -1; - } - return 0; - } - - @Override - public int launch(ParamBucket params) - { - // get and process the launch script params - try - { - processParams(params); - } catch (NotFoundException e) - { - LOGGER.log(Level.SEVERE, "Not enough arguments!"); - - return -1; - } - - // grab the system classloader and ... - cl = ClassLoader.getSystemClassLoader(); - - if (traits.contains("legacyLaunch") || traits.contains("alphaLaunch") ) - { - // legacy launch uses the applet wrapper - return legacyLaunch(); - } - else - { - // normal launch just calls main() - return launchWithMainClass(); - } - } - -} diff --git a/libraries/launcher/org/multimc/ParamBucket.java b/libraries/launcher/org/multimc/utils/ParamBucket.java similarity index 68% rename from libraries/launcher/org/multimc/ParamBucket.java rename to libraries/launcher/org/multimc/utils/ParamBucket.java index 8ff03ddc..26ff8eef 100644 --- a/libraries/launcher/org/multimc/ParamBucket.java +++ b/libraries/launcher/org/multimc/utils/ParamBucket.java @@ -14,36 +14,34 @@ * limitations under the License. */ -package org.multimc; +package org.multimc.utils; + +import org.multimc.exception.ParameterNotFoundException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -public class ParamBucket -{ +public final class ParamBucket { private final Map> paramsMap = new HashMap<>(); - public void add(String key, String value) - { + public void add(String key, String value) { paramsMap.computeIfAbsent(key, k -> new ArrayList<>()) .add(value); } - public List all(String key) throws NotFoundException - { + public List all(String key) throws ParameterNotFoundException { List params = paramsMap.get(key); if (params == null) - throw new NotFoundException(); + throw new ParameterNotFoundException(key); return params; } - public List allSafe(String key, List def) - { + public List allSafe(String key, List def) { List params = paramsMap.get(key); if (params == null || params.isEmpty()) @@ -52,23 +50,16 @@ public class ParamBucket return params; } - public List allSafe(String key) - { - return allSafe(key, new ArrayList<>()); - } - - public String first(String key) throws NotFoundException - { + public String first(String key) throws ParameterNotFoundException { List list = all(key); if (list.isEmpty()) - throw new NotFoundException(); + throw new ParameterNotFoundException(key); return list.get(0); } - public String firstSafe(String key, String def) - { + public String firstSafe(String key, String def) { List params = paramsMap.get(key); if (params == null || params.isEmpty()) @@ -77,9 +68,4 @@ public class ParamBucket return params.get(0); } - public String firstSafe(String key) - { - return firstSafe(key, ""); - } - } diff --git a/libraries/launcher/org/multimc/utils/Utils.java b/libraries/launcher/org/multimc/utils/Utils.java new file mode 100644 index 00000000..416eff26 --- /dev/null +++ b/libraries/launcher/org/multimc/utils/Utils.java @@ -0,0 +1,49 @@ +/* + * Copyright 2012-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.multimc.utils; + +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +public final class Utils { + + private Utils() {} + + /** + * Finds a field that looks like a Minecraft base folder in a supplied class + * + * @param clazz the class to scan + */ + public static Field getMinecraftBaseDirField(Class clazz) { + for (Field f : clazz.getDeclaredFields()) { + // Has to be File + if (f.getType() != File.class) + continue; + + // And Private Static. + if (!Modifier.isStatic(f.getModifiers()) || !Modifier.isPrivate(f.getModifiers())) + continue; + + return f; + } + + return null; + } + +} + From eeb5297284494c03f3b8e3927c5ed6cc3ca09a41 Mon Sep 17 00:00:00 2001 From: icelimetea Date: Tue, 3 May 2022 00:25:26 +0100 Subject: [PATCH 02/67] Use only Java 7 features (in order to deal with #515) --- .../launcher/org/multimc/LauncherFactory.java | 23 +++++++++++++------ .../org/multimc/applet/LegacyFrame.java | 21 +++++++++-------- .../org/multimc/impl/OneSixLauncher.java | 4 ++-- .../org/multimc/utils/ParamBucket.java | 11 +++++++-- 4 files changed, 39 insertions(+), 20 deletions(-) diff --git a/libraries/launcher/org/multimc/LauncherFactory.java b/libraries/launcher/org/multimc/LauncherFactory.java index b5d0dd5b..2b370058 100644 --- a/libraries/launcher/org/multimc/LauncherFactory.java +++ b/libraries/launcher/org/multimc/LauncherFactory.java @@ -5,30 +5,39 @@ import org.multimc.utils.ParamBucket; import java.util.HashMap; import java.util.Map; -import java.util.function.Function; public final class LauncherFactory { private static final LauncherFactory INSTANCE = new LauncherFactory(); - private final Map> launcherRegistry = new HashMap<>(); + private final Map launcherRegistry = new HashMap<>(); private LauncherFactory() { - launcherRegistry.put("onesix", OneSixLauncher::new); + launcherRegistry.put("onesix", new LauncherProvider() { + @Override + public Launcher provide(ParamBucket parameters) { + return new OneSixLauncher(parameters); + } + }); } public Launcher createLauncher(String name, ParamBucket parameters) { - Function launcherCreator = - launcherRegistry.get(name); + LauncherProvider launcherProvider = launcherRegistry.get(name); - if (launcherCreator == null) + if (launcherProvider == null) throw new IllegalArgumentException("Invalid launcher type: " + name); - return launcherCreator.apply(parameters); + return launcherProvider.provide(parameters); } public static LauncherFactory getInstance() { return INSTANCE; } + public interface LauncherProvider { + + Launcher provide(ParamBucket parameters); + + } + } diff --git a/libraries/launcher/org/multimc/applet/LegacyFrame.java b/libraries/launcher/org/multimc/applet/LegacyFrame.java index a5e6c170..d250ce26 100644 --- a/libraries/launcher/org/multimc/applet/LegacyFrame.java +++ b/libraries/launcher/org/multimc/applet/LegacyFrame.java @@ -141,16 +141,19 @@ public final class LegacyFrame extends Frame { @Override public void windowClosing(WindowEvent e) { - new Thread(() -> { - try { - Thread.sleep(30000L); - } catch (InterruptedException localInterruptedException) { - localInterruptedException.printStackTrace(); + new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(30000L); + } catch (InterruptedException localInterruptedException) { + localInterruptedException.printStackTrace(); + } + + LOGGER.info("Forcing exit!"); + + System.exit(0); } - - LOGGER.info("Forcing exit!"); - - System.exit(0); }).start(); if (appletWrap != null) { diff --git a/libraries/launcher/org/multimc/impl/OneSixLauncher.java b/libraries/launcher/org/multimc/impl/OneSixLauncher.java index d2596a69..19253dc0 100644 --- a/libraries/launcher/org/multimc/impl/OneSixLauncher.java +++ b/libraries/launcher/org/multimc/impl/OneSixLauncher.java @@ -58,10 +58,10 @@ public final class OneSixLauncher implements Launcher { public OneSixLauncher(ParamBucket params) { classLoader = ClassLoader.getSystemClassLoader(); - mcParams = params.allSafe("param", Collections.emptyList()); + mcParams = params.allSafe("param", Collections.emptyList()); mainClass = params.firstSafe("mainClass", "net.minecraft.client.Minecraft"); appletClass = params.firstSafe("appletClass", "net.minecraft.client.MinecraftApplet"); - traits = params.allSafe("traits", Collections.emptyList()); + traits = params.allSafe("traits", Collections.emptyList()); userName = params.first("userName"); sessionId = params.first("sessionId"); diff --git a/libraries/launcher/org/multimc/utils/ParamBucket.java b/libraries/launcher/org/multimc/utils/ParamBucket.java index 26ff8eef..5dbb8775 100644 --- a/libraries/launcher/org/multimc/utils/ParamBucket.java +++ b/libraries/launcher/org/multimc/utils/ParamBucket.java @@ -28,8 +28,15 @@ public final class ParamBucket { private final Map> paramsMap = new HashMap<>(); public void add(String key, String value) { - paramsMap.computeIfAbsent(key, k -> new ArrayList<>()) - .add(value); + List params = paramsMap.get(key); + + if (params == null) { + params = new ArrayList<>(); + + paramsMap.put(key, params); + } + + params.add(value); } public List all(String key) throws ParameterNotFoundException { From 4fdb21b41400e789ca44a5cc1079469eb2508370 Mon Sep 17 00:00:00 2001 From: icelimetea Date: Tue, 3 May 2022 00:27:14 +0100 Subject: [PATCH 03/67] Compile with Java 7 in mind --- libraries/launcher/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/launcher/CMakeLists.txt b/libraries/launcher/CMakeLists.txt index e0149482..0a0a541c 100644 --- a/libraries/launcher/CMakeLists.txt +++ b/libraries/launcher/CMakeLists.txt @@ -4,7 +4,7 @@ find_package(Java 1.7 REQUIRED COMPONENTS Development) include(UseJava) set(CMAKE_JAVA_JAR_ENTRY_POINT org.multimc.EntryPoint) -set(CMAKE_JAVA_COMPILE_FLAGS -target 8 -source 8 -Xlint:deprecation -Xlint:unchecked) +set(CMAKE_JAVA_COMPILE_FLAGS -target 7 -source 7 -Xlint:deprecation -Xlint:unchecked) set(SRC org/multimc/EntryPoint.java From 860a7af6796785898926bcf10b034545caa5401b Mon Sep 17 00:00:00 2001 From: icelimetea Date: Tue, 3 May 2022 00:53:22 +0100 Subject: [PATCH 04/67] Fix method access modifier --- libraries/launcher/org/multimc/impl/OneSixLauncher.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/launcher/org/multimc/impl/OneSixLauncher.java b/libraries/launcher/org/multimc/impl/OneSixLauncher.java index 19253dc0..a87b116c 100644 --- a/libraries/launcher/org/multimc/impl/OneSixLauncher.java +++ b/libraries/launcher/org/multimc/impl/OneSixLauncher.java @@ -145,7 +145,7 @@ public final class OneSixLauncher implements Launcher { invokeMain(minecraftClass); } - void launchWithMainClass() throws Exception { + private void launchWithMainClass() throws Exception { // window size, title and state, onesix // FIXME: there is no good way to maximize the minecraft window in onesix. From 9a87ae575ef58bb86d4bbd7bdb8ab7e026ad9a33 Mon Sep 17 00:00:00 2001 From: icelimetea Date: Tue, 3 May 2022 03:19:26 +0100 Subject: [PATCH 05/67] More minor fixes --- libraries/launcher/CMakeLists.txt | 2 +- .../launcher/net/minecraft/Launcher.java | 30 ++++------------ .../launcher/org/multimc/EntryPoint.java | 4 +-- .../launcher/org/multimc/LauncherFactory.java | 8 ++--- .../org/multimc/applet/LegacyFrame.java | 25 +++++++------ .../org/multimc/exception/ParseException.java | 4 --- .../org/multimc/impl/OneSixLauncher.java | 36 +++++++++++-------- .../{ParamBucket.java => Parameters.java} | 2 +- 8 files changed, 47 insertions(+), 64 deletions(-) rename libraries/launcher/org/multimc/utils/{ParamBucket.java => Parameters.java} (98%) diff --git a/libraries/launcher/CMakeLists.txt b/libraries/launcher/CMakeLists.txt index 0a0a541c..2c859499 100644 --- a/libraries/launcher/CMakeLists.txt +++ b/libraries/launcher/CMakeLists.txt @@ -14,7 +14,7 @@ set(SRC org/multimc/applet/LegacyFrame.java org/multimc/exception/ParameterNotFoundException.java org/multimc/exception/ParseException.java - org/multimc/utils/ParamBucket.java + org/multimc/utils/Parameters.java org/multimc/utils/Utils.java net/minecraft/Launcher.java ) diff --git a/libraries/launcher/net/minecraft/Launcher.java b/libraries/launcher/net/minecraft/Launcher.java index 04201047..265fa66a 100644 --- a/libraries/launcher/net/minecraft/Launcher.java +++ b/libraries/launcher/net/minecraft/Launcher.java @@ -24,22 +24,20 @@ import java.net.URL; import java.util.Map; import java.util.TreeMap; -public class Launcher extends Applet implements AppletStub { +public final class Launcher extends Applet implements AppletStub { private final Map params = new TreeMap<>(); + private final Applet wrappedApplet; + private boolean active = false; - private Applet wrappedApplet; - private URL documentBase; - - public Launcher(Applet applet, URL documentBase) { + public Launcher(Applet applet) { this.setLayout(new BorderLayout()); this.add(applet, "Center"); this.wrappedApplet = applet; - this.documentBase = documentBase; } public void setParameter(String name, String value) @@ -47,21 +45,6 @@ public class Launcher extends Applet implements AppletStub { params.put(name, value); } - public void replace(Applet applet) { - this.wrappedApplet = applet; - - applet.setStub(this); - applet.setSize(getWidth(), getHeight()); - - this.setLayout(new BorderLayout()); - this.add(applet, "Center"); - - applet.init(); - active = true; - applet.start(); - validate(); - } - @Override public String getParameter(String name) { String param = params.get(name); @@ -135,9 +118,8 @@ public class Launcher extends Applet implements AppletStub { public URL getDocumentBase() { try { // Special case only for Classic versions - if (wrappedApplet.getClass().getCanonicalName().startsWith("com.mojang")) { - return new URL("http", "www.minecraft.net", 80, "/game/", null); - } + if (wrappedApplet.getClass().getCanonicalName().startsWith("com.mojang")) + return new URL("http", "www.minecraft.net", 80, "/game/"); return new URL("http://www.minecraft.net/game/"); } catch (MalformedURLException e) { diff --git a/libraries/launcher/org/multimc/EntryPoint.java b/libraries/launcher/org/multimc/EntryPoint.java index be06d1b4..416f2189 100644 --- a/libraries/launcher/org/multimc/EntryPoint.java +++ b/libraries/launcher/org/multimc/EntryPoint.java @@ -15,7 +15,7 @@ package org.multimc;/* */ import org.multimc.exception.ParseException; -import org.multimc.utils.ParamBucket; +import org.multimc.utils.Parameters; import java.io.BufferedReader; import java.io.IOException; @@ -28,7 +28,7 @@ public final class EntryPoint { private static final Logger LOGGER = Logger.getLogger("EntryPoint"); - private final ParamBucket params = new ParamBucket(); + private final Parameters params = new Parameters(); private String launcherType; diff --git a/libraries/launcher/org/multimc/LauncherFactory.java b/libraries/launcher/org/multimc/LauncherFactory.java index 2b370058..17e0d905 100644 --- a/libraries/launcher/org/multimc/LauncherFactory.java +++ b/libraries/launcher/org/multimc/LauncherFactory.java @@ -1,7 +1,7 @@ package org.multimc; import org.multimc.impl.OneSixLauncher; -import org.multimc.utils.ParamBucket; +import org.multimc.utils.Parameters; import java.util.HashMap; import java.util.Map; @@ -15,13 +15,13 @@ public final class LauncherFactory { private LauncherFactory() { launcherRegistry.put("onesix", new LauncherProvider() { @Override - public Launcher provide(ParamBucket parameters) { + public Launcher provide(Parameters parameters) { return new OneSixLauncher(parameters); } }); } - public Launcher createLauncher(String name, ParamBucket parameters) { + public Launcher createLauncher(String name, Parameters parameters) { LauncherProvider launcherProvider = launcherRegistry.get(name); if (launcherProvider == null) @@ -36,7 +36,7 @@ public final class LauncherFactory { public interface LauncherProvider { - Launcher provide(ParamBucket parameters); + Launcher provide(Parameters parameters); } diff --git a/libraries/launcher/org/multimc/applet/LegacyFrame.java b/libraries/launcher/org/multimc/applet/LegacyFrame.java index d250ce26..c50995f6 100644 --- a/libraries/launcher/org/multimc/applet/LegacyFrame.java +++ b/libraries/launcher/org/multimc/applet/LegacyFrame.java @@ -23,8 +23,6 @@ import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.File; import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -38,11 +36,15 @@ public final class LegacyFrame extends Frame { private static final Logger LOGGER = Logger.getLogger("LegacyFrame"); - private Launcher appletWrap; + private final Launcher appletWrap; - public LegacyFrame(String title) { + public LegacyFrame(String title, Applet mcApplet) { super(title); + appletWrap = new Launcher(mcApplet); + + mcApplet.setStub(appletWrap); + try { setIconImage(ImageIO.read(new File("icon.png"))); } catch (IOException e) { @@ -53,7 +55,6 @@ public final class LegacyFrame extends Frame { } public void start ( - Applet mcApplet, String user, String session, int winSizeW, @@ -62,14 +63,14 @@ public final class LegacyFrame extends Frame { String serverAddress, String serverPort ) { - try { - appletWrap = new Launcher(mcApplet, new URL("http://www.minecraft.net/game")); - } catch (MalformedURLException ignored) {} - // Implements support for launching in to multiplayer on classic servers using a mpticket // file generated by an external program and stored in the instance's root folder. - Path mpticketFile = Paths.get(System.getProperty("user.dir") + "/../mpticket"); - Path mpticketFileCorrupt = Paths.get(System.getProperty("user.dir") + "/../mpticket.corrupt"); + + Path mpticketFile = + Paths.get(System.getProperty("user.dir"), "..", "mpticket"); + + Path mpticketFileCorrupt = + Paths.get(System.getProperty("user.dir"), "..", "mpticket.corrupt"); if (Files.exists(mpticketFile)) { try (Scanner fileScanner = new Scanner( @@ -115,8 +116,6 @@ public final class LegacyFrame extends Frame { appletWrap.setParameter("demo", "false"); appletWrap.setParameter("fullscreen", "false"); - mcApplet.setStub(appletWrap); - add(appletWrap); appletWrap.setPreferredSize(new Dimension(winSizeW, winSizeH)); diff --git a/libraries/launcher/org/multimc/exception/ParseException.java b/libraries/launcher/org/multimc/exception/ParseException.java index c9a4c856..848b395d 100644 --- a/libraries/launcher/org/multimc/exception/ParseException.java +++ b/libraries/launcher/org/multimc/exception/ParseException.java @@ -18,10 +18,6 @@ package org.multimc.exception; public final class ParseException extends IllegalArgumentException { - public ParseException() { - super(); - } - public ParseException(String message) { super(message); } diff --git a/libraries/launcher/org/multimc/impl/OneSixLauncher.java b/libraries/launcher/org/multimc/impl/OneSixLauncher.java index a87b116c..b981e4ff 100644 --- a/libraries/launcher/org/multimc/impl/OneSixLauncher.java +++ b/libraries/launcher/org/multimc/impl/OneSixLauncher.java @@ -17,7 +17,7 @@ package org.multimc.impl; import org.multimc.Launcher; import org.multimc.applet.LegacyFrame; -import org.multimc.utils.ParamBucket; +import org.multimc.utils.Parameters; import org.multimc.utils.Utils; import java.applet.Applet; @@ -55,7 +55,7 @@ public final class OneSixLauncher implements Launcher { private final ClassLoader classLoader; - public OneSixLauncher(ParamBucket params) { + public OneSixLauncher(Parameters params) { classLoader = ClassLoader.getSystemClassLoader(); mcParams = params.allSafe("param", Collections.emptyList()); @@ -72,22 +72,29 @@ public final class OneSixLauncher implements Launcher { cwd = System.getProperty("user.dir"); - String windowParams = params.firstSafe("windowParams", "854x480"); + String windowParams = params.firstSafe("windowParams", null); - String[] dimStrings = windowParams.split("x"); + if (windowParams != null) { + String[] dimStrings = windowParams.split("x"); - if (windowParams.equalsIgnoreCase("max")) { - maximize = true; + if (windowParams.equalsIgnoreCase("max")) { + maximize = true; + + winSizeW = DEFAULT_WINDOW_WIDTH; + winSizeH = DEFAULT_WINDOW_HEIGHT; + } else if (dimStrings.length == 2) { + maximize = false; + + winSizeW = Integer.parseInt(dimStrings[0]); + winSizeH = Integer.parseInt(dimStrings[1]); + } else { + throw new IllegalArgumentException("Unexpected window size parameter value: " + windowParams); + } + } else { + maximize = false; winSizeW = DEFAULT_WINDOW_WIDTH; winSizeH = DEFAULT_WINDOW_HEIGHT; - } else if (dimStrings.length == 2) { - maximize = false; - - winSizeW = Integer.parseInt(dimStrings[0]); - winSizeH = Integer.parseInt(dimStrings[1]); - } else { - throw new IllegalArgumentException("Unexpected window size parameter value: " + windowParams); } } @@ -121,10 +128,9 @@ public final class OneSixLauncher implements Launcher { Applet mcApplet = (Applet) mcAppletClass.getConstructor().newInstance(); - LegacyFrame mcWindow = new LegacyFrame(windowTitle); + LegacyFrame mcWindow = new LegacyFrame(windowTitle, mcApplet); mcWindow.start( - mcApplet, userName, sessionId, winSizeW, diff --git a/libraries/launcher/org/multimc/utils/ParamBucket.java b/libraries/launcher/org/multimc/utils/Parameters.java similarity index 98% rename from libraries/launcher/org/multimc/utils/ParamBucket.java rename to libraries/launcher/org/multimc/utils/Parameters.java index 5dbb8775..7be790c2 100644 --- a/libraries/launcher/org/multimc/utils/ParamBucket.java +++ b/libraries/launcher/org/multimc/utils/Parameters.java @@ -23,7 +23,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -public final class ParamBucket { +public final class Parameters { private final Map> paramsMap = new HashMap<>(); From dcc41ef885cbb2a823313a95e48d069c63589a42 Mon Sep 17 00:00:00 2001 From: icelimetea Date: Thu, 5 May 2022 07:14:32 +0100 Subject: [PATCH 06/67] Improve mpticket file parsing code --- .../org/multimc/applet/LegacyFrame.java | 42 +++++++------------ 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/libraries/launcher/org/multimc/applet/LegacyFrame.java b/libraries/launcher/org/multimc/applet/LegacyFrame.java index c50995f6..e3bd5047 100644 --- a/libraries/launcher/org/multimc/applet/LegacyFrame.java +++ b/libraries/launcher/org/multimc/applet/LegacyFrame.java @@ -28,7 +28,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; -import java.util.Scanner; +import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -51,7 +51,7 @@ public final class LegacyFrame extends Frame { LOGGER.log(Level.WARNING, "Unable to read Minecraft icon!", e); } - this.addWindowListener(new ForceExitHandler()); + addWindowListener(new ForceExitHandler()); } public void start ( @@ -73,34 +73,24 @@ public final class LegacyFrame extends Frame { Paths.get(System.getProperty("user.dir"), "..", "mpticket.corrupt"); if (Files.exists(mpticketFile)) { - try (Scanner fileScanner = new Scanner( - Files.newInputStream(mpticketFile), - StandardCharsets.US_ASCII.name() - )) { - String[] mpticketParams = new String[3]; + try { + List lines = Files.readAllLines(mpticketFile, StandardCharsets.UTF_8); - for (int i = 0; i < mpticketParams.length; i++) { - if (fileScanner.hasNextLine()) { - mpticketParams[i] = fileScanner.nextLine(); - } else { - Files.move( - mpticketFile, - mpticketFileCorrupt, - StandardCopyOption.REPLACE_EXISTING - ); + if (lines.size() != 3) { + Files.move( + mpticketFile, + mpticketFileCorrupt, + StandardCopyOption.REPLACE_EXISTING + ); - throw new IllegalArgumentException("Mpticket file is corrupted!"); - } + LOGGER.warning("Mpticket file is corrupted!"); + } else { + appletWrap.setParameter("server", lines.get(0)); + appletWrap.setParameter("port", lines.get(1)); + appletWrap.setParameter("mppass", lines.get(2)); } - - Files.delete(mpticketFile); - - // Assumes parameters are valid and in the correct order - appletWrap.setParameter("server", mpticketParams[0]); - appletWrap.setParameter("port", mpticketParams[1]); - appletWrap.setParameter("mppass", mpticketParams[2]); } catch (IOException e) { - LOGGER.log(Level.WARNING, "Unable to read mpticket file!", e); + LOGGER.log(Level.WARNING, "Unable to red mpticket file!", e); } } From 6bffa060637e3620739344925a4681ec494a725b Mon Sep 17 00:00:00 2001 From: icelimetea Date: Thu, 5 May 2022 07:16:16 +0100 Subject: [PATCH 07/67] Fix typo --- libraries/launcher/org/multimc/applet/LegacyFrame.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/launcher/org/multimc/applet/LegacyFrame.java b/libraries/launcher/org/multimc/applet/LegacyFrame.java index e3bd5047..0283f92c 100644 --- a/libraries/launcher/org/multimc/applet/LegacyFrame.java +++ b/libraries/launcher/org/multimc/applet/LegacyFrame.java @@ -85,12 +85,13 @@ public final class LegacyFrame extends Frame { LOGGER.warning("Mpticket file is corrupted!"); } else { + // Assumes parameters are valid and in the correct order appletWrap.setParameter("server", lines.get(0)); appletWrap.setParameter("port", lines.get(1)); appletWrap.setParameter("mppass", lines.get(2)); } } catch (IOException e) { - LOGGER.log(Level.WARNING, "Unable to red mpticket file!", e); + LOGGER.log(Level.WARNING, "Unable to read mpticket file!", e); } } From 113528e1f299de951a7223df033bbf390095dba3 Mon Sep 17 00:00:00 2001 From: icelimetea Date: Thu, 5 May 2022 07:20:33 +0100 Subject: [PATCH 08/67] Make line count check more lenient --- libraries/launcher/org/multimc/applet/LegacyFrame.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/launcher/org/multimc/applet/LegacyFrame.java b/libraries/launcher/org/multimc/applet/LegacyFrame.java index 0283f92c..f82cb605 100644 --- a/libraries/launcher/org/multimc/applet/LegacyFrame.java +++ b/libraries/launcher/org/multimc/applet/LegacyFrame.java @@ -76,7 +76,7 @@ public final class LegacyFrame extends Frame { try { List lines = Files.readAllLines(mpticketFile, StandardCharsets.UTF_8); - if (lines.size() != 3) { + if (lines.size() < 3) { Files.move( mpticketFile, mpticketFileCorrupt, From 8c8eabf7ac1920b47792b26790f3646cb6693ec0 Mon Sep 17 00:00:00 2001 From: flow Date: Thu, 21 Apr 2022 22:12:14 -0300 Subject: [PATCH 09/67] refactor: organize a little more the code in launcher/net/ This also reduces some code duplication by using some Task logic in NetAction. --- launcher/InstanceImportTask.cpp | 14 +- launcher/minecraft/AssetsUtils.cpp | 2 +- launcher/net/ByteArraySink.h | 67 +++-- launcher/net/Download.cpp | 60 ++--- launcher/net/Download.h | 14 +- launcher/net/FileSink.cpp | 50 ++-- launcher/net/FileSink.h | 36 +-- launcher/net/MetaCacheSink.cpp | 22 +- launcher/net/MetaCacheSink.h | 27 +- launcher/net/NetAction.h | 127 ++++----- launcher/net/NetJob.cpp | 274 ++++++++++---------- launcher/net/NetJob.h | 109 ++++---- launcher/net/Sink.h | 54 ++-- launcher/screenshots/ImgurAlbumCreation.cpp | 15 +- launcher/screenshots/ImgurAlbumCreation.h | 12 +- launcher/screenshots/ImgurUpload.cpp | 13 +- launcher/screenshots/ImgurUpload.h | 2 +- launcher/tasks/Task.h | 4 +- launcher/translations/TranslationsModel.cpp | 2 +- 19 files changed, 435 insertions(+), 469 deletions(-) diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp index 1a13c997..fc3432c1 100644 --- a/launcher/InstanceImportTask.cpp +++ b/launcher/InstanceImportTask.cpp @@ -14,27 +14,25 @@ */ #include "InstanceImportTask.h" +#include +#include "Application.h" #include "BaseInstance.h" #include "FileSystem.h" -#include "Application.h" #include "MMCZip.h" #include "NullInstance.h" -#include "settings/INISettingsObject.h" +#include "icons/IconList.h" #include "icons/IconUtils.h" -#include +#include "settings/INISettingsObject.h" // FIXME: this does not belong here, it's Minecraft/Flame specific +#include +#include "Json.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" #include "modplatform/flame/FileResolvingTask.h" #include "modplatform/flame/PackManifest.h" -#include "Json.h" -#include #include "modplatform/technic/TechnicPackProcessor.h" -#include "icons/IconList.h" -#include "Application.h" - InstanceImportTask::InstanceImportTask(const QUrl sourceUrl) { m_sourceUrl = sourceUrl; diff --git a/launcher/minecraft/AssetsUtils.cpp b/launcher/minecraft/AssetsUtils.cpp index 7290aeb4..281f730f 100644 --- a/launcher/minecraft/AssetsUtils.cpp +++ b/launcher/minecraft/AssetsUtils.cpp @@ -297,7 +297,7 @@ NetAction::Ptr AssetObject::getDownloadAction() auto rawHash = QByteArray::fromHex(hash.toLatin1()); objectDL->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawHash)); } - objectDL->m_total_progress = size; + objectDL->setProgress(objectDL->getProgress(), size); return objectDL; } return nullptr; diff --git a/launcher/net/ByteArraySink.h b/launcher/net/ByteArraySink.h index 20e6764c..75a66574 100644 --- a/launcher/net/ByteArraySink.h +++ b/launcher/net/ByteArraySink.h @@ -3,60 +3,59 @@ #include "Sink.h" namespace Net { + /* * Sink object for downloads that uses an external QByteArray it doesn't own as a target. */ -class ByteArraySink : public Sink -{ -public: - ByteArraySink(QByteArray *output) - :m_output(output) - { - // nil - }; +class ByteArraySink : public Sink { + public: + ByteArraySink(QByteArray* output) : m_output(output){}; - virtual ~ByteArraySink() - { - // nil - } + virtual ~ByteArraySink() = default; -public: - JobStatus init(QNetworkRequest & request) override + public: + auto init(QNetworkRequest& request) -> Task::State override { + if(!m_output) + return Task::State::Failed; + m_output->clear(); - if(initAllValidators(request)) - return Job_InProgress; - return Job_Failed; + if (initAllValidators(request)) + return Task::State::Running; + return Task::State::Failed; }; - JobStatus write(QByteArray & data) override + auto write(QByteArray& data) -> Task::State override { + if(!m_output) + return Task::State::Failed; + m_output->append(data); - if(writeAllValidators(data)) - return Job_InProgress; - return Job_Failed; + if (writeAllValidators(data)) + return Task::State::Running; + return Task::State::Failed; } - JobStatus abort() override + auto abort() -> Task::State override { + if(!m_output) + return Task::State::Failed; + m_output->clear(); failAllValidators(); - return Job_Failed; + return Task::State::Failed; } - JobStatus finalize(QNetworkReply &reply) override + auto finalize(QNetworkReply& reply) -> Task::State override { - if(finalizeAllValidators(reply)) - return Job_Finished; - return Job_Failed; + if (finalizeAllValidators(reply)) + return Task::State::Succeeded; + return Task::State::Failed; } - bool hasLocalData() override - { - return false; - } + auto hasLocalData() -> bool override { return false; } -private: - QByteArray * m_output; + private: + QByteArray* m_output; }; -} +} // namespace Net diff --git a/launcher/net/Download.cpp b/launcher/net/Download.cpp index 65cc8f67..5b5a04db 100644 --- a/launcher/net/Download.cpp +++ b/launcher/net/Download.cpp @@ -30,7 +30,7 @@ namespace Net { Download::Download() : NetAction() { - m_status = Job_NotStarted; + m_state = State::Inactive; } Download::Ptr Download::makeCached(QUrl url, MetaEntryPtr entry, Options options) @@ -68,29 +68,29 @@ void Download::addValidator(Validator* v) m_sink->addValidator(v); } -void Download::startImpl() +void Download::executeTask() { - if (m_status == Job_Aborted) { + if (getState() == Task::State::AbortedByUser) { qWarning() << "Attempt to start an aborted Download:" << m_url.toString(); emit aborted(m_index_within_job); return; } + QNetworkRequest request(m_url); - m_status = m_sink->init(request); - switch (m_status) { - case Job_Finished: + m_state = m_sink->init(request); + switch (m_state) { + case State::Succeeded: emit succeeded(m_index_within_job); qDebug() << "Download cache hit " << m_url.toString(); return; - case Job_InProgress: + case State::Running: qDebug() << "Downloading " << m_url.toString(); break; - case Job_Failed_Proceed: // this is meaningless in this context. We do need a sink. - case Job_NotStarted: - case Job_Failed: + case State::Inactive: + case State::Failed: emit failed(m_index_within_job); return; - case Job_Aborted: + case State::AbortedByUser: return; } @@ -111,8 +111,7 @@ void Download::startImpl() void Download::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) { - m_total_progress = bytesTotal; - m_progress = bytesReceived; + setProgress(bytesReceived, bytesTotal); emit netActionProgress(m_index_within_job, bytesReceived, bytesTotal); } @@ -120,17 +119,17 @@ void Download::downloadError(QNetworkReply::NetworkError error) { if (error == QNetworkReply::OperationCanceledError) { qCritical() << "Aborted " << m_url.toString(); - m_status = Job_Aborted; + m_state = State::AbortedByUser; } else { if (m_options & Option::AcceptLocalFiles) { if (m_sink->hasLocalData()) { - m_status = Job_Failed_Proceed; + m_state = State::Succeeded; return; } } // error happened during download. qCritical() << "Failed " << m_url.toString() << " with reason " << error; - m_status = Job_Failed; + m_state = State::Failed; } } @@ -194,7 +193,8 @@ bool Download::handleRedirect() m_url = QUrl(redirect.toString()); qDebug() << "Following redirect to " << m_url.toString(); - start(m_network); + startAction(m_network); + return true; } @@ -207,19 +207,20 @@ void Download::downloadFinished() } // if the download failed before this point ... - if (m_status == Job_Failed_Proceed) { + if (m_state == State::Succeeded) // pretend to succeed so we continue processing :) + { qDebug() << "Download failed but we are allowed to proceed:" << m_url.toString(); m_sink->abort(); m_reply.reset(); emit succeeded(m_index_within_job); return; - } else if (m_status == Job_Failed) { + } else if (m_state == State::Failed) { qDebug() << "Download failed in previous step:" << m_url.toString(); m_sink->abort(); m_reply.reset(); emit failed(m_index_within_job); return; - } else if (m_status == Job_Aborted) { + } else if (m_state == State::AbortedByUser) { qDebug() << "Download aborted in previous step:" << m_url.toString(); m_sink->abort(); m_reply.reset(); @@ -231,12 +232,12 @@ void Download::downloadFinished() auto data = m_reply->readAll(); if (data.size()) { qDebug() << "Writing extra" << data.size() << "bytes to" << m_target_path; - m_status = m_sink->write(data); + m_state = m_sink->write(data); } // otherwise, finalize the whole graph - m_status = m_sink->finalize(*m_reply.get()); - if (m_status != Job_Finished) { + m_state = m_sink->finalize(*m_reply.get()); + if (m_state != State::Succeeded) { qDebug() << "Download failed to finalize:" << m_url.toString(); m_sink->abort(); m_reply.reset(); @@ -250,10 +251,10 @@ void Download::downloadFinished() void Download::downloadReadyRead() { - if (m_status == Job_InProgress) { + if (m_state == State::Running) { auto data = m_reply->readAll(); - m_status = m_sink->write(data); - if (m_status == Job_Failed) { + m_state = m_sink->write(data); + if (m_state == State::Failed) { qCritical() << "Failed to process response chunk for " << m_target_path; } // qDebug() << "Download" << m_url.toString() << "gained" << data.size() << "bytes"; @@ -269,12 +270,7 @@ bool Net::Download::abort() if (m_reply) { m_reply->abort(); } else { - m_status = Job_Aborted; + m_state = State::AbortedByUser; } return true; } - -bool Net::Download::canAbort() -{ - return true; -} diff --git a/launcher/net/Download.h b/launcher/net/Download.h index 0f9bfe7f..231ad6a7 100644 --- a/launcher/net/Download.h +++ b/launcher/net/Download.h @@ -27,7 +27,7 @@ class Download : public NetAction { Q_OBJECT -public: /* types */ +public: typedef shared_qobject_ptr Ptr; enum class Option { @@ -36,7 +36,7 @@ public: /* types */ }; Q_DECLARE_FLAGS(Options, Option) -protected: /* con/des */ +protected: explicit Download(); public: virtual ~Download(){}; @@ -44,16 +44,16 @@ public: static Download::Ptr makeByteArray(QUrl url, QByteArray *output, Options options = Option::NoOptions); static Download::Ptr makeFile(QUrl url, QString path, Options options = Option::NoOptions); -public: /* methods */ +public: QString getTargetFilepath() { return m_target_path; } void addValidator(Validator * v); bool abort() override; - bool canAbort() override; + bool canAbort() const override { return true; }; -private: /* methods */ +private: bool handleRedirect(); protected slots: @@ -64,9 +64,9 @@ protected slots: void downloadReadyRead() override; public slots: - void startImpl() override; + void executeTask() override; -private: /* data */ +private: // FIXME: remove this, it has no business being here. QString m_target_path; std::unique_ptr m_sink; diff --git a/launcher/net/FileSink.cpp b/launcher/net/FileSink.cpp index 7e9b8929..0d8b09bb 100644 --- a/launcher/net/FileSink.cpp +++ b/launcher/net/FileSink.cpp @@ -1,25 +1,15 @@ #include "FileSink.h" + #include -#include + #include "FileSystem.h" namespace Net { -FileSink::FileSink(QString filename) - :m_filename(filename) -{ - // nil -} - -FileSink::~FileSink() -{ - // nil -} - -JobStatus FileSink::init(QNetworkRequest& request) +Task::State FileSink::init(QNetworkRequest& request) { auto result = initCache(request); - if(result != Job_InProgress) + if(result != Task::State::Running) { return result; } @@ -27,27 +17,27 @@ JobStatus FileSink::init(QNetworkRequest& request) if (!FS::ensureFilePathExists(m_filename)) { qCritical() << "Could not create folder for " + m_filename; - return Job_Failed; + return Task::State::Failed; } wroteAnyData = false; m_output_file.reset(new QSaveFile(m_filename)); if (!m_output_file->open(QIODevice::WriteOnly)) { qCritical() << "Could not open " + m_filename + " for writing"; - return Job_Failed; + return Task::State::Failed; } if(initAllValidators(request)) - return Job_InProgress; - return Job_Failed; + return Task::State::Running; + return Task::State::Failed; } -JobStatus FileSink::initCache(QNetworkRequest &) +Task::State FileSink::initCache(QNetworkRequest &) { - return Job_InProgress; + return Task::State::Running; } -JobStatus FileSink::write(QByteArray& data) +Task::State FileSink::write(QByteArray& data) { if (!writeAllValidators(data) || m_output_file->write(data) != data.size()) { @@ -55,20 +45,20 @@ JobStatus FileSink::write(QByteArray& data) m_output_file->cancelWriting(); m_output_file.reset(); wroteAnyData = false; - return Job_Failed; + return Task::State::Failed; } wroteAnyData = true; - return Job_InProgress; + return Task::State::Running; } -JobStatus FileSink::abort() +Task::State FileSink::abort() { m_output_file->cancelWriting(); failAllValidators(); - return Job_Failed; + return Task::State::Failed; } -JobStatus FileSink::finalize(QNetworkReply& reply) +Task::State FileSink::finalize(QNetworkReply& reply) { bool gotFile = false; QVariant statusCodeV = reply.attribute(QNetworkRequest::HttpStatusCodeAttribute); @@ -86,13 +76,13 @@ JobStatus FileSink::finalize(QNetworkReply& reply) // ask validators for data consistency // we only do this for actual downloads, not 'your data is still the same' cache hits if(!finalizeAllValidators(reply)) - return Job_Failed; + return Task::State::Failed; // nothing went wrong... if (!m_output_file->commit()) { qCritical() << "Failed to commit changes to " << m_filename; m_output_file->cancelWriting(); - return Job_Failed; + return Task::State::Failed; } } // then get rid of the save file @@ -101,9 +91,9 @@ JobStatus FileSink::finalize(QNetworkReply& reply) return finalizeCache(reply); } -JobStatus FileSink::finalizeCache(QNetworkReply &) +Task::State FileSink::finalizeCache(QNetworkReply &) { - return Job_Finished; + return Task::State::Succeeded; } bool FileSink::hasLocalData() diff --git a/launcher/net/FileSink.h b/launcher/net/FileSink.h index 875fe511..9d77b3d0 100644 --- a/launcher/net/FileSink.h +++ b/launcher/net/FileSink.h @@ -1,28 +1,30 @@ #pragma once -#include "Sink.h" + #include +#include "Sink.h" + namespace Net { -class FileSink : public Sink -{ -public: /* con/des */ - FileSink(QString filename); - virtual ~FileSink(); +class FileSink : public Sink { + public: + FileSink(QString filename) : m_filename(filename){}; + virtual ~FileSink() = default; -public: /* methods */ - JobStatus init(QNetworkRequest & request) override; - JobStatus write(QByteArray & data) override; - JobStatus abort() override; - JobStatus finalize(QNetworkReply & reply) override; - bool hasLocalData() 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; -protected: /* methods */ - virtual JobStatus initCache(QNetworkRequest &); - virtual JobStatus finalizeCache(QNetworkReply &reply); + auto hasLocalData() -> bool override; -protected: /* data */ + protected: + virtual auto initCache(QNetworkRequest&) -> Task::State; + virtual auto finalizeCache(QNetworkReply& reply) -> Task::State; + + protected: QString m_filename; bool wroteAnyData = false; std::unique_ptr m_output_file; }; -} +} // namespace Net diff --git a/launcher/net/MetaCacheSink.cpp b/launcher/net/MetaCacheSink.cpp index 5cdf0460..34ba9f56 100644 --- a/launcher/net/MetaCacheSink.cpp +++ b/launcher/net/MetaCacheSink.cpp @@ -12,17 +12,13 @@ MetaCacheSink::MetaCacheSink(MetaEntryPtr entry, ChecksumValidator * md5sum) addValidator(md5sum); } -MetaCacheSink::~MetaCacheSink() -{ - // nil -} - -JobStatus MetaCacheSink::initCache(QNetworkRequest& request) +Task::State MetaCacheSink::initCache(QNetworkRequest& request) { if (!m_entry->isStale()) { - return Job_Finished; + return Task::State::Succeeded; } + // check if file exists, if it does, use its information for the request QFile current(m_filename); if(current.exists() && current.size() != 0) @@ -36,25 +32,31 @@ JobStatus MetaCacheSink::initCache(QNetworkRequest& request) request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->getETag().toLatin1()); } } - return Job_InProgress; + + return Task::State::Running; } -JobStatus MetaCacheSink::finalizeCache(QNetworkReply & reply) +Task::State MetaCacheSink::finalizeCache(QNetworkReply & reply) { QFileInfo output_file_info(m_filename); + if(wroteAnyData) { m_entry->setMD5Sum(m_md5Node->hash().toHex().constData()); } + m_entry->setETag(reply.rawHeader("ETag").constData()); + if (reply.hasRawHeader("Last-Modified")) { m_entry->setRemoteChangedTimestamp(reply.rawHeader("Last-Modified").constData()); } + m_entry->setLocalChangedTimestamp(output_file_info.lastModified().toUTC().toMSecsSinceEpoch()); m_entry->setStale(false); APPLICATION->metacache()->updateEntry(m_entry); - return Job_Finished; + + return Task::State::Succeeded; } bool MetaCacheSink::hasLocalData() diff --git a/launcher/net/MetaCacheSink.h b/launcher/net/MetaCacheSink.h index edcf7ad1..431e10a8 100644 --- a/launcher/net/MetaCacheSink.h +++ b/launcher/net/MetaCacheSink.h @@ -1,22 +1,23 @@ #pragma once -#include "FileSink.h" + #include "ChecksumValidator.h" +#include "FileSink.h" #include "net/HttpMetaCache.h" namespace Net { -class MetaCacheSink : public FileSink -{ -public: /* con/des */ - MetaCacheSink(MetaEntryPtr entry, ChecksumValidator * md5sum); - virtual ~MetaCacheSink(); - bool hasLocalData() override; +class MetaCacheSink : public FileSink { + public: + MetaCacheSink(MetaEntryPtr entry, ChecksumValidator* md5sum); + virtual ~MetaCacheSink() = default; -protected: /* methods */ - JobStatus initCache(QNetworkRequest & request) override; - JobStatus finalizeCache(QNetworkReply & reply) override; + auto hasLocalData() -> bool override; -private: /* data */ + protected: + auto initCache(QNetworkRequest& request) -> Task::State override; + auto finalizeCache(QNetworkReply& reply) -> Task::State override; + + private: MetaEntryPtr m_entry; - ChecksumValidator * m_md5Node; + ChecksumValidator* m_md5Node; }; -} +} // namespace Net diff --git a/launcher/net/NetAction.h b/launcher/net/NetAction.h index efb20953..e15716f6 100644 --- a/launcher/net/NetAction.h +++ b/launcher/net/NetAction.h @@ -1,108 +1,81 @@ -/* Copyright 2013-2021 MultiMC Contributors +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher * - * 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 -#include -#include -#include #include -#include +#include -enum JobStatus -{ - Job_NotStarted, - Job_InProgress, - Job_Finished, - Job_Failed, - Job_Aborted, - /* - * FIXME: @NUKE this confuses the task failing with us having a fallback in the form of local data. Clear up the confusion. - * Same could be true for aborted task - the presence of pre-existing result is a separate concern - */ - Job_Failed_Proceed -}; +#include "QObjectPtr.h" +#include "tasks/Task.h" -class NetAction : public QObject -{ +class NetAction : public Task { Q_OBJECT -protected: - explicit NetAction() : QObject(nullptr) {}; + protected: + explicit NetAction() : Task(nullptr) {}; -public: + public: using Ptr = shared_qobject_ptr; - virtual ~NetAction() {}; + virtual ~NetAction() = default; - bool isRunning() const - { - return m_status == Job_InProgress; - } - bool isFinished() const - { - return m_status >= Job_Finished; - } - bool wasSuccessful() const - { - return m_status == Job_Finished || m_status == Job_Failed_Proceed; - } + QUrl url() { return m_url; } - qint64 totalProgress() const - { - return m_total_progress; - } - qint64 currentProgress() const - { - return m_progress; - } - virtual bool abort() - { - return false; - } - virtual bool canAbort() - { - return false; - } - QUrl url() - { - return m_url; - } - -signals: + signals: void started(int index); void netActionProgress(int index, qint64 current, qint64 total); void succeeded(int index); void failed(int index); void aborted(int index); -protected slots: + 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; -public slots: - void start(shared_qobject_ptr network) { + public slots: + void startAction(shared_qobject_ptr network) + { m_network = network; - startImpl(); + executeTask(); } -protected: - virtual void startImpl() = 0; + protected: + void executeTask() override {}; -public: + public: shared_qobject_ptr m_network; /// index within the parent job, FIXME: nuke @@ -113,10 +86,4 @@ public: /// source URL QUrl m_url; - - qint64 m_progress = 0; - qint64 m_total_progress = 1; - -protected: - JobStatus m_status = Job_NotStarted; }; diff --git a/launcher/net/NetJob.cpp b/launcher/net/NetJob.cpp index 9bad89ed..d08d6c4d 100644 --- a/launcher/net/NetJob.cpp +++ b/launcher/net/NetJob.cpp @@ -1,79 +1,170 @@ -/* Copyright 2013-2021 MultiMC Contributors +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher * - * 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. */ #include "NetJob.h" #include "Download.h" -#include +auto NetJob::addNetAction(NetAction::Ptr action) -> bool +{ + action->m_index_within_job = m_downloads.size(); + m_downloads.append(action); + part_info pi; + m_parts_progress.append(pi); + + partProgress(m_parts_progress.count() - 1, action->getProgress(), action->getTotalProgress()); + + if (action->isRunning()) { + connect(action.get(), &NetAction::succeeded, this, &NetJob::partSucceeded); + connect(action.get(), &NetAction::failed, this, &NetJob::partFailed); + connect(action.get(), &NetAction::netActionProgress, this, &NetJob::partProgress); + } else { + m_todo.append(m_parts_progress.size() - 1); + } + + return true; +} + +auto NetJob::canAbort() const -> bool +{ + bool canFullyAbort = true; + + // can abort the downloads on the queue? + for (auto index : m_todo) { + auto part = m_downloads[index]; + canFullyAbort &= part->canAbort(); + } + // can abort the active downloads? + for (auto index : m_doing) { + auto part = m_downloads[index]; + canFullyAbort &= part->canAbort(); + } + + return canFullyAbort; +} + +void NetJob::executeTask() +{ + // hack that delays early failures so they can be caught easier + QMetaObject::invokeMethod(this, "startMoreParts", Qt::QueuedConnection); +} + +auto NetJob::getFailedFiles() -> QStringList +{ + QStringList failed; + for (auto index : m_failed) { + failed.push_back(m_downloads[index]->url().toString()); + } + failed.sort(); + return failed; +} + +auto NetJob::abort() -> bool +{ + bool fullyAborted = true; + + // fail all downloads on the queue + m_failed.unite(m_todo.toSet()); + m_todo.clear(); + + // abort active downloads + auto toKill = m_doing.toList(); + for (auto index : toKill) { + auto part = m_downloads[index]; + fullyAborted &= part->abort(); + } + + return fullyAborted; +} void NetJob::partSucceeded(int index) { // do progress. all slots are 1 in size at least - auto &slot = parts_progress[index]; + auto& slot = m_parts_progress[index]; partProgress(index, slot.total_progress, slot.total_progress); m_doing.remove(index); m_done.insert(index); - downloads[index].get()->disconnect(this); + m_downloads[index].get()->disconnect(this); + startMoreParts(); } void NetJob::partFailed(int index) { m_doing.remove(index); - auto &slot = parts_progress[index]; - if (slot.failures == 3) - { + + auto& slot = m_parts_progress[index]; + // Can try 3 times before failing by definitive + if (slot.failures == 3) { m_failed.insert(index); - } - else - { + } else { slot.failures++; m_todo.enqueue(index); } - downloads[index].get()->disconnect(this); + + m_downloads[index].get()->disconnect(this); + startMoreParts(); } void NetJob::partAborted(int index) { m_aborted = true; + m_doing.remove(index); m_failed.insert(index); - downloads[index].get()->disconnect(this); + m_downloads[index].get()->disconnect(this); + startMoreParts(); } void NetJob::partProgress(int index, qint64 bytesReceived, qint64 bytesTotal) { - auto &slot = parts_progress[index]; + auto& slot = m_parts_progress[index]; slot.current_progress = bytesReceived; slot.total_progress = bytesTotal; int done = m_done.size(); int doing = m_doing.size(); - int all = parts_progress.size(); + int all = m_parts_progress.size(); qint64 bytesAll = 0; qint64 bytesTotalAll = 0; - for(auto & partIdx: m_doing) - { - auto part = parts_progress[partIdx]; + for (auto& partIdx : m_doing) { + auto part = m_parts_progress[partIdx]; // do not count parts with unknown/nonsensical total size - if(part.total_progress <= 0) - { + if (part.total_progress <= 0) { continue; } bytesAll += part.current_progress; @@ -85,134 +176,53 @@ void NetJob::partProgress(int index, qint64 bytesReceived, qint64 bytesTotal) auto current_total = all * 1000; // HACK: make sure it never jumps backwards. // FAIL: This breaks if the size is not known (or is it something else?) and jumps to 1000, so if it is 1000 reset it to inprogress - if(m_current_progress == 1000) { + if (m_current_progress == 1000) { m_current_progress = inprogress; } - if(m_current_progress > current) - { + if (m_current_progress > current) { current = m_current_progress; } m_current_progress = current; setProgress(current, current_total); } -void NetJob::executeTask() -{ - // hack that delays early failures so they can be caught easier - QMetaObject::invokeMethod(this, "startMoreParts", Qt::QueuedConnection); -} - void NetJob::startMoreParts() { - if(!isRunning()) - { - // this actually makes sense. You can put running downloads into a NetJob and then not start it until much later. + if (!isRunning()) { + // this actually makes sense. You can put running m_downloads into a NetJob and then not start it until much later. return; } + // OK. We are actively processing tasks, proceed. // Check for final conditions if there's nothing in the queue. - if(!m_todo.size()) - { - if(!m_doing.size()) - { - if(!m_failed.size()) - { + if (!m_todo.size()) { + if (!m_doing.size()) { + if (!m_failed.size()) { emitSucceeded(); - } - else if(m_aborted) - { + } else if (m_aborted) { emitAborted(); - } - else - { + } else { emitFailed(tr("Job '%1' failed to process:\n%2").arg(objectName()).arg(getFailedFiles().join("\n"))); } } return; } - // There's work to do, try to start more parts. - while (m_doing.size() < 6) - { - if(!m_todo.size()) + + // There's work to do, try to start more parts, to a maximum of 6 concurrent ones. + while (m_doing.size() < 6) { + if (m_todo.size() == 0) return; int doThis = m_todo.dequeue(); m_doing.insert(doThis); - auto part = downloads[doThis]; + + auto part = m_downloads[doThis]; + // connect signals :D - connect(part.get(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int))); - connect(part.get(), SIGNAL(failed(int)), SLOT(partFailed(int))); - connect(part.get(), SIGNAL(aborted(int)), SLOT(partAborted(int))); - connect(part.get(), SIGNAL(netActionProgress(int, qint64, qint64)), - SLOT(partProgress(int, qint64, qint64))); - part->start(m_network); + connect(part.get(), &NetAction::succeeded, this, &NetJob::partSucceeded); + connect(part.get(), &NetAction::failed, this, &NetJob::partFailed); + connect(part.get(), &NetAction::aborted, this, &NetJob::partAborted); + connect(part.get(), &NetAction::netActionProgress, this, &NetJob::partProgress); + + part->startAction(m_network); } } - - -QStringList NetJob::getFailedFiles() -{ - QStringList failed; - for (auto index: m_failed) - { - failed.push_back(downloads[index]->url().toString()); - } - failed.sort(); - return failed; -} - -bool NetJob::canAbort() const -{ - bool canFullyAbort = true; - // can abort the waiting? - for(auto index: m_todo) - { - auto part = downloads[index]; - canFullyAbort &= part->canAbort(); - } - // can abort the active? - for(auto index: m_doing) - { - auto part = downloads[index]; - canFullyAbort &= part->canAbort(); - } - return canFullyAbort; -} - -bool NetJob::abort() -{ - bool fullyAborted = true; - // fail all waiting - m_failed.unite(m_todo.toSet()); - m_todo.clear(); - // abort active - auto toKill = m_doing.toList(); - for(auto index: toKill) - { - auto part = downloads[index]; - fullyAborted &= part->abort(); - } - return fullyAborted; -} - -bool NetJob::addNetAction(NetAction::Ptr action) -{ - action->m_index_within_job = downloads.size(); - downloads.append(action); - part_info pi; - parts_progress.append(pi); - partProgress(parts_progress.count() - 1, action->currentProgress(), action->totalProgress()); - - if(action->isRunning()) - { - connect(action.get(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int))); - connect(action.get(), SIGNAL(failed(int)), SLOT(partFailed(int))); - connect(action.get(), SIGNAL(netActionProgress(int, qint64, qint64)), SLOT(partProgress(int, qint64, qint64))); - } - else - { - m_todo.append(parts_progress.size() - 1); - } - return true; -} - -NetJob::~NetJob() = default; diff --git a/launcher/net/NetJob.h b/launcher/net/NetJob.h index fdea710f..c397e2a1 100644 --- a/launcher/net/NetJob.h +++ b/launcher/net/NetJob.h @@ -1,88 +1,97 @@ -/* Copyright 2013-2021 MultiMC Contributors +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher * - * 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 + #include + +#include #include "NetAction.h" -#include "Download.h" -#include "HttpMetaCache.h" #include "tasks/Task.h" -#include "QObjectPtr.h" -class NetJob; +// Those are included so that they are also included by anyone using NetJob +#include "net/Download.h" +#include "net/HttpMetaCache.h" -class NetJob : public Task -{ +class NetJob : public Task { Q_OBJECT -public: + + public: using Ptr = shared_qobject_ptr; explicit NetJob(QString job_name, shared_qobject_ptr network) : Task(), m_network(network) { setObjectName(job_name); } - virtual ~NetJob(); + virtual ~NetJob() = default; - bool addNetAction(NetAction::Ptr action); + void executeTask() override; - NetAction::Ptr operator[](int index) - { - return downloads[index]; - } - const NetAction::Ptr at(const int index) - { - return downloads.at(index); - } - NetAction::Ptr first() - { - if (downloads.size()) - return downloads[0]; - return NetAction::Ptr(); - } - int size() const - { - return downloads.size(); - } - QStringList getFailedFiles(); + auto canAbort() const -> bool override; - bool canAbort() const override; + auto addNetAction(NetAction::Ptr action) -> bool; -private slots: + auto operator[](int index) -> NetAction::Ptr { return m_downloads[index]; } + auto at(int index) -> const NetAction::Ptr { return m_downloads.at(index); } + auto size() const -> int { return m_downloads.size(); } + auto first() -> NetAction::Ptr { return m_downloads.size() != 0 ? m_downloads[0] : NetAction::Ptr{}; } + + auto getFailedFiles() -> QStringList; + + public slots: + // Qt can't handle auto at the start for some reason? + bool abort() override; + + private slots: void startMoreParts(); -public slots: - virtual void executeTask() override; - virtual bool abort() override; - -private slots: void partProgress(int index, qint64 bytesReceived, qint64 bytesTotal); void partSucceeded(int index); void partFailed(int index); void partAborted(int index); -private: + private: shared_qobject_ptr m_network; - struct part_info - { + struct part_info { qint64 current_progress = 0; qint64 total_progress = 1; int failures = 0; }; - QList downloads; - QList parts_progress; + + QList m_downloads; + QList m_parts_progress; QQueue m_todo; QSet m_doing; QSet m_done; diff --git a/launcher/net/Sink.h b/launcher/net/Sink.h index d367fb15..3b2a7f8d 100644 --- a/launcher/net/Sink.h +++ b/launcher/net/Sink.h @@ -5,33 +5,30 @@ #include "Validator.h" namespace Net { -class Sink -{ -public: /* con/des */ - Sink() {}; - virtual ~Sink() {}; +class Sink { + public: + Sink() = default; + virtual ~Sink(){}; -public: /* methods */ - virtual JobStatus init(QNetworkRequest & request) = 0; - virtual JobStatus write(QByteArray & data) = 0; - virtual JobStatus abort() = 0; - virtual JobStatus finalize(QNetworkReply & reply) = 0; + public: + virtual Task::State init(QNetworkRequest& request) = 0; + virtual Task::State write(QByteArray& data) = 0; + virtual Task::State abort() = 0; + virtual Task::State finalize(QNetworkReply& reply) = 0; virtual bool hasLocalData() = 0; - void addValidator(Validator * validator) + void addValidator(Validator* validator) { - if(validator) - { + if (validator) { validators.push_back(std::shared_ptr(validator)); } } -protected: /* methods */ - bool finalizeAllValidators(QNetworkReply & reply) + protected: /* methods */ + bool finalizeAllValidators(QNetworkReply& reply) { - for(auto & validator: validators) - { - if(!validator->validate(reply)) + for (auto& validator : validators) { + if (!validator->validate(reply)) return false; } return true; @@ -39,32 +36,29 @@ protected: /* methods */ bool failAllValidators() { bool success = true; - for(auto & validator: validators) - { + for (auto& validator : validators) { success &= validator->abort(); } return success; } - bool initAllValidators(QNetworkRequest & request) + bool initAllValidators(QNetworkRequest& request) { - for(auto & validator: validators) - { - if(!validator->init(request)) + for (auto& validator : validators) { + if (!validator->init(request)) return false; } return true; } - bool writeAllValidators(QByteArray & data) + bool writeAllValidators(QByteArray& data) { - for(auto & validator: validators) - { - if(!validator->write(data)) + for (auto& validator : validators) { + if (!validator->write(data)) return false; } return true; } -protected: /* data */ + protected: /* data */ std::vector> validators; }; -} +} // namespace Net diff --git a/launcher/screenshots/ImgurAlbumCreation.cpp b/launcher/screenshots/ImgurAlbumCreation.cpp index d5de302a..81fac929 100644 --- a/launcher/screenshots/ImgurAlbumCreation.cpp +++ b/launcher/screenshots/ImgurAlbumCreation.cpp @@ -13,12 +13,12 @@ ImgurAlbumCreation::ImgurAlbumCreation(QList screenshots) : NetAction(), m_screenshots(screenshots) { m_url = BuildConfig.IMGUR_BASE_URL + "album.json"; - m_status = Job_NotStarted; + m_state = State::Inactive; } -void ImgurAlbumCreation::startImpl() +void ImgurAlbumCreation::executeTask() { - m_status = Job_InProgress; + m_state = State::Running; QNetworkRequest request(m_url); request.setHeader(QNetworkRequest::UserAgentHeader, BuildConfig.USER_AGENT_UNCACHED); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); @@ -43,11 +43,11 @@ void ImgurAlbumCreation::startImpl() void ImgurAlbumCreation::downloadError(QNetworkReply::NetworkError error) { qDebug() << m_reply->errorString(); - m_status = Job_Failed; + m_state = State::Failed; } void ImgurAlbumCreation::downloadFinished() { - if (m_status != Job_Failed) + if (m_state != State::Failed) { QByteArray data = m_reply->readAll(); m_reply.reset(); @@ -68,7 +68,7 @@ void ImgurAlbumCreation::downloadFinished() } m_deleteHash = object.value("data").toObject().value("deletehash").toString(); m_id = object.value("data").toObject().value("id").toString(); - m_status = Job_Finished; + m_state = State::Succeeded; emit succeeded(m_index_within_job); return; } @@ -82,7 +82,6 @@ void ImgurAlbumCreation::downloadFinished() } void ImgurAlbumCreation::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) { - m_total_progress = bytesTotal; - m_progress = bytesReceived; + setProgress(bytesReceived, bytesTotal); emit netActionProgress(m_index_within_job, bytesReceived, bytesTotal); } diff --git a/launcher/screenshots/ImgurAlbumCreation.h b/launcher/screenshots/ImgurAlbumCreation.h index cb048a23..4cb0ed5d 100644 --- a/launcher/screenshots/ImgurAlbumCreation.h +++ b/launcher/screenshots/ImgurAlbumCreation.h @@ -24,16 +24,14 @@ public: protected slots: - virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal); - virtual void downloadError(QNetworkReply::NetworkError error); - virtual void downloadFinished(); - virtual void downloadReadyRead() - { - } + void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override; + void downloadError(QNetworkReply::NetworkError error) override; + void downloadFinished() override; + void downloadReadyRead() override {} public slots: - virtual void startImpl(); + void executeTask() override; private: QList m_screenshots; diff --git a/launcher/screenshots/ImgurUpload.cpp b/launcher/screenshots/ImgurUpload.cpp index 76a84947..0f0fd79c 100644 --- a/launcher/screenshots/ImgurUpload.cpp +++ b/launcher/screenshots/ImgurUpload.cpp @@ -13,13 +13,13 @@ ImgurUpload::ImgurUpload(ScreenShot::Ptr shot) : NetAction(), m_shot(shot) { m_url = BuildConfig.IMGUR_BASE_URL + "upload.json"; - m_status = Job_NotStarted; + m_state = State::Inactive; } -void ImgurUpload::startImpl() +void ImgurUpload::executeTask() { finished = false; - m_status = Job_InProgress; + m_state = Task::State::Running; QNetworkRequest request(m_url); request.setHeader(QNetworkRequest::UserAgentHeader, BuildConfig.USER_AGENT_UNCACHED); request.setRawHeader("Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toStdString().c_str()); @@ -63,7 +63,7 @@ void ImgurUpload::downloadError(QNetworkReply::NetworkError error) qCritical() << "Double finished ImgurUpload!"; return; } - m_status = Job_Failed; + m_state = Task::State::Failed; finished = true; m_reply.reset(); emit failed(m_index_within_job); @@ -99,14 +99,13 @@ void ImgurUpload::downloadFinished() 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_status = Job_Finished; + m_state = Task::State::Succeeded; finished = true; emit succeeded(m_index_within_job); return; } void ImgurUpload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) { - m_total_progress = bytesTotal; - m_progress = bytesReceived; + setProgress(bytesReceived, bytesTotal); emit netActionProgress(m_index_within_job, bytesReceived, bytesTotal); } diff --git a/launcher/screenshots/ImgurUpload.h b/launcher/screenshots/ImgurUpload.h index cf54f58d..a1040551 100644 --- a/launcher/screenshots/ImgurUpload.h +++ b/launcher/screenshots/ImgurUpload.h @@ -21,7 +21,7 @@ slots: public slots: - void startImpl() override; + void executeTask() override; private: ScreenShot::Ptr m_shot; diff --git a/launcher/tasks/Task.h b/launcher/tasks/Task.h index 344a024e..61855160 100644 --- a/launcher/tasks/Task.h +++ b/launcher/tasks/Task.h @@ -52,6 +52,8 @@ class Task : public QObject { virtual bool canAbort() const { return false; } + auto getState() const -> State { return m_state; } + QString getStatus() { return m_status; } virtual auto getStepStatus() const -> QString { return m_status; } @@ -90,7 +92,7 @@ class Task : public QObject { void setStatus(const QString& status); void setProgress(qint64 current, qint64 total); - private: + protected: State m_state = State::Inactive; QStringList m_Warnings; QString m_failReason = ""; diff --git a/launcher/translations/TranslationsModel.cpp b/launcher/translations/TranslationsModel.cpp index 250854d3..fbd17060 100644 --- a/launcher/translations/TranslationsModel.cpp +++ b/launcher/translations/TranslationsModel.cpp @@ -667,7 +667,7 @@ void TranslationsModel::downloadTranslation(QString key) auto dl = Net::Download::makeCached(QUrl(BuildConfig.TRANSLATIONS_BASE_URL + lang->file_name), entry); auto rawHash = QByteArray::fromHex(lang->file_sha1.toLatin1()); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawHash)); - dl->m_total_progress = lang->file_size; + dl->setProgress(dl->getProgress(), lang->file_size); d->m_dl_job = new NetJob("Translation for " + key, APPLICATION->network()); d->m_dl_job->addNetAction(dl); From efa3fbff39bf0dabebdf1c6330090ee320895a4d Mon Sep 17 00:00:00 2001 From: flow Date: Tue, 26 Apr 2022 21:25:42 -0300 Subject: [PATCH 10/67] refactor: remove some superfluous signals Since now we're inheriting from Task, some signals can be reused. --- launcher/net/Download.cpp | 21 ++++++++++----------- launcher/net/NetAction.h | 10 ++-------- launcher/net/NetJob.cpp | 14 +++++++------- launcher/screenshots/ImgurAlbumCreation.cpp | 10 +++++----- launcher/screenshots/ImgurUpload.cpp | 12 ++++++------ launcher/tasks/Task.cpp | 3 +-- launcher/tasks/Task.h | 3 ++- 7 files changed, 33 insertions(+), 40 deletions(-) diff --git a/launcher/net/Download.cpp b/launcher/net/Download.cpp index 5b5a04db..5e5d64fa 100644 --- a/launcher/net/Download.cpp +++ b/launcher/net/Download.cpp @@ -72,7 +72,7 @@ void Download::executeTask() { if (getState() == Task::State::AbortedByUser) { qWarning() << "Attempt to start an aborted Download:" << m_url.toString(); - emit aborted(m_index_within_job); + emitAborted(); return; } @@ -80,7 +80,7 @@ void Download::executeTask() m_state = m_sink->init(request); switch (m_state) { case State::Succeeded: - emit succeeded(m_index_within_job); + emit succeeded(); qDebug() << "Download cache hit " << m_url.toString(); return; case State::Running: @@ -88,7 +88,7 @@ void Download::executeTask() break; case State::Inactive: case State::Failed: - emit failed(m_index_within_job); + emitFailed(); return; case State::AbortedByUser: return; @@ -102,8 +102,8 @@ void Download::executeTask() QNetworkReply* rep = m_network->get(request); m_reply.reset(rep); - connect(rep, SIGNAL(downloadProgress(qint64, qint64)), SLOT(downloadProgress(qint64, qint64))); - connect(rep, SIGNAL(finished()), SLOT(downloadFinished())); + connect(rep, &QNetworkReply::downloadProgress, this, &Download::downloadProgress); + connect(rep, &QNetworkReply::finished, this, &Download::downloadFinished); connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(downloadError(QNetworkReply::NetworkError))); connect(rep, &QNetworkReply::sslErrors, this, &Download::sslErrors); connect(rep, &QNetworkReply::readyRead, this, &Download::downloadReadyRead); @@ -112,7 +112,6 @@ void Download::executeTask() void Download::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) { setProgress(bytesReceived, bytesTotal); - emit netActionProgress(m_index_within_job, bytesReceived, bytesTotal); } void Download::downloadError(QNetworkReply::NetworkError error) @@ -212,19 +211,19 @@ void Download::downloadFinished() qDebug() << "Download failed but we are allowed to proceed:" << m_url.toString(); m_sink->abort(); m_reply.reset(); - emit succeeded(m_index_within_job); + emit succeeded(); return; } else if (m_state == State::Failed) { qDebug() << "Download failed in previous step:" << m_url.toString(); m_sink->abort(); m_reply.reset(); - emit failed(m_index_within_job); + emitFailed(); return; } else if (m_state == State::AbortedByUser) { qDebug() << "Download aborted in previous step:" << m_url.toString(); m_sink->abort(); m_reply.reset(); - emit aborted(m_index_within_job); + emitAborted(); return; } @@ -241,12 +240,12 @@ void Download::downloadFinished() qDebug() << "Download failed to finalize:" << m_url.toString(); m_sink->abort(); m_reply.reset(); - emit failed(m_index_within_job); + emitFailed(); return; } m_reply.reset(); qDebug() << "Download succeeded:" << m_url.toString(); - emit succeeded(m_index_within_job); + emit succeeded(); } void Download::downloadReadyRead() diff --git a/launcher/net/NetAction.h b/launcher/net/NetAction.h index e15716f6..86a37ee6 100644 --- a/launcher/net/NetAction.h +++ b/launcher/net/NetAction.h @@ -43,7 +43,7 @@ class NetAction : public Task { Q_OBJECT protected: - explicit NetAction() : Task(nullptr) {}; + explicit NetAction() : Task() {}; public: using Ptr = shared_qobject_ptr; @@ -51,13 +51,7 @@ class NetAction : public Task { virtual ~NetAction() = default; QUrl url() { return m_url; } - - signals: - void started(int index); - void netActionProgress(int index, qint64 current, qint64 total); - void succeeded(int index); - void failed(int index); - void aborted(int index); + auto index() -> int { return m_index_within_job; } protected slots: virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) = 0; diff --git a/launcher/net/NetJob.cpp b/launcher/net/NetJob.cpp index d08d6c4d..a9f89da4 100644 --- a/launcher/net/NetJob.cpp +++ b/launcher/net/NetJob.cpp @@ -45,9 +45,9 @@ auto NetJob::addNetAction(NetAction::Ptr action) -> bool partProgress(m_parts_progress.count() - 1, action->getProgress(), action->getTotalProgress()); if (action->isRunning()) { - connect(action.get(), &NetAction::succeeded, this, &NetJob::partSucceeded); - connect(action.get(), &NetAction::failed, this, &NetJob::partFailed); - connect(action.get(), &NetAction::netActionProgress, this, &NetJob::partProgress); + connect(action.get(), &NetAction::succeeded, [this, action]{ partSucceeded(action->index()); }); + connect(action.get(), &NetAction::failed, [this, action](QString){ partFailed(action->index()); }); + connect(action.get(), &NetAction::progress, [this, action](qint64 done, qint64 total) { partProgress(action->index(), done, total); }); } else { m_todo.append(m_parts_progress.size() - 1); } @@ -218,10 +218,10 @@ void NetJob::startMoreParts() auto part = m_downloads[doThis]; // connect signals :D - connect(part.get(), &NetAction::succeeded, this, &NetJob::partSucceeded); - connect(part.get(), &NetAction::failed, this, &NetJob::partFailed); - connect(part.get(), &NetAction::aborted, this, &NetJob::partAborted); - connect(part.get(), &NetAction::netActionProgress, this, &NetJob::partProgress); + connect(part.get(), &NetAction::succeeded, this, [this, part]{ partSucceeded(part->index()); }); + connect(part.get(), &NetAction::failed, this, [this, part](QString){ partFailed(part->index()); }); + connect(part.get(), &NetAction::aborted, this, [this, part]{ partAborted(part->index()); }); + connect(part.get(), &NetAction::progress, this, [this, part](qint64 done, qint64 total) { partProgress(part->index(), done, total); }); part->startAction(m_network); } diff --git a/launcher/screenshots/ImgurAlbumCreation.cpp b/launcher/screenshots/ImgurAlbumCreation.cpp index 81fac929..f94527c8 100644 --- a/launcher/screenshots/ImgurAlbumCreation.cpp +++ b/launcher/screenshots/ImgurAlbumCreation.cpp @@ -56,32 +56,32 @@ void ImgurAlbumCreation::downloadFinished() if (jsonError.error != QJsonParseError::NoError) { qDebug() << jsonError.errorString(); - emit failed(m_index_within_job); + emitFailed(); return; } auto object = doc.object(); if (!object.value("success").toBool()) { qDebug() << doc.toJson(); - emit failed(m_index_within_job); + 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(m_index_within_job); + emit succeeded(); return; } else { qDebug() << m_reply->readAll(); m_reply.reset(); - emit failed(m_index_within_job); + emitFailed(); return; } } void ImgurAlbumCreation::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) { setProgress(bytesReceived, bytesTotal); - emit netActionProgress(m_index_within_job, bytesReceived, bytesTotal); + emit progress(bytesReceived, bytesTotal); } diff --git a/launcher/screenshots/ImgurUpload.cpp b/launcher/screenshots/ImgurUpload.cpp index 0f0fd79c..05314de7 100644 --- a/launcher/screenshots/ImgurUpload.cpp +++ b/launcher/screenshots/ImgurUpload.cpp @@ -28,7 +28,7 @@ void ImgurUpload::executeTask() QFile f(m_shot->m_file.absoluteFilePath()); if (!f.open(QFile::ReadOnly)) { - emit failed(m_index_within_job); + emitFailed(); return; } @@ -66,7 +66,7 @@ void ImgurUpload::downloadError(QNetworkReply::NetworkError error) m_state = Task::State::Failed; finished = true; m_reply.reset(); - emit failed(m_index_within_job); + emitFailed(); } void ImgurUpload::downloadFinished() { @@ -84,7 +84,7 @@ void ImgurUpload::downloadFinished() qDebug() << "imgur server did not reply with JSON" << jsonError.errorString(); finished = true; m_reply.reset(); - emit failed(m_index_within_job); + emitFailed(); return; } auto object = doc.object(); @@ -93,7 +93,7 @@ void ImgurUpload::downloadFinished() qDebug() << "Screenshot upload not successful:" << doc.toJson(); finished = true; m_reply.reset(); - emit failed(m_index_within_job); + emitFailed(); return; } m_shot->m_imgurId = object.value("data").toObject().value("id").toString(); @@ -101,11 +101,11 @@ void ImgurUpload::downloadFinished() m_shot->m_imgurDeleteHash = object.value("data").toObject().value("deletehash").toString(); m_state = Task::State::Succeeded; finished = true; - emit succeeded(m_index_within_job); + emit succeeded(); return; } void ImgurUpload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) { setProgress(bytesReceived, bytesTotal); - emit netActionProgress(m_index_within_job, bytesReceived, bytesTotal); + emit progress(bytesReceived, bytesTotal); } diff --git a/launcher/tasks/Task.cpp b/launcher/tasks/Task.cpp index 57307b43..68e0e8a7 100644 --- a/launcher/tasks/Task.cpp +++ b/launcher/tasks/Task.cpp @@ -99,8 +99,7 @@ void Task::emitAborted() m_state = State::AbortedByUser; m_failReason = "Aborted."; qDebug() << "Task" << describe() << "aborted."; - emit failed(m_failReason); - emit finished(); + emit aborted(); } void Task::emitSucceeded() diff --git a/launcher/tasks/Task.h b/launcher/tasks/Task.h index 61855160..e09c57ae 100644 --- a/launcher/tasks/Task.h +++ b/launcher/tasks/Task.h @@ -73,6 +73,7 @@ class Task : public QObject { virtual void progress(qint64 current, qint64 total); void finished(); void succeeded(); + void aborted(); void failed(QString reason); void status(QString status); @@ -86,7 +87,7 @@ class Task : public QObject { protected slots: virtual void emitSucceeded(); virtual void emitAborted(); - virtual void emitFailed(QString reason); + virtual void emitFailed(QString reason = ""); public slots: void setStatus(const QString& status); From 040ee919e5ea71364daa08c30e09c843976f5734 Mon Sep 17 00:00:00 2001 From: flow Date: Wed, 27 Apr 2022 18:36:11 -0300 Subject: [PATCH 11/67] refactor: more net cleanup This runs clang-tidy on some other files in launcher/net/. This also makes use of some JSON wrappers in HttpMetaCache, instead of using the Qt stuff directly. Lastly, this removes useless null checks (crashes don't occur because of this, but because of concurrent usage / free of the QByteArray pointer), and fix a fixme in Download.h --- launcher/net/ByteArraySink.h | 11 +- launcher/net/ChecksumValidator.h | 48 ++++---- launcher/net/Download.cpp | 24 ++-- launcher/net/Download.h | 59 ++++------ launcher/net/FileSink.cpp | 47 ++++---- launcher/net/HttpMetaCache.cpp | 190 ++++++++++++++----------------- launcher/net/HttpMetaCache.h | 107 +++++++---------- launcher/net/Mode.h | 9 +- launcher/net/Sink.h | 33 +++--- 9 files changed, 228 insertions(+), 300 deletions(-) diff --git a/launcher/net/ByteArraySink.h b/launcher/net/ByteArraySink.h index 75a66574..8ae30bb3 100644 --- a/launcher/net/ByteArraySink.h +++ b/launcher/net/ByteArraySink.h @@ -6,6 +6,8 @@ namespace Net { /* * Sink object for downloads that uses an external QByteArray it doesn't own as a target. + * FIXME: It is possible that the QByteArray is freed while we're doing some operation on it, + * causing a segmentation fault. */ class ByteArraySink : public Sink { public: @@ -16,9 +18,6 @@ class ByteArraySink : public Sink { public: auto init(QNetworkRequest& request) -> Task::State override { - if(!m_output) - return Task::State::Failed; - m_output->clear(); if (initAllValidators(request)) return Task::State::Running; @@ -27,9 +26,6 @@ class ByteArraySink : public Sink { auto write(QByteArray& data) -> Task::State override { - if(!m_output) - return Task::State::Failed; - m_output->append(data); if (writeAllValidators(data)) return Task::State::Running; @@ -38,9 +34,6 @@ class ByteArraySink : public Sink { auto abort() -> Task::State override { - if(!m_output) - return Task::State::Failed; - m_output->clear(); failAllValidators(); return Task::State::Failed; diff --git a/launcher/net/ChecksumValidator.h b/launcher/net/ChecksumValidator.h index 0d6b19c2..8a8b10d5 100644 --- a/launcher/net/ChecksumValidator.h +++ b/launcher/net/ChecksumValidator.h @@ -1,55 +1,47 @@ #pragma once #include "Validator.h" + #include -#include #include namespace Net { -class ChecksumValidator: public Validator -{ -public: /* con/des */ +class ChecksumValidator : public Validator { + public: ChecksumValidator(QCryptographicHash::Algorithm algorithm, QByteArray expected = QByteArray()) - :m_checksum(algorithm), m_expected(expected) - { - }; - virtual ~ChecksumValidator() {}; + : m_checksum(algorithm), m_expected(expected){}; + virtual ~ChecksumValidator() = default; -public: /* methods */ - bool init(QNetworkRequest &) override + public: + auto init(QNetworkRequest&) -> bool override { m_checksum.reset(); return true; } - bool write(QByteArray & data) override + + auto write(QByteArray& data) -> bool override { m_checksum.addData(data); return true; } - bool abort() override + + auto abort() -> bool override { return true; } + + auto validate(QNetworkReply&) -> bool override { - return true; - } - bool validate(QNetworkReply &) override - { - if(m_expected.size() && m_expected != hash()) - { + if (m_expected.size() && m_expected != hash()) { qWarning() << "Checksum mismatch, download is bad."; return false; } return true; } - QByteArray hash() - { - return m_checksum.result(); - } - void setExpected(QByteArray expected) - { - m_expected = expected; - } -private: /* data */ + auto hash() -> QByteArray { return m_checksum.result(); } + + void setExpected(QByteArray expected) { m_expected = expected; } + + private: QCryptographicHash m_checksum; QByteArray m_expected; }; -} \ No newline at end of file +} // namespace Net diff --git a/launcher/net/Download.cpp b/launcher/net/Download.cpp index 5e5d64fa..3d6ca338 100644 --- a/launcher/net/Download.cpp +++ b/launcher/net/Download.cpp @@ -33,30 +33,29 @@ Download::Download() : NetAction() m_state = State::Inactive; } -Download::Ptr Download::makeCached(QUrl url, MetaEntryPtr entry, Options options) +auto Download::makeCached(QUrl url, MetaEntryPtr entry, Options options) -> Download::Ptr { - Download* dl = new Download(); + auto* dl = new Download(); dl->m_url = url; dl->m_options = options; auto md5Node = new ChecksumValidator(QCryptographicHash::Md5); auto cachedNode = new MetaCacheSink(entry, md5Node); dl->m_sink.reset(cachedNode); - dl->m_target_path = entry->getFullPath(); return dl; } -Download::Ptr Download::makeByteArray(QUrl url, QByteArray* output, Options options) +auto Download::makeByteArray(QUrl url, QByteArray* output, Options options) -> Download::Ptr { - Download* dl = new Download(); + auto* dl = new Download(); dl->m_url = url; dl->m_options = options; dl->m_sink.reset(new ByteArraySink(output)); return dl; } -Download::Ptr Download::makeFile(QUrl url, QString path, Options options) +auto Download::makeFile(QUrl url, QString path, Options options) -> Download::Ptr { - Download* dl = new Download(); + auto* dl = new Download(); dl->m_url = url; dl->m_options = options; dl->m_sink.reset(new FileSink(path)); @@ -143,7 +142,7 @@ void Download::sslErrors(const QList& errors) } } -bool Download::handleRedirect() +auto Download::handleRedirect() -> bool { QUrl redirect = m_reply->header(QNetworkRequest::LocationHeader).toUrl(); if (!redirect.isValid()) { @@ -230,7 +229,7 @@ void Download::downloadFinished() // make sure we got all the remaining data, if any auto data = m_reply->readAll(); if (data.size()) { - qDebug() << "Writing extra" << data.size() << "bytes to" << m_target_path; + qDebug() << "Writing extra" << data.size() << "bytes"; m_state = m_sink->write(data); } @@ -243,6 +242,7 @@ void Download::downloadFinished() emitFailed(); return; } + m_reply.reset(); qDebug() << "Download succeeded:" << m_url.toString(); emit succeeded(); @@ -254,17 +254,17 @@ void Download::downloadReadyRead() auto data = m_reply->readAll(); m_state = m_sink->write(data); if (m_state == State::Failed) { - qCritical() << "Failed to process response chunk for " << m_target_path; + qCritical() << "Failed to process response chunk"; } // qDebug() << "Download" << m_url.toString() << "gained" << data.size() << "bytes"; } else { - qCritical() << "Cannot write to " << m_target_path << ", illegal status" << m_status; + qCritical() << "Cannot write download data! illegal status " << m_status; } } } // namespace Net -bool Net::Download::abort() +auto Net::Download::abort() -> bool { if (m_reply) { m_reply->abort(); diff --git a/launcher/net/Download.h b/launcher/net/Download.h index 231ad6a7..9fb67127 100644 --- a/launcher/net/Download.h +++ b/launcher/net/Download.h @@ -15,63 +15,54 @@ #pragma once -#include "NetAction.h" #include "HttpMetaCache.h" -#include "Validator.h" +#include "NetAction.h" #include "Sink.h" +#include "Validator.h" #include "QObjectPtr.h" namespace Net { -class Download : public NetAction -{ +class Download : public NetAction { Q_OBJECT -public: - typedef shared_qobject_ptr Ptr; - enum class Option - { - NoOptions = 0, - AcceptLocalFiles = 1 - }; + public: + using Ptr = shared_qobject_ptr; + enum class Option { NoOptions = 0, AcceptLocalFiles = 1 }; Q_DECLARE_FLAGS(Options, Option) -protected: + protected: explicit Download(); -public: - virtual ~Download(){}; - static Download::Ptr makeCached(QUrl url, MetaEntryPtr entry, Options options = Option::NoOptions); - static Download::Ptr makeByteArray(QUrl url, QByteArray *output, Options options = Option::NoOptions); - static Download::Ptr makeFile(QUrl url, QString path, Options options = Option::NoOptions); -public: - QString getTargetFilepath() - { - return m_target_path; - } - void addValidator(Validator * v); - bool abort() override; - bool canAbort() const override { return true; }; + public: + ~Download() override = default; -private: - bool handleRedirect(); + static auto makeCached(QUrl url, MetaEntryPtr entry, Options options = Option::NoOptions) -> Download::Ptr; + static auto makeByteArray(QUrl url, QByteArray* output, Options options = Option::NoOptions) -> Download::Ptr; + static auto makeFile(QUrl url, QString path, Options options = Option::NoOptions) -> Download::Ptr; -protected slots: + public: + void addValidator(Validator* v); + auto abort() -> bool override; + auto canAbort() const -> bool override { return true; }; + + private: + auto handleRedirect() -> bool; + + protected slots: void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override; void downloadError(QNetworkReply::NetworkError error) override; - void sslErrors(const QList & errors); + void sslErrors(const QList& errors); void downloadFinished() override; void downloadReadyRead() override; -public slots: + public slots: void executeTask() override; -private: - // FIXME: remove this, it has no business being here. - QString m_target_path; + private: std::unique_ptr m_sink; Options m_options; }; -} +} // namespace Net Q_DECLARE_OPERATORS_FOR_FLAGS(Net::Download::Options) diff --git a/launcher/net/FileSink.cpp b/launcher/net/FileSink.cpp index 0d8b09bb..d2d2b06f 100644 --- a/launcher/net/FileSink.cpp +++ b/launcher/net/FileSink.cpp @@ -1,7 +1,5 @@ #include "FileSink.h" -#include - #include "FileSystem.h" namespace Net { @@ -9,44 +7,38 @@ namespace Net { Task::State FileSink::init(QNetworkRequest& request) { auto result = initCache(request); - if(result != Task::State::Running) - { + if (result != Task::State::Running) { return result; } + // create a new save file and open it for writing - if (!FS::ensureFilePathExists(m_filename)) - { + if (!FS::ensureFilePathExists(m_filename)) { qCritical() << "Could not create folder for " + m_filename; return Task::State::Failed; } + wroteAnyData = false; m_output_file.reset(new QSaveFile(m_filename)); - if (!m_output_file->open(QIODevice::WriteOnly)) - { + if (!m_output_file->open(QIODevice::WriteOnly)) { qCritical() << "Could not open " + m_filename + " for writing"; return Task::State::Failed; } - if(initAllValidators(request)) + if (initAllValidators(request)) return Task::State::Running; return Task::State::Failed; } -Task::State FileSink::initCache(QNetworkRequest &) -{ - return Task::State::Running; -} - Task::State FileSink::write(QByteArray& data) { - if (!writeAllValidators(data) || m_output_file->write(data) != data.size()) - { + if (!writeAllValidators(data) || m_output_file->write(data) != data.size()) { qCritical() << "Failed writing into " + m_filename; m_output_file->cancelWriting(); m_output_file.reset(); wroteAnyData = false; return Task::State::Failed; } + wroteAnyData = true; return Task::State::Running; } @@ -64,34 +56,39 @@ Task::State FileSink::finalize(QNetworkReply& reply) QVariant statusCodeV = reply.attribute(QNetworkRequest::HttpStatusCodeAttribute); bool validStatus = false; int statusCode = statusCodeV.toInt(&validStatus); - if(validStatus) - { + if (validStatus) { // this leaves out 304 Not Modified gotFile = statusCode == 200 || statusCode == 203; } + // if we wrote any data to the save file, we try to commit the data to the real file. // if it actually got a proper file, we write it even if it was empty - if (gotFile || wroteAnyData) - { + if (gotFile || wroteAnyData) { // ask validators for data consistency // we only do this for actual downloads, not 'your data is still the same' cache hits - if(!finalizeAllValidators(reply)) + if (!finalizeAllValidators(reply)) return Task::State::Failed; + // nothing went wrong... - if (!m_output_file->commit()) - { + if (!m_output_file->commit()) { qCritical() << "Failed to commit changes to " << m_filename; m_output_file->cancelWriting(); return Task::State::Failed; } } + // then get rid of the save file m_output_file.reset(); return finalizeCache(reply); } -Task::State FileSink::finalizeCache(QNetworkReply &) +Task::State FileSink::initCache(QNetworkRequest&) +{ + return Task::State::Running; +} + +Task::State FileSink::finalizeCache(QNetworkReply&) { return Task::State::Succeeded; } @@ -101,4 +98,4 @@ bool FileSink::hasLocalData() QFileInfo info(m_filename); return info.exists() && info.size() != 0; } -} +} // namespace Net diff --git a/launcher/net/HttpMetaCache.cpp b/launcher/net/HttpMetaCache.cpp index 8734e0bf..b41a18b1 100644 --- a/launcher/net/HttpMetaCache.cpp +++ b/launcher/net/HttpMetaCache.cpp @@ -15,29 +15,26 @@ #include "HttpMetaCache.h" #include "FileSystem.h" +#include "Json.h" -#include -#include -#include #include +#include +#include +#include #include -#include -#include -#include - -QString MetaEntry::getFullPath() +auto MetaEntry::getFullPath() -> QString { // FIXME: make local? return FS::PathCombine(basePath, relativePath); } -HttpMetaCache::HttpMetaCache(QString path) : QObject() +HttpMetaCache::HttpMetaCache(QString path) : QObject(), m_index_file(path) { - m_index_file = path; saveBatchingTimer.setSingleShot(true); saveBatchingTimer.setTimerType(Qt::VeryCoarseTimer); + connect(&saveBatchingTimer, SIGNAL(timeout()), SLOT(SaveNow())); } @@ -47,45 +44,42 @@ HttpMetaCache::~HttpMetaCache() SaveNow(); } -MetaEntryPtr HttpMetaCache::getEntry(QString base, QString resource_path) +auto HttpMetaCache::getEntry(QString base, QString resource_path) -> MetaEntryPtr { // no base. no base path. can't store - if (!m_entries.contains(base)) - { + if (!m_entries.contains(base)) { // TODO: log problem - return MetaEntryPtr(); + return {}; } - EntryMap &map = m_entries[base]; - if (map.entry_list.contains(resource_path)) - { + + EntryMap& map = m_entries[base]; + if (map.entry_list.contains(resource_path)) { return map.entry_list[resource_path]; } - return MetaEntryPtr(); + + return {}; } -MetaEntryPtr HttpMetaCache::resolveEntry(QString base, QString resource_path, QString expected_etag) +auto HttpMetaCache::resolveEntry(QString base, QString resource_path, QString expected_etag) -> MetaEntryPtr { auto entry = getEntry(base, resource_path); // it's not present? generate a default stale entry - if (!entry) - { + if (!entry) { return staleEntry(base, resource_path); } - auto &selected_base = m_entries[base]; + auto& selected_base = m_entries[base]; QString real_path = FS::PathCombine(selected_base.base_path, resource_path); QFileInfo finfo(real_path); // is the file really there? if not -> stale - if (!finfo.isFile() || !finfo.isReadable()) - { + if (!finfo.isFile() || !finfo.isReadable()) { // if the file doesn't exist, we disown the entry selected_base.entry_list.remove(resource_path); return staleEntry(base, resource_path); } - if (!expected_etag.isEmpty() && expected_etag != entry->etag) - { + if (!expected_etag.isEmpty() && expected_etag != entry->etag) { // if the etag doesn't match expected, we disown the entry selected_base.entry_list.remove(resource_path); return staleEntry(base, resource_path); @@ -93,18 +87,15 @@ MetaEntryPtr HttpMetaCache::resolveEntry(QString base, QString resource_path, QS // if the file changed, check md5sum qint64 file_last_changed = finfo.lastModified().toUTC().toMSecsSinceEpoch(); - if (file_last_changed != entry->local_changed_timestamp) - { + if (file_last_changed != entry->local_changed_timestamp) { QFile input(real_path); input.open(QIODevice::ReadOnly); - QString md5sum = QCryptographicHash::hash(input.readAll(), QCryptographicHash::Md5) - .toHex() - .constData(); - if (entry->md5sum != md5sum) - { + QString md5sum = QCryptographicHash::hash(input.readAll(), QCryptographicHash::Md5).toHex().constData(); + if (entry->md5sum != md5sum) { selected_base.entry_list.remove(resource_path); return staleEntry(base, resource_path); } + // md5sums matched... keep entry and save the new state to file entry->local_changed_timestamp = file_last_changed; SaveEventually(); @@ -115,42 +106,42 @@ MetaEntryPtr HttpMetaCache::resolveEntry(QString base, QString resource_path, QS return entry; } -bool HttpMetaCache::updateEntry(MetaEntryPtr stale_entry) +auto HttpMetaCache::updateEntry(MetaEntryPtr stale_entry) -> bool { - if (!m_entries.contains(stale_entry->baseId)) - { - qCritical() << "Cannot add entry with unknown base: " - << stale_entry->baseId.toLocal8Bit(); + if (!m_entries.contains(stale_entry->baseId)) { + qCritical() << "Cannot add entry with unknown base: " << stale_entry->baseId.toLocal8Bit(); return false; } - if (stale_entry->stale) - { + + if (stale_entry->stale) { qCritical() << "Cannot add stale entry: " << stale_entry->getFullPath().toLocal8Bit(); return false; } + m_entries[stale_entry->baseId].entry_list[stale_entry->relativePath] = stale_entry; SaveEventually(); + + return true; +} + +auto HttpMetaCache::evictEntry(MetaEntryPtr entry) -> bool +{ + if (!entry) + return false; + + entry->stale = true; + SaveEventually(); return true; } -bool HttpMetaCache::evictEntry(MetaEntryPtr entry) -{ - if(entry) - { - entry->stale = true; - SaveEventually(); - return true; - } - return false; -} - -MetaEntryPtr HttpMetaCache::staleEntry(QString base, QString resource_path) +auto HttpMetaCache::staleEntry(QString base, QString resource_path) -> MetaEntryPtr { auto foo = new MetaEntry(); foo->baseId = base; foo->basePath = getBasePath(base); foo->relativePath = resource_path; foo->stale = true; + return MetaEntryPtr(foo); } @@ -159,24 +150,25 @@ void HttpMetaCache::addBase(QString base, QString base_root) // TODO: report error if (m_entries.contains(base)) return; + // TODO: check if the base path is valid EntryMap foo; foo.base_path = base_root; m_entries[base] = foo; } -QString HttpMetaCache::getBasePath(QString base) +auto HttpMetaCache::getBasePath(QString base) -> QString { - if (m_entries.contains(base)) - { + if (m_entries.contains(base)) { return m_entries[base].base_path; } - return QString(); + + return {}; } void HttpMetaCache::Load() { - if(m_index_file.isNull()) + if (m_index_file.isNull()) return; QFile index(m_index_file); @@ -184,41 +176,35 @@ void HttpMetaCache::Load() return; QJsonDocument json = QJsonDocument::fromJson(index.readAll()); - if (!json.isObject()) - return; - auto root = json.object(); + + auto root = Json::requireObject(json, "HttpMetaCache root"); + // check file version first - auto version_val = root.value("version"); - if (!version_val.isString()) - return; - if (version_val.toString() != "1") + auto version_val = Json::ensureString(root, "version"); + if (version_val != "1") return; // read the entry array - auto entries_val = root.value("entries"); - if (!entries_val.isArray()) - return; - QJsonArray array = entries_val.toArray(); - for (auto element : array) - { - if (!element.isObject()) - return; - auto element_obj = element.toObject(); - QString base = element_obj.value("base").toString(); + auto array = Json::ensureArray(root, "entries"); + for (auto element : array) { + auto element_obj = Json::ensureObject(element); + auto base = Json::ensureString(element_obj, "base"); if (!m_entries.contains(base)) continue; - auto &entrymap = m_entries[base]; + + auto& entrymap = m_entries[base]; + auto foo = new MetaEntry(); foo->baseId = base; - QString path = foo->relativePath = element_obj.value("path").toString(); - foo->md5sum = element_obj.value("md5sum").toString(); - foo->etag = element_obj.value("etag").toString(); - foo->local_changed_timestamp = element_obj.value("last_changed_timestamp").toDouble(); - foo->remote_changed_timestamp = - element_obj.value("remote_changed_timestamp").toString(); + foo->relativePath = Json::ensureString(element_obj, "path"); + foo->md5sum = Json::ensureString(element_obj, "md5sum"); + foo->etag = Json::ensureString(element_obj, "etag"); + foo->local_changed_timestamp = Json::ensureDouble(element_obj, "last_changed_timestamp"); + foo->remote_changed_timestamp = Json::ensureString(element_obj, "remote_changed_timestamp"); // presumed innocent until closer examination foo->stale = false; - entrymap.entry_list[path] = MetaEntryPtr(foo); + + entrymap.entry_list[foo->relativePath] = MetaEntryPtr(foo); } } @@ -231,42 +217,36 @@ void HttpMetaCache::SaveEventually() void HttpMetaCache::SaveNow() { - if(m_index_file.isNull()) + if (m_index_file.isNull()) return; + QJsonObject toplevel; - toplevel.insert("version", QJsonValue(QString("1"))); + Json::writeString(toplevel, "version", "1"); + QJsonArray entriesArr; - for (auto group : m_entries) - { - for (auto entry : group.entry_list) - { + for (auto group : m_entries) { + for (auto entry : group.entry_list) { // do not save stale entries. they are dead. - if(entry->stale) - { + if (entry->stale) { continue; } + QJsonObject entryObj; - entryObj.insert("base", QJsonValue(entry->baseId)); - entryObj.insert("path", QJsonValue(entry->relativePath)); - entryObj.insert("md5sum", QJsonValue(entry->md5sum)); - entryObj.insert("etag", QJsonValue(entry->etag)); - entryObj.insert("last_changed_timestamp", - QJsonValue(double(entry->local_changed_timestamp))); + Json::writeString(entryObj, "base", entry->baseId); + Json::writeString(entryObj, "path", entry->relativePath); + Json::writeString(entryObj, "md5sum", entry->md5sum); + Json::writeString(entryObj, "etag", entry->etag); + entryObj.insert("last_changed_timestamp", QJsonValue(double(entry->local_changed_timestamp))); if (!entry->remote_changed_timestamp.isEmpty()) - entryObj.insert("remote_changed_timestamp", - QJsonValue(entry->remote_changed_timestamp)); + entryObj.insert("remote_changed_timestamp", QJsonValue(entry->remote_changed_timestamp)); entriesArr.append(entryObj); } } toplevel.insert("entries", entriesArr); - QJsonDocument doc(toplevel); - try - { - FS::write(m_index_file, doc.toJson()); - } - catch (const Exception &e) - { + try { + Json::write(toplevel, m_index_file); + } catch (const Exception& e) { qWarning() << e.what(); } } diff --git a/launcher/net/HttpMetaCache.h b/launcher/net/HttpMetaCache.h index 1c10e8c7..d8d1608e 100644 --- a/launcher/net/HttpMetaCache.h +++ b/launcher/net/HttpMetaCache.h @@ -14,109 +14,88 @@ */ #pragma once -#include -#include #include +#include +#include #include class HttpMetaCache; -class MetaEntry -{ -friend class HttpMetaCache; -protected: - MetaEntry() {} -public: - bool isStale() - { - return stale; - } - void setStale(bool stale) - { - this->stale = stale; - } - QString getFullPath(); - QString getRemoteChangedTimestamp() - { - return remote_changed_timestamp; - } - void setRemoteChangedTimestamp(QString remote_changed_timestamp) - { - this->remote_changed_timestamp = remote_changed_timestamp; - } - void setLocalChangedTimestamp(qint64 timestamp) - { - local_changed_timestamp = timestamp; - } - QString getETag() - { - return etag; - } - void setETag(QString etag) - { - this->etag = etag; - } - QString getMD5Sum() - { - return md5sum; - } - void setMD5Sum(QString md5sum) - { - this->md5sum = md5sum; - } -protected: +class MetaEntry { + friend class HttpMetaCache; + + protected: + MetaEntry() = default; + + public: + auto isStale() -> bool { return stale; } + void setStale(bool stale) { this->stale = stale; } + + auto getFullPath() -> QString; + + auto getRemoteChangedTimestamp() -> QString { return remote_changed_timestamp; } + void setRemoteChangedTimestamp(QString remote_changed_timestamp) { this->remote_changed_timestamp = remote_changed_timestamp; } + void setLocalChangedTimestamp(qint64 timestamp) { local_changed_timestamp = timestamp; } + + auto getETag() -> QString { return etag; } + void setETag(QString etag) { this->etag = etag; } + + auto getMD5Sum() -> QString { return md5sum; } + void setMD5Sum(QString md5sum) { this->md5sum = md5sum; } + + protected: QString baseId; QString basePath; QString relativePath; QString md5sum; QString etag; qint64 local_changed_timestamp = 0; - QString remote_changed_timestamp; // QString for now, RFC 2822 encoded time + QString remote_changed_timestamp; // QString for now, RFC 2822 encoded time bool stale = true; }; -typedef std::shared_ptr MetaEntryPtr; +using MetaEntryPtr = std::shared_ptr; -class HttpMetaCache : public QObject -{ +class HttpMetaCache : public QObject { Q_OBJECT -public: + public: // supply path to the cache index file HttpMetaCache(QString path = QString()); - ~HttpMetaCache(); + ~HttpMetaCache() override; // get the entry solely from the cache // you probably don't want this, unless you have some specific caching needs. - MetaEntryPtr getEntry(QString base, QString resource_path); + auto getEntry(QString base, QString resource_path) -> MetaEntryPtr; // get the entry from cache and verify that it isn't stale (within reason) - MetaEntryPtr resolveEntry(QString base, QString resource_path, - QString expected_etag = QString()); + auto resolveEntry(QString base, QString resource_path, QString expected_etag = QString()) -> MetaEntryPtr; // add a previously resolved stale entry - bool updateEntry(MetaEntryPtr stale_entry); + auto updateEntry(MetaEntryPtr stale_entry) -> bool; // evict selected entry from cache - bool evictEntry(MetaEntryPtr entry); + auto evictEntry(MetaEntryPtr entry) -> bool; void addBase(QString base, QString base_root); // (re)start a timer that calls SaveNow later. void SaveEventually(); void Load(); - QString getBasePath(QString base); -public -slots: + + auto getBasePath(QString base) -> QString; + + public slots: void SaveNow(); -private: + private: // create a new stale entry, given the parameters - MetaEntryPtr staleEntry(QString base, QString resource_path); - struct EntryMap - { + auto staleEntry(QString base, QString resource_path) -> MetaEntryPtr; + + struct EntryMap { QString base_path; QMap entry_list; }; + QMap m_entries; QString m_index_file; QTimer saveBatchingTimer; diff --git a/launcher/net/Mode.h b/launcher/net/Mode.h index 9a95f5ad..3d75981f 100644 --- a/launcher/net/Mode.h +++ b/launcher/net/Mode.h @@ -1,10 +1,5 @@ #pragma once -namespace Net -{ -enum class Mode -{ - Offline, - Online -}; +namespace Net { +enum class Mode { Offline, Online }; } diff --git a/launcher/net/Sink.h b/launcher/net/Sink.h index 3b2a7f8d..c8800220 100644 --- a/launcher/net/Sink.h +++ b/launcher/net/Sink.h @@ -8,14 +8,15 @@ namespace Net { class Sink { public: Sink() = default; - virtual ~Sink(){}; + virtual ~Sink() = default; public: - virtual Task::State init(QNetworkRequest& request) = 0; - virtual Task::State write(QByteArray& data) = 0; - virtual Task::State abort() = 0; - virtual Task::State finalize(QNetworkReply& reply) = 0; - virtual bool hasLocalData() = 0; + virtual auto init(QNetworkRequest& request) -> Task::State = 0; + virtual auto write(QByteArray& data) -> Task::State = 0; + virtual auto abort() -> Task::State = 0; + virtual auto finalize(QNetworkReply& reply) -> Task::State = 0; + + virtual auto hasLocalData() -> bool = 0; void addValidator(Validator* validator) { @@ -24,7 +25,15 @@ class Sink { } } - protected: /* methods */ + protected: + bool initAllValidators(QNetworkRequest& request) + { + for (auto& validator : validators) { + if (!validator->init(request)) + return false; + } + return true; + } bool finalizeAllValidators(QNetworkReply& reply) { for (auto& validator : validators) { @@ -41,14 +50,6 @@ class Sink { } return success; } - bool initAllValidators(QNetworkRequest& request) - { - for (auto& validator : validators) { - if (!validator->init(request)) - return false; - } - return true; - } bool writeAllValidators(QByteArray& data) { for (auto& validator : validators) { @@ -58,7 +59,7 @@ class Sink { return true; } - protected: /* data */ + protected: std::vector> validators; }; } // namespace Net From 57d65177c8ebb5463c88dd8e26f1e0a33f648bed Mon Sep 17 00:00:00 2001 From: flow Date: Sun, 1 May 2022 11:05:31 -0300 Subject: [PATCH 12/67] fix: abort and fail logic in tasks Also sets up correctly the status connections --- launcher/net/Download.cpp | 9 ++++++--- launcher/net/NetJob.cpp | 3 +++ launcher/tasks/Task.cpp | 1 + launcher/tasks/Task.h | 2 +- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/launcher/net/Download.cpp b/launcher/net/Download.cpp index 3d6ca338..9c01fa8d 100644 --- a/launcher/net/Download.cpp +++ b/launcher/net/Download.cpp @@ -69,6 +69,8 @@ void Download::addValidator(Validator* v) void Download::executeTask() { + setStatus(tr("Downloading %1").arg(m_url.toString())); + if (getState() == Task::State::AbortedByUser) { qWarning() << "Attempt to start an aborted Download:" << m_url.toString(); emitAborted(); @@ -90,6 +92,7 @@ void Download::executeTask() emitFailed(); return; case State::AbortedByUser: + emitAborted(); return; } @@ -216,13 +219,13 @@ void Download::downloadFinished() qDebug() << "Download failed in previous step:" << m_url.toString(); m_sink->abort(); m_reply.reset(); - emitFailed(); + emit failed(""); return; } else if (m_state == State::AbortedByUser) { qDebug() << "Download aborted in previous step:" << m_url.toString(); m_sink->abort(); m_reply.reset(); - emitAborted(); + emit aborted(); return; } @@ -239,7 +242,7 @@ void Download::downloadFinished() qDebug() << "Download failed to finalize:" << m_url.toString(); m_sink->abort(); m_reply.reset(); - emitFailed(); + emit failed(""); return; } diff --git a/launcher/net/NetJob.cpp b/launcher/net/NetJob.cpp index a9f89da4..906a735f 100644 --- a/launcher/net/NetJob.cpp +++ b/launcher/net/NetJob.cpp @@ -47,7 +47,9 @@ auto NetJob::addNetAction(NetAction::Ptr action) -> bool if (action->isRunning()) { connect(action.get(), &NetAction::succeeded, [this, action]{ partSucceeded(action->index()); }); connect(action.get(), &NetAction::failed, [this, action](QString){ partFailed(action->index()); }); + connect(action.get(), &NetAction::aborted, [this, action](){ partAborted(action->index()); }); connect(action.get(), &NetAction::progress, [this, action](qint64 done, qint64 total) { partProgress(action->index(), done, total); }); + connect(action.get(), &NetAction::status, this, &NetJob::status); } else { m_todo.append(m_parts_progress.size() - 1); } @@ -222,6 +224,7 @@ void NetJob::startMoreParts() connect(part.get(), &NetAction::failed, this, [this, part](QString){ partFailed(part->index()); }); connect(part.get(), &NetAction::aborted, this, [this, part]{ partAborted(part->index()); }); connect(part.get(), &NetAction::progress, this, [this, part](qint64 done, qint64 total) { partProgress(part->index(), done, total); }); + connect(part.get(), &NetAction::status, this, &NetJob::status); part->startAction(m_network); } diff --git a/launcher/tasks/Task.cpp b/launcher/tasks/Task.cpp index 68e0e8a7..d2d62c9e 100644 --- a/launcher/tasks/Task.cpp +++ b/launcher/tasks/Task.cpp @@ -100,6 +100,7 @@ void Task::emitAborted() m_failReason = "Aborted."; qDebug() << "Task" << describe() << "aborted."; emit aborted(); + emit finished(); } void Task::emitSucceeded() diff --git a/launcher/tasks/Task.h b/launcher/tasks/Task.h index e09c57ae..0ca37e02 100644 --- a/launcher/tasks/Task.h +++ b/launcher/tasks/Task.h @@ -79,7 +79,7 @@ class Task : public QObject { public slots: virtual void start(); - virtual bool abort() { return false; }; + virtual bool abort() { if(canAbort()) emitAborted(); return canAbort(); }; protected: virtual void executeTask() = 0; From 0bce08d30f2bbdeca19c375840880f69ffeac81b Mon Sep 17 00:00:00 2001 From: flow Date: Mon, 2 May 2022 12:56:24 -0300 Subject: [PATCH 13/67] chore: add polymc license headers to launcher/net files --- launcher/net/ByteArraySink.h | 35 ++++++++++++++++++++++++++ launcher/net/ChecksumValidator.h | 35 ++++++++++++++++++++++++++ launcher/net/Download.cpp | 41 ++++++++++++++++++++++-------- launcher/net/Download.h | 40 +++++++++++++++++++++-------- launcher/net/FileSink.cpp | 35 ++++++++++++++++++++++++++ launcher/net/FileSink.h | 35 ++++++++++++++++++++++++++ launcher/net/HttpMetaCache.cpp | 40 +++++++++++++++++++++-------- launcher/net/HttpMetaCache.h | 43 ++++++++++++++++++++++++-------- launcher/net/MetaCacheSink.cpp | 35 ++++++++++++++++++++++++++ launcher/net/MetaCacheSink.h | 35 ++++++++++++++++++++++++++ launcher/net/NetAction.h | 1 + launcher/net/NetJob.cpp | 1 + launcher/net/NetJob.h | 1 + launcher/net/PasteUpload.cpp | 35 ++++++++++++++++++++++++++ launcher/net/PasteUpload.h | 36 ++++++++++++++++++++++++++ launcher/net/Sink.h | 35 ++++++++++++++++++++++++++ launcher/net/Validator.h | 35 ++++++++++++++++++++++++++ 17 files changed, 476 insertions(+), 42 deletions(-) diff --git a/launcher/net/ByteArraySink.h b/launcher/net/ByteArraySink.h index 8ae30bb3..501318a1 100644 --- a/launcher/net/ByteArraySink.h +++ b/launcher/net/ByteArraySink.h @@ -1,3 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 flowln + * + * 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 . + * + * 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 #include "Sink.h" diff --git a/launcher/net/ChecksumValidator.h b/launcher/net/ChecksumValidator.h index 8a8b10d5..a2ca2c7a 100644 --- a/launcher/net/ChecksumValidator.h +++ b/launcher/net/ChecksumValidator.h @@ -1,3 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 flowln + * + * 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 . + * + * 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 #include "Validator.h" diff --git a/launcher/net/Download.cpp b/launcher/net/Download.cpp index 9c01fa8d..97033de1 100644 --- a/launcher/net/Download.cpp +++ b/launcher/net/Download.cpp @@ -1,22 +1,41 @@ -/* Copyright 2013-2021 MultiMC Contributors +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 flowln * - * 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. */ #include "Download.h" #include -#include #include #include "ByteArraySink.h" diff --git a/launcher/net/Download.h b/launcher/net/Download.h index 9fb67127..20932944 100644 --- a/launcher/net/Download.h +++ b/launcher/net/Download.h @@ -1,16 +1,36 @@ -/* Copyright 2013-2021 MultiMC Contributors +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 flowln * - * 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 diff --git a/launcher/net/FileSink.cpp b/launcher/net/FileSink.cpp index d2d2b06f..ba0caf6c 100644 --- a/launcher/net/FileSink.cpp +++ b/launcher/net/FileSink.cpp @@ -1,3 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 flowln + * + * 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 . + * + * 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. + */ + #include "FileSink.h" #include "FileSystem.h" diff --git a/launcher/net/FileSink.h b/launcher/net/FileSink.h index 9d77b3d0..dffbdca6 100644 --- a/launcher/net/FileSink.h +++ b/launcher/net/FileSink.h @@ -1,3 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 flowln + * + * 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 . + * + * 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 #include diff --git a/launcher/net/HttpMetaCache.cpp b/launcher/net/HttpMetaCache.cpp index b41a18b1..4d86c0b8 100644 --- a/launcher/net/HttpMetaCache.cpp +++ b/launcher/net/HttpMetaCache.cpp @@ -1,16 +1,36 @@ -/* Copyright 2013-2021 MultiMC Contributors +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 flowln * - * 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. */ #include "HttpMetaCache.h" diff --git a/launcher/net/HttpMetaCache.h b/launcher/net/HttpMetaCache.h index d8d1608e..e944b3d5 100644 --- a/launcher/net/HttpMetaCache.h +++ b/launcher/net/HttpMetaCache.h @@ -1,22 +1,43 @@ -/* Copyright 2013-2021 MultiMC Contributors +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 flowln * - * 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 -#include + #include #include +#include #include class HttpMetaCache; diff --git a/launcher/net/MetaCacheSink.cpp b/launcher/net/MetaCacheSink.cpp index 34ba9f56..f86dd870 100644 --- a/launcher/net/MetaCacheSink.cpp +++ b/launcher/net/MetaCacheSink.cpp @@ -1,3 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 flowln + * + * 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 . + * + * 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. + */ + #include "MetaCacheSink.h" #include #include diff --git a/launcher/net/MetaCacheSink.h b/launcher/net/MetaCacheSink.h index 431e10a8..c9f7edfe 100644 --- a/launcher/net/MetaCacheSink.h +++ b/launcher/net/MetaCacheSink.h @@ -1,3 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 flowln + * + * 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 . + * + * 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 #include "ChecksumValidator.h" diff --git a/launcher/net/NetAction.h b/launcher/net/NetAction.h index 86a37ee6..729d4132 100644 --- a/launcher/net/NetAction.h +++ b/launcher/net/NetAction.h @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* * PolyMC - Minecraft Launcher + * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/net/NetJob.cpp b/launcher/net/NetJob.cpp index 906a735f..df899178 100644 --- a/launcher/net/NetJob.cpp +++ b/launcher/net/NetJob.cpp @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* * PolyMC - Minecraft Launcher + * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/net/NetJob.h b/launcher/net/NetJob.h index c397e2a1..63c1cf51 100644 --- a/launcher/net/NetJob.h +++ b/launcher/net/NetJob.h @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* * PolyMC - Minecraft Launcher + * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/net/PasteUpload.cpp b/launcher/net/PasteUpload.cpp index 52b82a0e..e88c8987 100644 --- a/launcher/net/PasteUpload.cpp +++ b/launcher/net/PasteUpload.cpp @@ -1,3 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 flowln + * + * 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 . + * + * 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. + */ + #include "PasteUpload.h" #include "BuildConfig.h" #include "Application.h" diff --git a/launcher/net/PasteUpload.h b/launcher/net/PasteUpload.h index 62b2dc36..53979352 100644 --- a/launcher/net/PasteUpload.h +++ b/launcher/net/PasteUpload.h @@ -1,4 +1,40 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 flowln + * + * 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 . + * + * 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 + #include "tasks/Task.h" #include #include diff --git a/launcher/net/Sink.h b/launcher/net/Sink.h index c8800220..3870f29b 100644 --- a/launcher/net/Sink.h +++ b/launcher/net/Sink.h @@ -1,3 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 flowln + * + * 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 . + * + * 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 #include "net/NetAction.h" diff --git a/launcher/net/Validator.h b/launcher/net/Validator.h index 59b72a0b..e1d71d1c 100644 --- a/launcher/net/Validator.h +++ b/launcher/net/Validator.h @@ -1,3 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 flowln + * + * 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 . + * + * 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 #include "net/NetAction.h" From dd2b324d8f7081f52decd90210ce11ef37625315 Mon Sep 17 00:00:00 2001 From: flow Date: Mon, 2 May 2022 14:33:21 -0300 Subject: [PATCH 14/67] chore: add license header to remaining files Also remove some unused imports --- launcher/InstanceImportTask.cpp | 40 ++++++++++++++----- launcher/minecraft/AssetsUtils.cpp | 40 ++++++++++++++----- launcher/screenshots/ImgurAlbumCreation.cpp | 35 ++++++++++++++++ launcher/screenshots/ImgurAlbumCreation.h | 37 ++++++++++++++++- launcher/screenshots/ImgurUpload.cpp | 35 ++++++++++++++++ launcher/screenshots/ImgurUpload.h | 37 ++++++++++++++++- launcher/tasks/Task.cpp | 40 ++++++++++++++----- launcher/tasks/Task.h | 44 ++++++++++++++------- launcher/translations/TranslationsModel.cpp | 35 ++++++++++++++++ 9 files changed, 297 insertions(+), 46 deletions(-) diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp index fc3432c1..ca7e0590 100644 --- a/launcher/InstanceImportTask.cpp +++ b/launcher/InstanceImportTask.cpp @@ -1,16 +1,36 @@ -/* Copyright 2013-2021 MultiMC Contributors +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 flowln * - * 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. */ #include "InstanceImportTask.h" diff --git a/launcher/minecraft/AssetsUtils.cpp b/launcher/minecraft/AssetsUtils.cpp index 281f730f..15062c2b 100644 --- a/launcher/minecraft/AssetsUtils.cpp +++ b/launcher/minecraft/AssetsUtils.cpp @@ -1,16 +1,36 @@ -/* Copyright 2013-2021 MultiMC Contributors +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 flowln * - * 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. */ #include diff --git a/launcher/screenshots/ImgurAlbumCreation.cpp b/launcher/screenshots/ImgurAlbumCreation.cpp index f94527c8..7afdc5cc 100644 --- a/launcher/screenshots/ImgurAlbumCreation.cpp +++ b/launcher/screenshots/ImgurAlbumCreation.cpp @@ -1,3 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 flowln + * + * 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 . + * + * 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. + */ + #include "ImgurAlbumCreation.h" #include diff --git a/launcher/screenshots/ImgurAlbumCreation.h b/launcher/screenshots/ImgurAlbumCreation.h index 4cb0ed5d..0228b6e4 100644 --- a/launcher/screenshots/ImgurAlbumCreation.h +++ b/launcher/screenshots/ImgurAlbumCreation.h @@ -1,7 +1,42 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 flowln + * + * 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 . + * + * 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 + #include "net/NetAction.h" #include "Screenshot.h" -#include "QObjectPtr.h" typedef shared_qobject_ptr ImgurAlbumCreationPtr; class ImgurAlbumCreation : public NetAction diff --git a/launcher/screenshots/ImgurUpload.cpp b/launcher/screenshots/ImgurUpload.cpp index 05314de7..fbcfb95f 100644 --- a/launcher/screenshots/ImgurUpload.cpp +++ b/launcher/screenshots/ImgurUpload.cpp @@ -1,3 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 flowln + * + * 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 . + * + * 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. + */ + #include "ImgurUpload.h" #include "BuildConfig.h" diff --git a/launcher/screenshots/ImgurUpload.h b/launcher/screenshots/ImgurUpload.h index a1040551..404dc876 100644 --- a/launcher/screenshots/ImgurUpload.h +++ b/launcher/screenshots/ImgurUpload.h @@ -1,5 +1,40 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 flowln + * + * 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 . + * + * 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 -#include "QObjectPtr.h" + #include "net/NetAction.h" #include "Screenshot.h" diff --git a/launcher/tasks/Task.cpp b/launcher/tasks/Task.cpp index d2d62c9e..bb71b98c 100644 --- a/launcher/tasks/Task.cpp +++ b/launcher/tasks/Task.cpp @@ -1,16 +1,36 @@ -/* Copyright 2013-2021 MultiMC Contributors +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 flowln * - * 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. */ #include "Task.h" diff --git a/launcher/tasks/Task.h b/launcher/tasks/Task.h index 0ca37e02..f0e6e402 100644 --- a/launcher/tasks/Task.h +++ b/launcher/tasks/Task.h @@ -1,24 +1,40 @@ -/* Copyright 2013-2021 MultiMC Contributors +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 flowln * - * 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 -#include -#include -#include - #include "QObjectPtr.h" class Task : public QObject { diff --git a/launcher/translations/TranslationsModel.cpp b/launcher/translations/TranslationsModel.cpp index fbd17060..53722d69 100644 --- a/launcher/translations/TranslationsModel.cpp +++ b/launcher/translations/TranslationsModel.cpp @@ -1,3 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 flowln + * + * 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 . + * + * 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. + */ + #include "TranslationsModel.h" #include From 067484a6a8647e6012f3fdad61653716cfb44470 Mon Sep 17 00:00:00 2001 From: icelimetea Date: Fri, 13 May 2022 16:59:00 +0100 Subject: [PATCH 15/67] Fix formatting --- libraries/launcher/org/multimc/EntryPoint.java | 4 +++- libraries/launcher/org/multimc/applet/LegacyFrame.java | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/libraries/launcher/org/multimc/EntryPoint.java b/libraries/launcher/org/multimc/EntryPoint.java index 416f2189..0244a04d 100644 --- a/libraries/launcher/org/multimc/EntryPoint.java +++ b/libraries/launcher/org/multimc/EntryPoint.java @@ -1,4 +1,4 @@ -package org.multimc;/* +/* * Copyright 2012-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,6 +14,8 @@ package org.multimc;/* * limitations under the License. */ +package org.multimc; + import org.multimc.exception.ParseException; import org.multimc.utils.Parameters; diff --git a/libraries/launcher/org/multimc/applet/LegacyFrame.java b/libraries/launcher/org/multimc/applet/LegacyFrame.java index f82cb605..caec079c 100644 --- a/libraries/launcher/org/multimc/applet/LegacyFrame.java +++ b/libraries/launcher/org/multimc/applet/LegacyFrame.java @@ -1,4 +1,4 @@ -package org.multimc.applet;/* +/* * Copyright 2012-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,6 +14,8 @@ package org.multimc.applet;/* * limitations under the License. */ +package org.multimc.applet; + import net.minecraft.Launcher; import javax.imageio.ImageIO; From c054d0f329a9d1d3ae76a605d82f0ad8e0ebdc99 Mon Sep 17 00:00:00 2001 From: icelimetea Date: Fri, 13 May 2022 17:21:35 +0100 Subject: [PATCH 16/67] Add the license header to LauncherFactory --- .../launcher/org/multimc/LauncherFactory.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/libraries/launcher/org/multimc/LauncherFactory.java b/libraries/launcher/org/multimc/LauncherFactory.java index 17e0d905..007ce7e8 100644 --- a/libraries/launcher/org/multimc/LauncherFactory.java +++ b/libraries/launcher/org/multimc/LauncherFactory.java @@ -1,3 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 icelimetea, + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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.multimc; import org.multimc.impl.OneSixLauncher; From c3336251e0789fae6da5935c0e2b7f38eab08763 Mon Sep 17 00:00:00 2001 From: icelimetea Date: Fri, 13 May 2022 18:10:11 +0100 Subject: [PATCH 17/67] Add the license header to EntryPoint --- .../launcher/org/multimc/EntryPoint.java | 39 ++++++++++++++----- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/libraries/launcher/org/multimc/EntryPoint.java b/libraries/launcher/org/multimc/EntryPoint.java index 0244a04d..ba5b0926 100644 --- a/libraries/launcher/org/multimc/EntryPoint.java +++ b/libraries/launcher/org/multimc/EntryPoint.java @@ -1,17 +1,36 @@ +// SPDX-License-Identifier: GPL-3.0-only /* - * Copyright 2012-2021 MultiMC Contributors + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 icelimetea, * - * 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. */ package org.multimc; From fac0b027b31ba2ac29730f6091b3d19ba78b40d2 Mon Sep 17 00:00:00 2001 From: icelimetea Date: Sat, 14 May 2022 16:46:57 +0100 Subject: [PATCH 18/67] Fix the license header --- .../launcher/org/multimc/LauncherFactory.java | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/libraries/launcher/org/multimc/LauncherFactory.java b/libraries/launcher/org/multimc/LauncherFactory.java index 007ce7e8..1b30a415 100644 --- a/libraries/launcher/org/multimc/LauncherFactory.java +++ b/libraries/launcher/org/multimc/LauncherFactory.java @@ -14,23 +14,6 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - * Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. */ package org.multimc; From 3f259eb97a207c6d4d0ae3ad481541eda96df798 Mon Sep 17 00:00:00 2001 From: icelimetea Date: Sat, 14 May 2022 16:48:14 +0100 Subject: [PATCH 19/67] Refactor script parsing --- .../launcher/org/multimc/EntryPoint.java | 43 ++++++------------- .../launcher/org/multimc/LauncherFactory.java | 4 +- 2 files changed, 16 insertions(+), 31 deletions(-) diff --git a/libraries/launcher/org/multimc/EntryPoint.java b/libraries/launcher/org/multimc/EntryPoint.java index ba5b0926..c0500bbe 100644 --- a/libraries/launcher/org/multimc/EntryPoint.java +++ b/libraries/launcher/org/multimc/EntryPoint.java @@ -51,8 +51,6 @@ public final class EntryPoint { private final Parameters params = new Parameters(); - private String launcherType; - public static void main(String[] args) { EntryPoint listener = new EntryPoint(); @@ -80,15 +78,6 @@ public final class EntryPoint { return Action.Abort; } - case "launcher": { - if (tokens.length != 2) - throw new ParseException("Expected 2 tokens, got " + tokens.length); - - launcherType = tokens[1]; - - return Action.Proceed; - } - default: { if (tokens.length != 2) throw new ParseException("Error while parsing:" + inData); @@ -129,30 +118,24 @@ public final class EntryPoint { return 1; } - if (launcherType != null) { - try { - Launcher launcher = - LauncherFactory - .getInstance() - .createLauncher(launcherType, params); + try { + Launcher launcher = + LauncherFactory + .getInstance() + .createLauncher(params); - launcher.launch(); + launcher.launch(); - return 0; - } catch (IllegalArgumentException e) { - LOGGER.log(Level.SEVERE, "Wrong argument.", e); + return 0; + } catch (IllegalArgumentException e) { + LOGGER.log(Level.SEVERE, "Wrong argument.", e); - return 1; - } catch (Exception e) { - LOGGER.log(Level.SEVERE, "Exception caught from launcher.", e); + return 1; + } catch (Exception e) { + LOGGER.log(Level.SEVERE, "Exception caught from launcher.", e); - return 1; - } + return 1; } - - LOGGER.log(Level.SEVERE, "No valid launcher implementation specified."); - - return 1; } private enum Action { diff --git a/libraries/launcher/org/multimc/LauncherFactory.java b/libraries/launcher/org/multimc/LauncherFactory.java index 1b30a415..a2af8581 100644 --- a/libraries/launcher/org/multimc/LauncherFactory.java +++ b/libraries/launcher/org/multimc/LauncherFactory.java @@ -39,7 +39,9 @@ public final class LauncherFactory { }); } - public Launcher createLauncher(String name, Parameters parameters) { + public Launcher createLauncher(Parameters parameters) { + String name = parameters.first("launcher"); + LauncherProvider launcherProvider = launcherRegistry.get(name); if (launcherProvider == null) From 2b52cf01f5999db4a8b1ea009cb5d24dd4eb4e1c Mon Sep 17 00:00:00 2001 From: Kenneth Chew Date: Sat, 14 May 2022 19:51:23 -0400 Subject: [PATCH 20/67] Build Windows installer --- .github/workflows/build.yml | 17 +++- program_info/win_install.nsi | 169 +++++++++++++++++++++++++++++++++++ 2 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 program_info/win_install.nsi diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0590b348..7ab30d45 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -63,6 +63,7 @@ jobs: ninja:p qt5:p ccache:p + nsis:p - name: Setup ccache if: runner.os != 'Windows' && inputs.build_type == 'Debug' @@ -100,7 +101,7 @@ jobs: run: | brew update brew install qt@5 ninja - + - name: Update Qt (AppImage) if: runner.os == 'Linux' && matrix.appimage == true run: | @@ -190,6 +191,13 @@ jobs: 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 + - name: Package (Windows, installer) + if: runner.os == 'Windows' + shell: msys2 {0} + run: | + cd ${{ env.INSTALL_PORTABLE_DIR }} + makensis -NOCD "-DVERSION=${{ env.VERSION }}" "-DMUI_ICON=${{ github.workspace }}/program_info/polymc.ico" "-XOutFile ${{ github.workspace }}/PolyMC-Setup.exe" "${{ github.workspace }}/program_info/win_install.nsi" + - name: Package (Linux) if: runner.os == 'Linux' && matrix.appimage != true run: | @@ -257,6 +265,13 @@ jobs: name: PolyMC-${{ matrix.name }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }} path: ${{ env.INSTALL_PORTABLE_DIR }}/** + - name: Upload installer (Windows) + if: runner.os == 'Windows' + uses: actions/upload-artifact@v3 + with: + name: PolyMC-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}-Setup + path: PolyMC-Setup.exe + - name: Upload binary tarball (Linux) if: runner.os == 'Linux' && matrix.appimage != true uses: actions/upload-artifact@v3 diff --git a/program_info/win_install.nsi b/program_info/win_install.nsi new file mode 100644 index 00000000..2b0d9760 --- /dev/null +++ b/program_info/win_install.nsi @@ -0,0 +1,169 @@ +!define MULTIUSER_EXECUTIONLEVEL Highest +!define MULTIUSER_MUI +!define MULTIUSER_INSTALLMODE_COMMANDLINE + +!define MULTIUSER_INSTALLMODE_INSTDIR PolyMC +!define MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_KEY Software\PolyMC +!define MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_VALUENAME InstallDir + +!include "FileFunc.nsh" +!include "MUI2.nsh" +!include "MultiUser.nsh" + +Name "PolyMC" +RequestExecutionLevel highest + +;-------------------------------- + +; Pages + +!insertmacro MUI_PAGE_WELCOME +!insertmacro MULTIUSER_PAGE_INSTALLMODE +!define MUI_COMPONENTSPAGE_NODESC +!insertmacro MUI_PAGE_COMPONENTS +!insertmacro MUI_PAGE_DIRECTORY +!insertmacro MUI_PAGE_INSTFILES +!define MUI_FINISHPAGE_RUN "$InstDir\polymc.exe" +!insertmacro MUI_PAGE_FINISH + +!insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_INSTFILES + +!insertmacro MUI_LANGUAGE "English" + +;-------------------------------- + +; The stuff to install +Section "PolyMC" + + SectionIn RO + + nsExec::Exec /TIMEOUT=2000 'TaskKill /IM polymc.exe /F' + + SetOutPath $INSTDIR + + File "polymc.exe" + File "qt.conf" + File *.dll + File /r "iconengines" + File /r "imageformats" + File /r "jars" + File /r "platforms" + File /r "styles" + + ; Write the installation path into the registry + WriteRegStr SHCTX SOFTWARE\PolyMC "InstallDir" "$INSTDIR" + + ; Write the uninstall keys for Windows + !define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\PolyMC" + WriteRegStr SHCTX "${UNINST_KEY}" "DisplayName" "PolyMC" + WriteRegStr SHCTX "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\polymc.exe" + WriteRegStr SHCTX "${UNINST_KEY}" "UninstallString" '"$INSTDIR\uninstall.exe" /$MultiUser.InstallMode' + WriteRegStr SHCTX "${UNINST_KEY}" "QuietUninstallString" '"$INSTDIR\uninstall.exe" /$MultiUser.InstallMode /S' + WriteRegStr SHCTX "${UNINST_KEY}" "InstallLocation" "$INSTDIR" + WriteRegStr SHCTX "${UNINST_KEY}" "Publisher" "PolyMC Contributors" + WriteRegStr SHCTX "${UNINST_KEY}" "ProductVersion" "${VERSION}" + ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2 + IntFmt $0 "0x%08X" $0 + WriteRegDWORD SHCTX "${UNINST_KEY}" "EstimatedSize" "$0" + WriteRegDWORD SHCTX "${UNINST_KEY}" "NoModify" 1 + WriteRegDWORD SHCTX "${UNINST_KEY}" "NoRepair" 1 + WriteUninstaller "$INSTDIR\uninstall.exe" + +SectionEnd + +Section "Start Menu Shortcuts" + + CreateShortcut "$SMPROGRAMS\PolyMC.lnk" "$INSTDIR\polymc.exe" "" "$INSTDIR\polymc.exe" 0 + +SectionEnd + +Section /o "Portable" + + SetOutPath $INSTDIR + File "portable.txt" + +SectionEnd + +;-------------------------------- + +; Uninstaller + +Section "Uninstall" + + nsExec::Exec /TIMEOUT=2000 'TaskKill /IM polymc.exe /F' + + DeleteRegKey SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\PolyMC" + DeleteRegKey SHCTX SOFTWARE\PolyMC + + Delete $INSTDIR\polymc.exe + Delete $INSTDIR\uninstall.exe + Delete $INSTDIR\portable.txt + + Delete $INSTDIR\libbrotlicommon.dll + Delete $INSTDIR\libbrotlidec.dll + Delete $INSTDIR\libbz2-1.dll + Delete $INSTDIR\libcrypto-1_1-x64.dll + Delete $INSTDIR\libcrypto-1_1.dll + Delete $INSTDIR\libdouble-conversion.dll + Delete $INSTDIR\libfreetype-6.dll + Delete $INSTDIR\libgcc_s_seh-1.dll + Delete $INSTDIR\libgcc_s_dw2-1.dll + Delete $INSTDIR\libglib-2.0-0.dll + Delete $INSTDIR\libgraphite2.dll + Delete $INSTDIR\libharfbuzz-0.dll + Delete $INSTDIR\libiconv-2.dll + Delete $INSTDIR\libicudt69.dll + Delete $INSTDIR\libicuin69.dll + Delete $INSTDIR\libicuuc69.dll + Delete $INSTDIR\libintl-8.dll + Delete $INSTDIR\libjasper-4.dll + Delete $INSTDIR\libjpeg-8.dll + Delete $INSTDIR\libmd4c.dll + Delete $INSTDIR\libpcre-1.dll + Delete $INSTDIR\libpcre2-16-0.dll + Delete $INSTDIR\libpng16-16.dll + Delete $INSTDIR\libssl-1_1-x64.dll + Delete $INSTDIR\libssl-1_1.dll + Delete $INSTDIR\libssp-0.dll + Delete $INSTDIR\libstdc++-6.dll + Delete $INSTDIR\libwebp-7.dll + Delete $INSTDIR\libwebpdemux-2.dll + Delete $INSTDIR\libwebpmux-3.dll + Delete $INSTDIR\libwinpthread-1.dll + Delete $INSTDIR\libzstd.dll + Delete $INSTDIR\Qt5Core.dll + Delete $INSTDIR\Qt5Gui.dll + Delete $INSTDIR\Qt5Network.dll + Delete $INSTDIR\Qt5Qml.dll + Delete $INSTDIR\Qt5QmlModels.dll + Delete $INSTDIR\Qt5Quick.dll + Delete $INSTDIR\Qt5Svg.dll + Delete $INSTDIR\Qt5WebSockets.dll + Delete $INSTDIR\Qt5Widgets.dll + Delete $INSTDIR\Qt5Xml.dll + Delete $INSTDIR\zlib1.dll + + Delete $INSTDIR\qt.conf + + RMDir /r $INSTDIR\iconengines + RMDir /r $INSTDIR\imageformats + RMDir /r $INSTDIR\jars + RMDir /r $INSTDIR\platforms + RMDir /r $INSTDIR\styles + + Delete "$SMPROGRAMS\PolyMC.lnk" + + RMDir "$INSTDIR" + +SectionEnd + +; Multi-user + +Function .onInit + !insertmacro MULTIUSER_INIT +FunctionEnd + +Function un.onInit + !insertmacro MULTIUSER_UNINIT +FunctionEnd From 2993318d195812f4b03c703aa9e68aeff941aece Mon Sep 17 00:00:00 2001 From: Kenneth Chew Date: Mon, 16 May 2022 15:29:37 -0400 Subject: [PATCH 21/67] Remove admin requirement (no multi-user install option) --- program_info/win_install.nsi | 54 +++++++++++++----------------------- 1 file changed, 20 insertions(+), 34 deletions(-) diff --git a/program_info/win_install.nsi b/program_info/win_install.nsi index 2b0d9760..18a1b64e 100644 --- a/program_info/win_install.nsi +++ b/program_info/win_install.nsi @@ -1,24 +1,16 @@ -!define MULTIUSER_EXECUTIONLEVEL Highest -!define MULTIUSER_MUI -!define MULTIUSER_INSTALLMODE_COMMANDLINE - -!define MULTIUSER_INSTALLMODE_INSTDIR PolyMC -!define MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_KEY Software\PolyMC -!define MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_VALUENAME InstallDir - !include "FileFunc.nsh" !include "MUI2.nsh" -!include "MultiUser.nsh" Name "PolyMC" -RequestExecutionLevel highest +InstallDir "$LOCALAPPDATA\PolyMC" +InstallDirRegKey HKCU "Software\PolyMC" "InstallDir" +RequestExecutionLevel user ;-------------------------------- ; Pages !insertmacro MUI_PAGE_WELCOME -!insertmacro MULTIUSER_PAGE_INSTALLMODE !define MUI_COMPONENTSPAGE_NODESC !insertmacro MUI_PAGE_COMPONENTS !insertmacro MUI_PAGE_DIRECTORY @@ -29,6 +21,10 @@ RequestExecutionLevel highest !insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_INSTFILES +;-------------------------------- + +; Languages + !insertmacro MUI_LANGUAGE "English" ;-------------------------------- @@ -52,22 +48,22 @@ Section "PolyMC" File /r "styles" ; Write the installation path into the registry - WriteRegStr SHCTX SOFTWARE\PolyMC "InstallDir" "$INSTDIR" + WriteRegStr HKCU Software\PolyMC "InstallDir" "$INSTDIR" ; Write the uninstall keys for Windows !define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\PolyMC" - WriteRegStr SHCTX "${UNINST_KEY}" "DisplayName" "PolyMC" - WriteRegStr SHCTX "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\polymc.exe" - WriteRegStr SHCTX "${UNINST_KEY}" "UninstallString" '"$INSTDIR\uninstall.exe" /$MultiUser.InstallMode' - WriteRegStr SHCTX "${UNINST_KEY}" "QuietUninstallString" '"$INSTDIR\uninstall.exe" /$MultiUser.InstallMode /S' - WriteRegStr SHCTX "${UNINST_KEY}" "InstallLocation" "$INSTDIR" - WriteRegStr SHCTX "${UNINST_KEY}" "Publisher" "PolyMC Contributors" - WriteRegStr SHCTX "${UNINST_KEY}" "ProductVersion" "${VERSION}" + WriteRegStr HKCU "${UNINST_KEY}" "DisplayName" "PolyMC" + WriteRegStr HKCU "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\polymc.exe" + WriteRegStr HKCU "${UNINST_KEY}" "UninstallString" '"$INSTDIR\uninstall.exe"' + WriteRegStr HKCU "${UNINST_KEY}" "QuietUninstallString" '"$INSTDIR\uninstall.exe" /S' + WriteRegStr HKCU "${UNINST_KEY}" "InstallLocation" "$INSTDIR" + WriteRegStr HKCU "${UNINST_KEY}" "Publisher" "PolyMC Contributors" + WriteRegStr HKCU "${UNINST_KEY}" "ProductVersion" "${VERSION}" ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2 IntFmt $0 "0x%08X" $0 - WriteRegDWORD SHCTX "${UNINST_KEY}" "EstimatedSize" "$0" - WriteRegDWORD SHCTX "${UNINST_KEY}" "NoModify" 1 - WriteRegDWORD SHCTX "${UNINST_KEY}" "NoRepair" 1 + WriteRegDWORD HKCU "${UNINST_KEY}" "EstimatedSize" "$0" + WriteRegDWORD HKCU "${UNINST_KEY}" "NoModify" 1 + WriteRegDWORD HKCU "${UNINST_KEY}" "NoRepair" 1 WriteUninstaller "$INSTDIR\uninstall.exe" SectionEnd @@ -93,8 +89,8 @@ Section "Uninstall" nsExec::Exec /TIMEOUT=2000 'TaskKill /IM polymc.exe /F' - DeleteRegKey SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\PolyMC" - DeleteRegKey SHCTX SOFTWARE\PolyMC + DeleteRegKey HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\PolyMC" + DeleteRegKey HKCU SOFTWARE\PolyMC Delete $INSTDIR\polymc.exe Delete $INSTDIR\uninstall.exe @@ -157,13 +153,3 @@ Section "Uninstall" RMDir "$INSTDIR" SectionEnd - -; Multi-user - -Function .onInit - !insertmacro MULTIUSER_INIT -FunctionEnd - -Function un.onInit - !insertmacro MULTIUSER_UNINIT -FunctionEnd From 6dfec4db40f09697f34f65419edb7d689e3c5dc7 Mon Sep 17 00:00:00 2001 From: Lenny McLennington Date: Tue, 17 May 2022 00:21:57 +0100 Subject: [PATCH 22/67] Fix toolbar disappearing in a certain circumstance. --- launcher/ui/MainWindow.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index ca345b1f..3f854511 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -1865,6 +1865,9 @@ void MainWindow::globalSettingsClosed() updateMainToolBar(); updateToolsMenu(); updateStatusCenter(); + // This needs to be done to prevent UI elements disappearing in the event the config is changed + // but PolyMC exits abnormally, causing the window state to never be saved: + APPLICATION->settings()->set("MainWindowState", saveState().toBase64()); update(); } From 85ec9d95a43cc884224095477e7321b84d2cc99f Mon Sep 17 00:00:00 2001 From: Kenneth Chew Date: Mon, 16 May 2022 19:28:04 -0400 Subject: [PATCH 23/67] Support installer languages other than English --- program_info/win_install.nsi | 68 ++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/program_info/win_install.nsi b/program_info/win_install.nsi index 18a1b64e..ce13b8b0 100644 --- a/program_info/win_install.nsi +++ b/program_info/win_install.nsi @@ -1,6 +1,8 @@ !include "FileFunc.nsh" !include "MUI2.nsh" +Unicode true + Name "PolyMC" InstallDir "$LOCALAPPDATA\PolyMC" InstallDirRegKey HKCU "Software\PolyMC" "InstallDir" @@ -26,6 +28,72 @@ RequestExecutionLevel user ; Languages !insertmacro MUI_LANGUAGE "English" +!insertmacro MUI_LANGUAGE "French" +!insertmacro MUI_LANGUAGE "German" +!insertmacro MUI_LANGUAGE "Spanish" +!insertmacro MUI_LANGUAGE "SpanishInternational" +!insertmacro MUI_LANGUAGE "SimpChinese" +!insertmacro MUI_LANGUAGE "TradChinese" +!insertmacro MUI_LANGUAGE "Japanese" +!insertmacro MUI_LANGUAGE "Korean" +!insertmacro MUI_LANGUAGE "Italian" +!insertmacro MUI_LANGUAGE "Dutch" +!insertmacro MUI_LANGUAGE "Danish" +!insertmacro MUI_LANGUAGE "Swedish" +!insertmacro MUI_LANGUAGE "Norwegian" +!insertmacro MUI_LANGUAGE "NorwegianNynorsk" +!insertmacro MUI_LANGUAGE "Finnish" +!insertmacro MUI_LANGUAGE "Greek" +!insertmacro MUI_LANGUAGE "Russian" +!insertmacro MUI_LANGUAGE "Portuguese" +!insertmacro MUI_LANGUAGE "PortugueseBR" +!insertmacro MUI_LANGUAGE "Polish" +!insertmacro MUI_LANGUAGE "Ukrainian" +!insertmacro MUI_LANGUAGE "Czech" +!insertmacro MUI_LANGUAGE "Slovak" +!insertmacro MUI_LANGUAGE "Croatian" +!insertmacro MUI_LANGUAGE "Bulgarian" +!insertmacro MUI_LANGUAGE "Hungarian" +!insertmacro MUI_LANGUAGE "Thai" +!insertmacro MUI_LANGUAGE "Romanian" +!insertmacro MUI_LANGUAGE "Latvian" +!insertmacro MUI_LANGUAGE "Macedonian" +!insertmacro MUI_LANGUAGE "Estonian" +!insertmacro MUI_LANGUAGE "Turkish" +!insertmacro MUI_LANGUAGE "Lithuanian" +!insertmacro MUI_LANGUAGE "Slovenian" +!insertmacro MUI_LANGUAGE "Serbian" +!insertmacro MUI_LANGUAGE "SerbianLatin" +!insertmacro MUI_LANGUAGE "Arabic" +!insertmacro MUI_LANGUAGE "Farsi" +!insertmacro MUI_LANGUAGE "Hebrew" +!insertmacro MUI_LANGUAGE "Indonesian" +!insertmacro MUI_LANGUAGE "Mongolian" +!insertmacro MUI_LANGUAGE "Luxembourgish" +!insertmacro MUI_LANGUAGE "Albanian" +!insertmacro MUI_LANGUAGE "Breton" +!insertmacro MUI_LANGUAGE "Belarusian" +!insertmacro MUI_LANGUAGE "Icelandic" +!insertmacro MUI_LANGUAGE "Malay" +!insertmacro MUI_LANGUAGE "Bosnian" +!insertmacro MUI_LANGUAGE "Kurdish" +!insertmacro MUI_LANGUAGE "Irish" +!insertmacro MUI_LANGUAGE "Uzbek" +!insertmacro MUI_LANGUAGE "Galician" +!insertmacro MUI_LANGUAGE "Afrikaans" +!insertmacro MUI_LANGUAGE "Catalan" +!insertmacro MUI_LANGUAGE "Esperanto" +!insertmacro MUI_LANGUAGE "Asturian" +!insertmacro MUI_LANGUAGE "Basque" +!insertmacro MUI_LANGUAGE "Pashto" +!insertmacro MUI_LANGUAGE "ScotsGaelic" +!insertmacro MUI_LANGUAGE "Georgian" +!insertmacro MUI_LANGUAGE "Vietnamese" +!insertmacro MUI_LANGUAGE "Welsh" +!insertmacro MUI_LANGUAGE "Armenian" +!insertmacro MUI_LANGUAGE "Corsican" +!insertmacro MUI_LANGUAGE "Tatar" +!insertmacro MUI_LANGUAGE "Hindi" ;-------------------------------- From 96deb5b09d6729f4b5f3a5c1880b526ee9882326 Mon Sep 17 00:00:00 2001 From: flow Date: Tue, 17 May 2022 06:36:30 -0300 Subject: [PATCH 24/67] chore: remove copyright from files i didnt mess with This is what happens when you auto-pilot stuff xdd --- launcher/net/PasteUpload.cpp | 1 - launcher/net/PasteUpload.h | 1 - launcher/net/Validator.h | 1 - 3 files changed, 3 deletions(-) diff --git a/launcher/net/PasteUpload.cpp b/launcher/net/PasteUpload.cpp index e88c8987..3d106c92 100644 --- a/launcher/net/PasteUpload.cpp +++ b/launcher/net/PasteUpload.cpp @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* * PolyMC - Minecraft Launcher - * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/net/PasteUpload.h b/launcher/net/PasteUpload.h index 53979352..ea3a06d3 100644 --- a/launcher/net/PasteUpload.h +++ b/launcher/net/PasteUpload.h @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* * PolyMC - Minecraft Launcher - * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/net/Validator.h b/launcher/net/Validator.h index e1d71d1c..6b3d4635 100644 --- a/launcher/net/Validator.h +++ b/launcher/net/Validator.h @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* * PolyMC - Minecraft Launcher - * Copyright (c) 2022 flowln * * 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 From 17bbfe8d8951ddc7acca0222c6d2e38fb29eef25 Mon Sep 17 00:00:00 2001 From: flow Date: Tue, 17 May 2022 06:47:00 -0300 Subject: [PATCH 25/67] fix: virtual signal in Task.h --- launcher/tasks/Task.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/tasks/Task.h b/launcher/tasks/Task.h index f0e6e402..f7765c3d 100644 --- a/launcher/tasks/Task.h +++ b/launcher/tasks/Task.h @@ -86,7 +86,7 @@ class Task : public QObject { signals: void started(); - virtual void progress(qint64 current, qint64 total); + void progress(qint64 current, qint64 total); void finished(); void succeeded(); void aborted(); From 77caaca50dab7ba8e455d641ac6b448052bc6799 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Thu, 19 May 2022 08:09:18 +0200 Subject: [PATCH 26/67] fix: only consider enabled mod loaders --- launcher/minecraft/PackProfile.cpp | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp index d53f41e1..87d11c4c 100644 --- a/launcher/minecraft/PackProfile.cpp +++ b/launcher/minecraft/PackProfile.cpp @@ -36,6 +36,13 @@ #include "ComponentUpdateTask.h" #include "Application.h" +#include "modplatform/ModAPI.h" + +static const QMap modloaderMapping{ + {"net.minecraftforge", ModAPI::Forge}, + {"net.fabricmc.fabric-loader", ModAPI::Fabric}, + {"org.quiltmc.quilt-loader", ModAPI::Quilt} +}; PackProfile::PackProfile(MinecraftInstance * instance) : QAbstractListModel() @@ -973,17 +980,15 @@ void PackProfile::disableInteraction(bool disable) ModAPI::ModLoaderType PackProfile::getModLoader() { - if (!getComponentVersion("net.minecraftforge").isEmpty()) + QMapIterator i(modloaderMapping); + + while (i.hasNext()) { - return ModAPI::Forge; - } - else if (!getComponentVersion("net.fabricmc.fabric-loader").isEmpty()) - { - return ModAPI::Fabric; - } - else if (!getComponentVersion("org.quiltmc.quilt-loader").isEmpty()) - { - return ModAPI::Quilt; + i.next(); + Component* c = getComponent(i.key()); + if (c != nullptr && c->isEnabled()) { + return i.value(); + } } return ModAPI::Unspecified; } From 943090db98dbbe969afed8a4fb59f4bbb43449cc Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Thu, 19 May 2022 08:40:28 +0200 Subject: [PATCH 27/67] refactor: allow tracking multiple mod loaders --- launcher/minecraft/PackProfile.cpp | 8 +++-- launcher/minecraft/PackProfile.h | 2 +- launcher/modplatform/ModAPI.h | 15 +++++--- launcher/modplatform/flame/FlameAPI.h | 17 +++++---- launcher/modplatform/modrinth/ModrinthAPI.h | 35 ++++++++----------- launcher/ui/pages/instance/ModFolderPage.cpp | 2 +- launcher/ui/pages/modplatform/ModModel.cpp | 4 +-- launcher/ui/pages/modplatform/ModPage.cpp | 2 +- launcher/ui/pages/modplatform/ModPage.h | 2 +- .../pages/modplatform/flame/FlameModPage.cpp | 4 +-- .../ui/pages/modplatform/flame/FlameModPage.h | 2 +- .../modplatform/modrinth/ModrinthModPage.cpp | 4 +-- .../modplatform/modrinth/ModrinthModPage.h | 2 +- 13 files changed, 54 insertions(+), 45 deletions(-) diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp index 87d11c4c..125048f0 100644 --- a/launcher/minecraft/PackProfile.cpp +++ b/launcher/minecraft/PackProfile.cpp @@ -978,8 +978,10 @@ void PackProfile::disableInteraction(bool disable) } } -ModAPI::ModLoaderType PackProfile::getModLoader() +ModAPI::ModLoaderTypes PackProfile::getModLoaders() { + ModAPI::ModLoaderTypes result = ModAPI::Unspecified; + QMapIterator i(modloaderMapping); while (i.hasNext()) @@ -987,8 +989,8 @@ ModAPI::ModLoaderType PackProfile::getModLoader() i.next(); Component* c = getComponent(i.key()); if (c != nullptr && c->isEnabled()) { - return i.value(); + result |= i.value(); } } - return ModAPI::Unspecified; + return result; } diff --git a/launcher/minecraft/PackProfile.h b/launcher/minecraft/PackProfile.h index ab4cd5c8..918e7f7a 100644 --- a/launcher/minecraft/PackProfile.h +++ b/launcher/minecraft/PackProfile.h @@ -118,7 +118,7 @@ public: // todo(merged): is this the best approach void appendComponent(ComponentPtr component); - ModAPI::ModLoaderType getModLoader(); + ModAPI::ModLoaderTypes getModLoaders(); private: void scheduleSave(); diff --git a/launcher/modplatform/ModAPI.h b/launcher/modplatform/ModAPI.h index 8e6cd45c..4230df0b 100644 --- a/launcher/modplatform/ModAPI.h +++ b/launcher/modplatform/ModAPI.h @@ -16,14 +16,21 @@ class ModAPI { public: virtual ~ModAPI() = default; - // https://docs.curseforge.com/?http#tocS_ModLoaderType - enum ModLoaderType { Unspecified = 0, Forge = 1, Cauldron = 2, LiteLoader = 3, Fabric = 4, Quilt = 5 }; + enum ModLoaderType { + Unspecified = 0, + Forge = 1 << 0, + Cauldron = 1 << 1, + LiteLoader = 1 << 2, + Fabric = 1 << 3, + Quilt = 1 << 4 + }; + Q_DECLARE_FLAGS(ModLoaderTypes, ModLoaderType) struct SearchArgs { int offset; QString search; QString sorting; - ModLoaderType mod_loader; + ModLoaderTypes loaders; std::list versions; }; @@ -33,7 +40,7 @@ class ModAPI { struct VersionSearchArgs { QString addonId; std::list mcVersions; - ModLoaderType loader; + ModLoaderTypes loaders; }; virtual void getVersions(CallerType* caller, VersionSearchArgs&& args) const = 0; diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h index 61628e60..8bb33d47 100644 --- a/launcher/modplatform/flame/FlameAPI.h +++ b/launcher/modplatform/flame/FlameAPI.h @@ -37,14 +37,14 @@ class FlameAPI : public NetworkModAPI { .arg(args.offset) .arg(args.search) .arg(getSortFieldInt(args.sorting)) - .arg(getMappedModLoader(args.mod_loader)) + .arg(getMappedModLoader(args.loaders)) .arg(gameVersionStr); }; inline auto getVersionsURL(VersionSearchArgs& args) const -> QString override { QString gameVersionQuery = args.mcVersions.size() == 1 ? QString("gameVersion=%1&").arg(args.mcVersions.front().toString()) : ""; - QString modLoaderQuery = QString("modLoaderType=%1&").arg(getMappedModLoader(args.loader)); + QString modLoaderQuery = QString("modLoaderType=%1&").arg(getMappedModLoader(args.loaders)); return QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&%2%3") .arg(args.addonId) @@ -53,11 +53,16 @@ class FlameAPI : public NetworkModAPI { }; public: - static auto getMappedModLoader(const ModLoaderType type) -> const ModLoaderType + static auto getMappedModLoader(const ModLoaderTypes loaders) -> const int { + // https://docs.curseforge.com/?http#tocS_ModLoaderType + if (loaders & Forge) + return 1; + if (loaders & Fabric) + return 4; // TODO: remove this once Quilt drops official Fabric support - if (type == Quilt) // NOTE: Most if not all Fabric mods should work *currently* - return Fabric; - return type; + if (loaders & Quilt) // NOTE: Most if not all Fabric mods should work *currently* + return 4; // Quilt would probably be 5 + return 0; } }; diff --git a/launcher/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h index 6d642b5e..39f6c49a 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.h +++ b/launcher/modplatform/modrinth/ModrinthAPI.h @@ -28,30 +28,25 @@ class ModrinthAPI : public NetworkModAPI { public: inline auto getAuthorURL(const QString& name) const -> QString { return "https://modrinth.com/user/" + name; }; - static auto getModLoaderStrings(ModLoaderType type) -> const QStringList + static auto getModLoaderStrings(const ModLoaderTypes types) -> const QStringList { QStringList l; - switch (type) + for (auto loader : {Forge, Fabric, Quilt}) { - case Unspecified: - for (auto loader : {Forge, Fabric, Quilt}) - { - l << ModAPI::getModLoaderString(loader); - } - break; - - case Quilt: - l << ModAPI::getModLoaderString(Fabric); - default: - l << ModAPI::getModLoaderString(type); + if (types & loader || types == Unspecified) + { + l << ModAPI::getModLoaderString(loader); + } } + if (types & Quilt && ~types & Fabric) // Add Fabric if Quilt is in use, if Fabric isn't already there + l << ModAPI::getModLoaderString(Fabric); return l; } - static auto getModLoaderFilters(ModLoaderType type) -> const QString + static auto getModLoaderFilters(ModLoaderTypes types) -> const QString { QStringList l; - for (auto loader : getModLoaderStrings(type)) + for (auto loader : getModLoaderStrings(types)) { l << QString("\"categories:%1\"").arg(loader); } @@ -61,7 +56,7 @@ class ModrinthAPI : public NetworkModAPI { private: inline auto getModSearchURL(SearchArgs& args) const -> QString override { - if (!validateModLoader(args.mod_loader)) { + if (!validateModLoaders(args.loaders)) { qWarning() << "Modrinth only have Forge and Fabric-compatible mods!"; return ""; } @@ -76,7 +71,7 @@ class ModrinthAPI : public NetworkModAPI { .arg(args.offset) .arg(args.search) .arg(args.sorting) - .arg(getModLoaderFilters(args.mod_loader)) + .arg(getModLoaderFilters(args.loaders)) .arg(getGameVersionsArray(args.versions)); }; @@ -88,7 +83,7 @@ class ModrinthAPI : public NetworkModAPI { "loaders=[\"%3\"]") .arg(args.addonId) .arg(getGameVersionsString(args.mcVersions)) - .arg(getModLoaderStrings(args.loader).join("\",\"")); + .arg(getModLoaderStrings(args.loaders).join("\",\"")); }; auto getGameVersionsArray(std::list mcVersions) const -> QString @@ -101,9 +96,9 @@ class ModrinthAPI : public NetworkModAPI { return s.isEmpty() ? QString() : QString("[%1],").arg(s); } - inline auto validateModLoader(ModLoaderType modLoader) const -> bool + inline auto validateModLoaders(ModLoaderTypes loaders) const -> bool { - return modLoader == Unspecified || modLoader == Forge || modLoader == Fabric || modLoader == Quilt; + return loaders == Unspecified || loaders & (Forge | Fabric | Quilt); } }; diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index 8113fe85..5574f9d2 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -391,7 +391,7 @@ void ModFolderPage::on_actionInstall_mods_triggered() return; //this is a null instance or a legacy instance } auto profile = ((MinecraftInstance *)m_inst)->getPackProfile(); - if (profile->getModLoader() == ModAPI::Unspecified) { + if (profile->getModLoaders() == ModAPI::Unspecified) { QMessageBox::critical(this,tr("Error"),tr("Please install a mod loader first!")); return; } diff --git a/launcher/ui/pages/modplatform/ModModel.cpp b/launcher/ui/pages/modplatform/ModModel.cpp index 540ee2fd..9dd8f737 100644 --- a/launcher/ui/pages/modplatform/ModModel.cpp +++ b/launcher/ui/pages/modplatform/ModModel.cpp @@ -68,7 +68,7 @@ void ListModel::requestModVersions(ModPlatform::IndexedPack const& current) { auto profile = (dynamic_cast((dynamic_cast(parent()))->m_instance))->getPackProfile(); - m_parent->apiProvider()->getVersions(this, { current.addonId.toString(), getMineVersions(), profile->getModLoader() }); + m_parent->apiProvider()->getVersions(this, { current.addonId.toString(), getMineVersions(), profile->getModLoaders() }); } void ListModel::performPaginatedSearch() @@ -76,7 +76,7 @@ void ListModel::performPaginatedSearch() auto profile = (dynamic_cast((dynamic_cast(parent()))->m_instance))->getPackProfile(); m_parent->apiProvider()->searchMods( - this, { nextSearchOffset, currentSearchTerm, getSorts()[currentSort], profile->getModLoader(), getMineVersions() }); + this, { nextSearchOffset, currentSearchTerm, getSorts()[currentSort], profile->getModLoaders(), getMineVersions() }); } void ListModel::refresh() diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index 6dd3a453..ad36cf2f 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -175,7 +175,7 @@ void ModPage::updateModVersions(int prev_count) bool valid = false; for(auto& mcVer : m_filter->versions){ //NOTE: Flame doesn't care about loader, so passing it changes nothing. - if (validateVersion(version, mcVer.toString(), packProfile->getModLoader())) { + if (validateVersion(version, mcVer.toString(), packProfile->getModLoaders())) { valid = true; break; } diff --git a/launcher/ui/pages/modplatform/ModPage.h b/launcher/ui/pages/modplatform/ModPage.h index eb89b0e2..0e658a8d 100644 --- a/launcher/ui/pages/modplatform/ModPage.h +++ b/launcher/ui/pages/modplatform/ModPage.h @@ -37,7 +37,7 @@ class ModPage : public QWidget, public BasePage { void retranslate() override; auto shouldDisplay() const -> bool override = 0; - virtual auto validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, ModAPI::ModLoaderType loader = ModAPI::Unspecified) const -> bool = 0; + virtual auto validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, ModAPI::ModLoaderTypes loaders = ModAPI::Unspecified) const -> bool = 0; auto apiProvider() const -> const ModAPI* { return api.get(); }; auto getFilter() const -> const std::shared_ptr { return m_filter; } diff --git a/launcher/ui/pages/modplatform/flame/FlameModPage.cpp b/launcher/ui/pages/modplatform/flame/FlameModPage.cpp index 70759994..1c160fd4 100644 --- a/launcher/ui/pages/modplatform/flame/FlameModPage.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameModPage.cpp @@ -61,9 +61,9 @@ FlameModPage::FlameModPage(ModDownloadDialog* dialog, BaseInstance* instance) connect(ui->modSelectionButton, &QPushButton::clicked, this, &FlameModPage::onModSelected); } -auto FlameModPage::validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, ModAPI::ModLoaderType loader) const -> bool +auto FlameModPage::validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, ModAPI::ModLoaderTypes loaders) const -> bool { - Q_UNUSED(loader); + Q_UNUSED(loaders); return ver.mcVersion.contains(mineVer); } diff --git a/launcher/ui/pages/modplatform/flame/FlameModPage.h b/launcher/ui/pages/modplatform/flame/FlameModPage.h index 27cbdb8c..86e1a17b 100644 --- a/launcher/ui/pages/modplatform/flame/FlameModPage.h +++ b/launcher/ui/pages/modplatform/flame/FlameModPage.h @@ -55,7 +55,7 @@ class FlameModPage : public ModPage { inline auto debugName() const -> QString override { return "Flame"; } inline auto metaEntryBase() const -> QString override { return "FlameMods"; }; - auto validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, ModAPI::ModLoaderType loader = ModAPI::Unspecified) const -> bool override; + auto validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, ModAPI::ModLoaderTypes loaders = ModAPI::Unspecified) const -> bool override; auto shouldDisplay() const -> bool override; }; diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModPage.cpp index d3a1f859..0b81ea93 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModPage.cpp @@ -61,9 +61,9 @@ ModrinthModPage::ModrinthModPage(ModDownloadDialog* dialog, BaseInstance* instan connect(ui->modSelectionButton, &QPushButton::clicked, this, &ModrinthModPage::onModSelected); } -auto ModrinthModPage::validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, ModAPI::ModLoaderType loader) const -> bool +auto ModrinthModPage::validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, ModAPI::ModLoaderTypes loaders) const -> bool { - auto loaderStrings = ModrinthAPI::getModLoaderStrings(loader); + auto loaderStrings = ModrinthAPI::getModLoaderStrings(loaders); auto loaderCompatible = false; for (auto remoteLoader : ver.loaders) diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModPage.h b/launcher/ui/pages/modplatform/modrinth/ModrinthModPage.h index b1e72bfe..c39acaa0 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModPage.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModPage.h @@ -55,7 +55,7 @@ class ModrinthModPage : public ModPage { inline auto debugName() const -> QString override { return "Modrinth"; } inline auto metaEntryBase() const -> QString override { return "ModrinthPacks"; }; - auto validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, ModAPI::ModLoaderType loader = ModAPI::Unspecified) const -> bool override; + auto validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, ModAPI::ModLoaderTypes loaders = ModAPI::Unspecified) const -> bool override; auto shouldDisplay() const -> bool override; }; From 36045a8b0aa5c99e8520a39e6cc372ab9b549668 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Thu, 19 May 2022 12:35:44 +0200 Subject: [PATCH 28/67] chore: improve readability Co-authored-by: flow --- launcher/modplatform/modrinth/ModrinthAPI.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/launcher/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h index 39f6c49a..79bc5175 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.h +++ b/launcher/modplatform/modrinth/ModrinthAPI.h @@ -33,12 +33,12 @@ class ModrinthAPI : public NetworkModAPI { QStringList l; for (auto loader : {Forge, Fabric, Quilt}) { - if (types & loader || types == Unspecified) + if ((types & loader) || types == Unspecified) { l << ModAPI::getModLoaderString(loader); } } - if (types & Quilt && ~types & Fabric) // Add Fabric if Quilt is in use, if Fabric isn't already there + if ((types & Quilt) && (~types & Fabric)) // Add Fabric if Quilt is in use, if Fabric isn't already there l << ModAPI::getModLoaderString(Fabric); return l; } @@ -98,7 +98,7 @@ class ModrinthAPI : public NetworkModAPI { inline auto validateModLoaders(ModLoaderTypes loaders) const -> bool { - return loaders == Unspecified || loaders & (Forge | Fabric | Quilt); + return (loaders == Unspecified) || (loaders & (Forge | Fabric | Quilt)); } }; From 97a83c9b7a72d37218acfbf5c325245eab0b5b23 Mon Sep 17 00:00:00 2001 From: Jamie Mansfield Date: Sun, 1 May 2022 18:12:21 +0100 Subject: [PATCH 29/67] ATLauncher: Avoid downloading Forge twice for older packs This resolves a quirk where Forge would still be downloaded for use as a jarmod, even when we detected Forge as a component. --- launcher/modplatform/atlauncher/ATLPackInstallTask.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp index 9dcb3504..991d737c 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp @@ -574,8 +574,6 @@ void PackInstallTask::downloadMods() jobPtr->addNetAction(dl); auto path = FS::PathCombine(m_stagingPath, "minecraft", relpath, mod.file); - qDebug() << "Will download" << url << "to" << path; - modsToCopy[entry->getFullPath()] = path; if(mod.type == ModType::Forge) { auto vlist = APPLICATION->metadataIndex()->get("net.minecraftforge"); @@ -597,6 +595,10 @@ void PackInstallTask::downloadMods() qDebug() << "Jarmod: " + path; jarmods.push_back(path); } + + // Download after Forge handling, to avoid downloading Forge twice. + qDebug() << "Will download" << url << "to" << path; + modsToCopy[entry->getFullPath()] = path; } } From c329730de848f9ecf864aa4edbbc650faad7f21a Mon Sep 17 00:00:00 2001 From: Jamie Mansfield Date: Sun, 1 May 2022 19:32:34 +0100 Subject: [PATCH 30/67] ATLauncher: Install LiteLoader as a component where possible --- .../atlauncher/ATLPackInstallTask.cpp | 91 ++++++++++++++++--- 1 file changed, 80 insertions(+), 11 deletions(-) diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp index 991d737c..e9e3b872 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp @@ -1,18 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0-only /* - * Copyright 2020-2021 Jamie Mansfield - * Copyright 2021 Petr Mrazek + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 Jamie Mansfield * - * 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 2020-2021 Jamie Mansfield + * Copyright 2021 Petr Mrazek + * + * 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 "ATLPackInstallTask.h" @@ -305,7 +324,55 @@ bool PackInstallTask::createLibrariesComponent(QString instanceRoot, std::shared auto f = std::make_shared(); f->name = m_pack + " " + m_version_name + " (libraries)"; + const static QMap liteLoaderMap = { + { "61179803bcd5fb7790789b790908663d", "1.12-SNAPSHOT" }, + { "1420785ecbfed5aff4a586c5c9dd97eb", "1.12.2-SNAPSHOT" }, + { "073f68e2fcb518b91fd0d99462441714", "1.6.2_03" }, + { "10a15b52fc59b1bfb9c05b56de1097d6", "1.6.2_02" }, + { "b52f90f08303edd3d4c374e268a5acf1", "1.6.2_04" }, + { "ea747e24e03e24b7cad5bc8a246e0319", "1.6.2_01" }, + { "55785ccc82c07ff0ba038fe24be63ea2", "1.7.10_01" }, + { "63ada46e033d0cb6782bada09ad5ca4e", "1.7.10_04" }, + { "7983e4b28217c9ae8569074388409c86", "1.7.10_03" }, + { "c09882458d74fe0697c7681b8993097e", "1.7.10_02" }, + { "db7235aefd407ac1fde09a7baba50839", "1.7.10_00" }, + { "6e9028816027f53957bd8fcdfabae064", "1.8" }, + { "5e732dc446f9fe2abe5f9decaec40cde", "1.10-SNAPSHOT" }, + { "3a98b5ed95810bf164e71c1a53be568d", "1.11.2-SNAPSHOT" }, + { "ba8e6285966d7d988a96496f48cbddaa", "1.8.9-SNAPSHOT" }, + { "8524af3ac3325a82444cc75ae6e9112f", "1.11-SNAPSHOT" }, + { "53639d52340479ccf206a04f5e16606f", "1.5.2_01" }, + { "1fcdcf66ce0a0806b7ad8686afdce3f7", "1.6.4_00" }, + { "531c116f71ae2b11033f9a11a0f8e668", "1.6.4_01" }, + { "4009eeb99c9068f608d3483a6439af88", "1.7.2_03" }, + { "66f343354b8417abce1a10d557d2c6e9", "1.7.2_04" }, + { "ab554c21f28fbc4ae9b098bcb5f4cceb", "1.7.2_05" }, + { "e1d76a05a3723920e2f80a5e66c45f16", "1.7.2_02" }, + { "00318cb0c787934d523f63cdfe8ddde4", "1.9-SNAPSHOT" }, + { "986fd1ee9525cb0dcab7609401cef754", "1.9.4-SNAPSHOT" }, + { "571ad5e6edd5ff40259570c9be588bb5", "1.9.4" }, + { "1cdd72f7232e45551f16cc8ffd27ccf3", "1.10.2-SNAPSHOT" }, + { "8a7c21f32d77ee08b393dd3921ced8eb", "1.10.2" }, + { "b9bef8abc8dc309069aeba6fbbe58980", "1.12.1-SNAPSHOT" } + }; + for(const auto & lib : m_version.libraries) { + // If the library is LiteLoader, we need to ignore it and handle it separately. + if (liteLoaderMap.contains(lib.md5)) { + auto vlist = APPLICATION->metadataIndex()->get("com.mumfrey.liteloader"); + if (vlist) { + if (!vlist->isLoaded()) + vlist->load(Net::Mode::Online); + + auto ver = vlist->getVersion(liteLoaderMap.value(lib.md5)); + if (ver) { + ver->load(Net::Mode::Online); + componentsToInstall.insert("com.mumfrey.liteloader", ver); + continue; + } + } + } + auto libName = detectLibrary(lib); GradleSpecifier libSpecifier(libName); @@ -579,6 +646,8 @@ void PackInstallTask::downloadMods() auto vlist = APPLICATION->metadataIndex()->get("net.minecraftforge"); if(vlist) { + if (!vlist->isLoaded()) + vlist->load(Net::Mode::Online); auto ver = vlist->getVersion(mod.version); if(ver) { ver->load(Net::Mode::Online); From f5f59203a203318371fbc5257234b8c2c5eeb300 Mon Sep 17 00:00:00 2001 From: Jamie Mansfield Date: Sun, 1 May 2022 22:42:29 +0100 Subject: [PATCH 31/67] ATLauncher: Reduce boilerplate code for fetching versions --- .../atlauncher/ATLPackInstallTask.cpp | 63 +++++++++---------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp index e9e3b872..4b8b8eb0 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp @@ -58,6 +58,8 @@ namespace ATLauncher { +static Meta::VersionPtr getComponentVersion(const QString& uid, const QString& version); + PackInstallTask::PackInstallTask(UserInteractionSupport *support, QString pack, QString version) { m_support = support; @@ -115,19 +117,11 @@ void PackInstallTask::onDownloadSucceeded() } m_version = version; - auto vlist = APPLICATION->metadataIndex()->get("net.minecraft"); - if(!vlist) - { - emitFailed(tr("Failed to get local metadata index for %1").arg("net.minecraft")); - return; - } - - auto ver = vlist->getVersion(m_version.minecraft); + auto ver = getComponentVersion("net.minecraft", m_version.minecraft); if (!ver) { - emitFailed(tr("Failed to get local metadata index for '%1' v%2").arg("net.minecraft").arg(m_version.minecraft)); + emitFailed(tr("Failed to get local metadata index for '%1' v%2").arg("net.minecraft", m_version.minecraft)); return; } - ver->load(Net::Mode::Online); minecraftVersion = ver; if(m_version.noConfigs) { @@ -359,17 +353,10 @@ bool PackInstallTask::createLibrariesComponent(QString instanceRoot, std::shared for(const auto & lib : m_version.libraries) { // If the library is LiteLoader, we need to ignore it and handle it separately. if (liteLoaderMap.contains(lib.md5)) { - auto vlist = APPLICATION->metadataIndex()->get("com.mumfrey.liteloader"); - if (vlist) { - if (!vlist->isLoaded()) - vlist->load(Net::Mode::Online); - - auto ver = vlist->getVersion(liteLoaderMap.value(lib.md5)); - if (ver) { - ver->load(Net::Mode::Online); - componentsToInstall.insert("com.mumfrey.liteloader", ver); - continue; - } + auto ver = getComponentVersion("com.mumfrey.liteloader", liteLoaderMap.value(lib.md5)); + if (ver) { + componentsToInstall.insert("com.mumfrey.liteloader", ver); + continue; } } @@ -643,17 +630,10 @@ void PackInstallTask::downloadMods() auto path = FS::PathCombine(m_stagingPath, "minecraft", relpath, mod.file); if(mod.type == ModType::Forge) { - auto vlist = APPLICATION->metadataIndex()->get("net.minecraftforge"); - if(vlist) - { - if (!vlist->isLoaded()) - vlist->load(Net::Mode::Online); - auto ver = vlist->getVersion(mod.version); - if(ver) { - ver->load(Net::Mode::Online); - componentsToInstall.insert("net.minecraftforge", ver); - continue; - } + auto ver = getComponentVersion("net.minecraftforge", mod.version); + if (ver) { + componentsToInstall.insert("net.minecraftforge", ver); + continue; } qDebug() << "Jarmod: " + path; @@ -850,4 +830,23 @@ void PackInstallTask::install() emitSucceeded(); } +static Meta::VersionPtr getComponentVersion(const QString& uid, const QString& version) +{ + auto vlist = APPLICATION->metadataIndex()->get(uid); + if (!vlist) + return {}; + + if (!vlist->isLoaded()) + vlist->load(Net::Mode::Online); + + auto ver = vlist->getVersion(version); + if (!ver) + return {}; + + if (!ver->isLoaded()) + ver->load(Net::Mode::Online); + + return ver; +} + } From 188c5aaa356323392be1100d74f62d70ab298695 Mon Sep 17 00:00:00 2001 From: Jamie Mansfield Date: Tue, 17 May 2022 18:43:35 +0100 Subject: [PATCH 32/67] Launch: Match Vanilla launcher version string behaviour This removes a means of profiling users. --- launcher/minecraft/MinecraftInstance.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index e20dc24c..61326fac 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -2,6 +2,7 @@ /* * PolyMC - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022 Jamie Mansfield * * 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 @@ -487,9 +488,8 @@ QStringList MinecraftInstance::processMinecraftArgs( } } - // blatant self-promotion. - token_mapping["profile_name"] = token_mapping["version_name"] = BuildConfig.LAUNCHER_NAME; - + token_mapping["profile_name"] = name(); + token_mapping["version_name"] = profile->getMinecraftVersion(); token_mapping["version_type"] = profile->getMinecraftVersionType(); QString absRootDir = QDir(gameRoot()).absolutePath(); From 96f16069a93afa320de174e740bc6b915e9a1103 Mon Sep 17 00:00:00 2001 From: Jamie Mansfield Date: Tue, 17 May 2022 21:03:15 +0100 Subject: [PATCH 33/67] Launch: Apply the Minecraft version correctly It was previously using a deprecated field. --- launcher/minecraft/VersionFile.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/launcher/minecraft/VersionFile.cpp b/launcher/minecraft/VersionFile.cpp index 9db30ba2..f242fbe7 100644 --- a/launcher/minecraft/VersionFile.cpp +++ b/launcher/minecraft/VersionFile.cpp @@ -2,6 +2,7 @@ /* * PolyMC - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022 Jamie Mansfield * * 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 @@ -55,7 +56,7 @@ void VersionFile::applyTo(LaunchProfile *profile) // Only real Minecraft can set those. Don't let anything override them. if (isMinecraftVersion(uid)) { - profile->applyMinecraftVersion(minecraftVersion); + profile->applyMinecraftVersion(version); profile->applyMinecraftVersionType(type); // HACK: ignore assets from other version files than Minecraft // workaround for stupid assets issue caused by amazon: From cbc8c1aed63e9cd106b468ae720aa650beec646a Mon Sep 17 00:00:00 2001 From: Kenneth Chew <79120643+kthchew@users.noreply.github.com> Date: Fri, 20 May 2022 15:56:13 -0400 Subject: [PATCH 34/67] Use consistent naming scheme Co-authored-by: Sefa Eyeoglu --- .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 7ab30d45..d12f176c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -269,7 +269,7 @@ jobs: if: runner.os == 'Windows' uses: actions/upload-artifact@v3 with: - name: PolyMC-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}-Setup + name: PolyMC-${{ matrix.name }}-Setup-${{ env.VERSION }}-${{ inputs.build_type }} path: PolyMC-Setup.exe - name: Upload binary tarball (Linux) From 2bc6da038dea701699ba9fc46eb68b3a74d5f488 Mon Sep 17 00:00:00 2001 From: Kenneth Chew Date: Fri, 20 May 2022 17:09:26 -0400 Subject: [PATCH 35/67] Add installer to release workflow --- .github/workflows/trigger_release.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/trigger_release.yml b/.github/workflows/trigger_release.yml index ff0d2b3f..91cd0474 100644 --- a/.github/workflows/trigger_release.yml +++ b/.github/workflows/trigger_release.yml @@ -43,10 +43,12 @@ jobs: for d in PolyMC-Windows-*; do cd "${d}" || continue ARCH="$(echo -n ${d} | cut -d '-' -f 3)" + INST="$(echo -n ${d} | grep -o Setup || true)" PORT="$(echo -n ${d} | grep -o Portable || true)" NAME="PolyMC-Windows-${ARCH}" test -z "${PORT}" || NAME="${NAME}-Portable" - zip -r -9 "../${NAME}-${{ env.VERSION }}.zip" * + test -z "${INST}" || mv PolyMC-*.exe ../${NAME}-Setup-${{ env.VERSION }}.exe + test -n "${INST}" || zip -r -9 "../${NAME}-${{ env.VERSION }}.zip" * cd .. done @@ -66,7 +68,9 @@ jobs: PolyMC-Linux-${{ env.VERSION }}-x86_64.AppImage PolyMC-Windows-i686-${{ env.VERSION }}.zip PolyMC-Windows-i686-Portable-${{ env.VERSION }}.zip + PolyMC-Windows-i686-Setup-${{ env.VERSION }}.exe PolyMC-Windows-x86_64-${{ env.VERSION }}.zip PolyMC-Windows-x86_64-Portable-${{ env.VERSION }}.zip + PolyMC-Windows-x86_64-Setup-${{ env.VERSION }}.exe PolyMC-macOS-${{ env.VERSION }}.tar.gz PolyMC-${{ env.VERSION }}.tar.gz From 12cadf3af0a4e3a01330283fae2d6267d3c3f525 Mon Sep 17 00:00:00 2001 From: Kenneth Chew Date: Fri, 20 May 2022 17:09:42 -0400 Subject: [PATCH 36/67] Add `/NoUninstaller` parameter for Windows installer --- program_info/win_install.nsi | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/program_info/win_install.nsi b/program_info/win_install.nsi index ce13b8b0..2d3f0f57 100644 --- a/program_info/win_install.nsi +++ b/program_info/win_install.nsi @@ -1,4 +1,5 @@ !include "FileFunc.nsh" +!include "LogicLib.nsh" !include "MUI2.nsh" Unicode true @@ -119,20 +120,24 @@ Section "PolyMC" WriteRegStr HKCU Software\PolyMC "InstallDir" "$INSTDIR" ; Write the uninstall keys for Windows - !define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\PolyMC" - WriteRegStr HKCU "${UNINST_KEY}" "DisplayName" "PolyMC" - WriteRegStr HKCU "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\polymc.exe" - WriteRegStr HKCU "${UNINST_KEY}" "UninstallString" '"$INSTDIR\uninstall.exe"' - WriteRegStr HKCU "${UNINST_KEY}" "QuietUninstallString" '"$INSTDIR\uninstall.exe" /S' - WriteRegStr HKCU "${UNINST_KEY}" "InstallLocation" "$INSTDIR" - WriteRegStr HKCU "${UNINST_KEY}" "Publisher" "PolyMC Contributors" - WriteRegStr HKCU "${UNINST_KEY}" "ProductVersion" "${VERSION}" - ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2 - IntFmt $0 "0x%08X" $0 - WriteRegDWORD HKCU "${UNINST_KEY}" "EstimatedSize" "$0" - WriteRegDWORD HKCU "${UNINST_KEY}" "NoModify" 1 - WriteRegDWORD HKCU "${UNINST_KEY}" "NoRepair" 1 - WriteUninstaller "$INSTDIR\uninstall.exe" + ${GetParameters} $R0 + ${GetOptions} $R0 "/NoUninstaller" $R1 + ${If} ${Errors} + !define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\PolyMC" + WriteRegStr HKCU "${UNINST_KEY}" "DisplayName" "PolyMC" + WriteRegStr HKCU "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\polymc.exe" + WriteRegStr HKCU "${UNINST_KEY}" "UninstallString" '"$INSTDIR\uninstall.exe"' + WriteRegStr HKCU "${UNINST_KEY}" "QuietUninstallString" '"$INSTDIR\uninstall.exe" /S' + WriteRegStr HKCU "${UNINST_KEY}" "InstallLocation" "$INSTDIR" + WriteRegStr HKCU "${UNINST_KEY}" "Publisher" "PolyMC Contributors" + WriteRegStr HKCU "${UNINST_KEY}" "ProductVersion" "${VERSION}" + ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2 + IntFmt $0 "0x%08X" $0 + WriteRegDWORD HKCU "${UNINST_KEY}" "EstimatedSize" "$0" + WriteRegDWORD HKCU "${UNINST_KEY}" "NoModify" 1 + WriteRegDWORD HKCU "${UNINST_KEY}" "NoRepair" 1 + WriteUninstaller "$INSTDIR\uninstall.exe" + ${EndIf} SectionEnd From cdd83c279cafdacee6c863d7fb0ae94a6bf34e3e Mon Sep 17 00:00:00 2001 From: Kenneth Chew Date: Fri, 20 May 2022 17:12:08 -0400 Subject: [PATCH 37/67] Remove portable option in Windows installer --- .github/workflows/build.yml | 2 +- program_info/win_install.nsi | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d12f176c..53db7d76 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -195,7 +195,7 @@ jobs: if: runner.os == 'Windows' shell: msys2 {0} run: | - cd ${{ env.INSTALL_PORTABLE_DIR }} + cd ${{ env.INSTALL_DIR }} makensis -NOCD "-DVERSION=${{ env.VERSION }}" "-DMUI_ICON=${{ github.workspace }}/program_info/polymc.ico" "-XOutFile ${{ github.workspace }}/PolyMC-Setup.exe" "${{ github.workspace }}/program_info/win_install.nsi" - name: Package (Linux) diff --git a/program_info/win_install.nsi b/program_info/win_install.nsi index 2d3f0f57..a47d4ae3 100644 --- a/program_info/win_install.nsi +++ b/program_info/win_install.nsi @@ -147,13 +147,6 @@ Section "Start Menu Shortcuts" SectionEnd -Section /o "Portable" - - SetOutPath $INSTDIR - File "portable.txt" - -SectionEnd - ;-------------------------------- ; Uninstaller From 1ec7878c07a8ba7d04a9fe860761872547fd5a0d Mon Sep 17 00:00:00 2001 From: Kenneth Chew Date: Fri, 20 May 2022 17:22:30 -0400 Subject: [PATCH 38/67] Add `/NoShortcuts` parameter for Windows installer --- program_info/win_install.nsi | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/program_info/win_install.nsi b/program_info/win_install.nsi index a47d4ae3..7d48ccf2 100644 --- a/program_info/win_install.nsi +++ b/program_info/win_install.nsi @@ -141,7 +141,7 @@ Section "PolyMC" SectionEnd -Section "Start Menu Shortcuts" +Section "Start Menu Shortcuts" SHORTCUTS CreateShortcut "$SMPROGRAMS\PolyMC.lnk" "$INSTDIR\polymc.exe" "" "$INSTDIR\polymc.exe" 0 @@ -219,3 +219,15 @@ Section "Uninstall" RMDir "$INSTDIR" SectionEnd + +;-------------------------------- + +; Extra command line parameters + +Function .onInit +${GetParameters} $R0 +${GetOptions} $R0 "/NoShortcuts" $R1 +${IfNot} ${Errors} + !insertmacro UnselectSection ${SHORTCUTS} +${EndIf} +FunctionEnd From 3cab0e69f1c299e385330d97ab5159a0c8c904ec Mon Sep 17 00:00:00 2001 From: Kenneth Chew Date: Fri, 20 May 2022 17:23:11 -0400 Subject: [PATCH 39/67] Fix default install location --- program_info/win_install.nsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program_info/win_install.nsi b/program_info/win_install.nsi index 7d48ccf2..4ca4de1a 100644 --- a/program_info/win_install.nsi +++ b/program_info/win_install.nsi @@ -5,7 +5,7 @@ Unicode true Name "PolyMC" -InstallDir "$LOCALAPPDATA\PolyMC" +InstallDir "$LOCALAPPDATA\Programs\PolyMC" InstallDirRegKey HKCU "Software\PolyMC" "InstallDir" RequestExecutionLevel user From c04adf74521127bc50c67f3e2ddd1edfe2330358 Mon Sep 17 00:00:00 2001 From: timoreo Date: Sat, 21 May 2022 08:31:07 +0200 Subject: [PATCH 40/67] Do the url trick on initial modpack download too --- launcher/modplatform/flame/FlamePackIndex.cpp | 10 +++++++++- launcher/modplatform/flame/FlamePackIndex.h | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/launcher/modplatform/flame/FlamePackIndex.cpp b/launcher/modplatform/flame/FlamePackIndex.cpp index ac24c647..6d48a3bf 100644 --- a/launcher/modplatform/flame/FlamePackIndex.cpp +++ b/launcher/modplatform/flame/FlamePackIndex.cpp @@ -65,7 +65,15 @@ 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.downloadUrl = Json::requireString(version, "downloadUrl"); + file.fileName = Json::requireString(version, "fileName"); + file.downloadUrl = Json::ensureString(version, "downloadUrl"); + if(file.downloadUrl.isEmpty()){ + //FIXME : HACK, MAY NOT WORK FOR LONG + file.downloadUrl = QString("https://media.forgecdn.net/files/%1/%2/%3") + .arg(QString::number(QString::number(file.fileId).leftRef(4).toInt()) + ,QString::number(QString::number(file.fileId).rightRef(3).toInt()) + ,QUrl::toPercentEncoding(file.fileName)); + } unsortedVersions.append(file); } diff --git a/launcher/modplatform/flame/FlamePackIndex.h b/launcher/modplatform/flame/FlamePackIndex.h index 7ffa29c3..a8bb15be 100644 --- a/launcher/modplatform/flame/FlamePackIndex.h +++ b/launcher/modplatform/flame/FlamePackIndex.h @@ -18,6 +18,7 @@ struct IndexedVersion { QString version; QString mcVersion; QString downloadUrl; + QString fileName; }; struct IndexedPack From 7c251efc473ee90069d1e87a056bde64f1d6fbf7 Mon Sep 17 00:00:00 2001 From: Jamie Mansfield Date: Mon, 2 May 2022 20:27:20 +0100 Subject: [PATCH 41/67] ATLauncher: Display mod colours in optional mod dialog --- .../atlauncher/ATLPackInstallTask.cpp | 2 +- .../atlauncher/ATLPackInstallTask.h | 2 +- .../atlauncher/ATLPackManifest.cpp | 6 ++++++ .../modplatform/atlauncher/ATLPackManifest.h | 6 +++++- .../atlauncher/AtlOptionalModDialog.cpp | 21 +++++++++++++------ .../atlauncher/AtlOptionalModDialog.h | 6 ++++-- .../pages/modplatform/atlauncher/AtlPage.cpp | 5 +++-- .../ui/pages/modplatform/atlauncher/AtlPage.h | 2 +- 8 files changed, 36 insertions(+), 14 deletions(-) diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp index 4b8b8eb0..90dc1365 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp @@ -556,7 +556,7 @@ void PackInstallTask::downloadMods() QVector selectedMods; if (!optionalMods.isEmpty()) { setStatus(tr("Selecting optional mods...")); - selectedMods = m_support->chooseOptionalMods(optionalMods); + selectedMods = m_support->chooseOptionalMods(m_version, optionalMods); } setStatus(tr("Downloading mods...")); diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.h b/launcher/modplatform/atlauncher/ATLPackInstallTask.h index 783ec19b..6bc30689 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.h +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.h @@ -37,7 +37,7 @@ public: /** * Requests a user interaction to select which optional mods should be installed. */ - virtual QVector chooseOptionalMods(QVector mods) = 0; + virtual QVector chooseOptionalMods(PackVersion version, QVector mods) = 0; /** * Requests a user interaction to select a component version from a given version list diff --git a/launcher/modplatform/atlauncher/ATLPackManifest.cpp b/launcher/modplatform/atlauncher/ATLPackManifest.cpp index 40be6d53..a8f2711b 100644 --- a/launcher/modplatform/atlauncher/ATLPackManifest.cpp +++ b/launcher/modplatform/atlauncher/ATLPackManifest.cpp @@ -178,6 +178,7 @@ static void loadVersionMod(ATLauncher::VersionMod & p, QJsonObject & obj) { p.depends.append(Json::requireString(depends)); } } + p.colour = Json::ensureString(obj, QString("colour"), ""); p.client = Json::ensureBoolean(obj, QString("client"), false); @@ -232,4 +233,9 @@ void ATLauncher::loadVersion(PackVersion & v, QJsonObject & obj) auto configsObj = Json::requireObject(obj, "configs"); loadVersionConfigs(v.configs, configsObj); } + + auto colourObj = Json::ensureObject(obj, "colours"); + for (const auto &key : colourObj.keys()) { + v.colours[key] = Json::requireString(colourObj.value(key), "colour"); + } } diff --git a/launcher/modplatform/atlauncher/ATLPackManifest.h b/launcher/modplatform/atlauncher/ATLPackManifest.h index 673f2f8b..2911107e 100644 --- a/launcher/modplatform/atlauncher/ATLPackManifest.h +++ b/launcher/modplatform/atlauncher/ATLPackManifest.h @@ -16,9 +16,10 @@ #pragma once +#include +#include #include #include -#include namespace ATLauncher { @@ -109,6 +110,7 @@ struct VersionMod bool library; QString group; QVector depends; + QString colour; bool client; @@ -134,6 +136,8 @@ struct PackVersion QVector libraries; QVector mods; VersionConfigs configs; + + QMap colours; }; void loadVersion(PackVersion & v, QJsonObject & obj); diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp index 26aa60af..aee5a78e 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp @@ -43,8 +43,11 @@ #include "modplatform/atlauncher/ATLShareCode.h" #include "Application.h" -AtlOptionalModListModel::AtlOptionalModListModel(QWidget *parent, QVector mods) - : QAbstractListModel(parent), m_mods(mods) { +AtlOptionalModListModel::AtlOptionalModListModel(QWidget* parent, ATLauncher::PackVersion version, QVector mods) + : QAbstractListModel(parent) + , m_version(version) + , m_mods(mods) +{ // fill mod index for (int i = 0; i < m_mods.size(); i++) { auto mod = m_mods.at(i); @@ -97,6 +100,11 @@ QVariant AtlOptionalModListModel::data(const QModelIndex &index, int role) const return mod.description; } } + else if (role == Qt::ForegroundRole) { + if (!mod.colour.isEmpty() && m_version.colours.contains(mod.colour)) { + return QColor(QString("#%1").arg(m_version.colours[mod.colour])); + } + } else if (role == Qt::CheckStateRole) { if (index.column() == EnabledColumn) { return m_selection[mod.name] ? Qt::Checked : Qt::Unchecked; @@ -287,12 +295,13 @@ void AtlOptionalModListModel::setMod(ATLauncher::VersionMod mod, int index, bool } } - -AtlOptionalModDialog::AtlOptionalModDialog(QWidget *parent, QVector mods) - : QDialog(parent), ui(new Ui::AtlOptionalModDialog) { +AtlOptionalModDialog::AtlOptionalModDialog(QWidget* parent, ATLauncher::PackVersion version, QVector mods) + : QDialog(parent) + , ui(new Ui::AtlOptionalModDialog) +{ ui->setupUi(this); - listModel = new AtlOptionalModListModel(this, mods); + listModel = new AtlOptionalModListModel(this, version, mods); ui->treeView->setModel(listModel); ui->treeView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.h b/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.h index 953b288e..8e02444e 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.h +++ b/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.h @@ -56,7 +56,7 @@ public: DescriptionColumn, }; - AtlOptionalModListModel(QWidget *parent, QVector mods); + AtlOptionalModListModel(QWidget *parent, ATLauncher::PackVersion version, QVector mods); QVector getResult(); @@ -86,7 +86,9 @@ private: NetJob::Ptr m_jobPtr; QByteArray m_response; + ATLauncher::PackVersion m_version; QVector m_mods; + QMap m_selection; QMap m_index; QMap> m_dependants; @@ -96,7 +98,7 @@ class AtlOptionalModDialog : public QDialog { Q_OBJECT public: - AtlOptionalModDialog(QWidget *parent, QVector mods); + AtlOptionalModDialog(QWidget *parent, ATLauncher::PackVersion version, QVector mods); ~AtlOptionalModDialog() override; QVector getResult() { diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp index df9b9207..03923ed9 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp @@ -169,8 +169,9 @@ void AtlPage::onVersionSelectionChanged(QString data) suggestCurrent(); } -QVector AtlPage::chooseOptionalMods(QVector mods) { - AtlOptionalModDialog optionalModDialog(this, mods); +QVector AtlPage::chooseOptionalMods(ATLauncher::PackVersion version, QVector mods) +{ + AtlOptionalModDialog optionalModDialog(this, version, mods); optionalModDialog.exec(); return optionalModDialog.getResult(); } diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlPage.h b/launcher/ui/pages/modplatform/atlauncher/AtlPage.h index c95b0127..eac86b51 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlPage.h +++ b/launcher/ui/pages/modplatform/atlauncher/AtlPage.h @@ -84,7 +84,7 @@ private: void suggestCurrent(); QString chooseVersion(Meta::VersionListPtr vlist, QString minecraftVersion) override; - QVector chooseOptionalMods(QVector mods) override; + QVector chooseOptionalMods(ATLauncher::PackVersion version, QVector mods) override; private slots: void triggerSearch(); From 305973c0e7c07693a8b08d1908e64fc4986e13e0 Mon Sep 17 00:00:00 2001 From: Jamie Mansfield Date: Thu, 5 May 2022 20:14:19 +0100 Subject: [PATCH 42/67] ATLauncher: Display install messages if applicable --- .../atlauncher/ATLPackInstallTask.cpp | 7 ++- .../atlauncher/ATLPackInstallTask.h | 45 +++++++++++++---- .../atlauncher/ATLPackManifest.cpp | 50 +++++++++++++++---- .../modplatform/atlauncher/ATLPackManifest.h | 46 +++++++++++++---- .../pages/modplatform/atlauncher/AtlPage.cpp | 13 ++++- .../ui/pages/modplatform/atlauncher/AtlPage.h | 1 + 6 files changed, 126 insertions(+), 36 deletions(-) diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp index 90dc1365..9b14f355 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp @@ -95,14 +95,13 @@ void PackInstallTask::onDownloadSucceeded() qDebug() << "PackInstallTask::onDownloadSucceeded: " << QThread::currentThreadId(); jobPtr.reset(); - QJsonParseError parse_error; + QJsonParseError parse_error {}; QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error); if(parse_error.error != QJsonParseError::NoError) { qWarning() << "Error while parsing JSON response from FTB at " << parse_error.offset << " reason: " << parse_error.errorString(); qWarning() << response; return; } - auto obj = doc.object(); ATLauncher::PackVersion version; @@ -117,6 +116,10 @@ void PackInstallTask::onDownloadSucceeded() } m_version = version; + // Display install message if one exists + if (!m_version.messages.install.isEmpty()) + m_support->displayMessage(m_version.messages.install); + auto ver = getComponentVersion("net.minecraft", m_version.minecraft); if (!ver) { emitFailed(tr("Failed to get local metadata index for '%1' v%2").arg("net.minecraft", m_version.minecraft)); diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.h b/launcher/modplatform/atlauncher/ATLPackInstallTask.h index 6bc30689..f0af4e3a 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.h +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.h @@ -1,18 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0-only /* - * Copyright 2020-2021 Jamie Mansfield - * Copyright 2021 Petr Mrazek + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 Jamie Mansfield * - * 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 2020-2021 Jamie Mansfield + * Copyright 2021 Petr Mrazek + * + * 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 @@ -45,6 +64,10 @@ public: */ virtual QString chooseVersion(Meta::VersionListPtr vlist, QString minecraftVersion) = 0; + /** + * Requests a user interaction to display a message. + */ + virtual void displayMessage(QString message) = 0; }; class PackInstallTask : public InstanceTask diff --git a/launcher/modplatform/atlauncher/ATLPackManifest.cpp b/launcher/modplatform/atlauncher/ATLPackManifest.cpp index a8f2711b..259c170c 100644 --- a/launcher/modplatform/atlauncher/ATLPackManifest.cpp +++ b/launcher/modplatform/atlauncher/ATLPackManifest.cpp @@ -1,18 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0-only /* - * Copyright 2020-2021 Jamie Mansfield - * Copyright 2021 Petr Mrazek + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 Jamie Mansfield * - * 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 2020-2021 Jamie Mansfield + * Copyright 2021 Petr Mrazek + * + * 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 "ATLPackManifest.h" @@ -186,6 +205,12 @@ static void loadVersionMod(ATLauncher::VersionMod & p, QJsonObject & obj) { p.effectively_hidden = p.hidden || p.library; } +static void loadVersionMessages(ATLauncher::VersionMessages& m, QJsonObject& obj) +{ + m.install = Json::ensureString(obj, "install", ""); + m.update = Json::ensureString(obj, "update", ""); +} + void ATLauncher::loadVersion(PackVersion & v, QJsonObject & obj) { v.version = Json::requireString(obj, "version"); @@ -238,4 +263,7 @@ void ATLauncher::loadVersion(PackVersion & v, QJsonObject & obj) for (const auto &key : colourObj.keys()) { v.colours[key] = Json::requireString(colourObj.value(key), "colour"); } + + auto messages = Json::ensureObject(obj, "messages"); + loadVersionMessages(v.messages, messages); } diff --git a/launcher/modplatform/atlauncher/ATLPackManifest.h b/launcher/modplatform/atlauncher/ATLPackManifest.h index 2911107e..931a11dc 100644 --- a/launcher/modplatform/atlauncher/ATLPackManifest.h +++ b/launcher/modplatform/atlauncher/ATLPackManifest.h @@ -1,17 +1,36 @@ +// SPDX-License-Identifier: GPL-3.0-only /* - * Copyright 2020 Jamie Mansfield + * PolyMC - Minecraft Launcher + * Copyright (c) 2022 Jamie Mansfield * - * 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 2020 Jamie Mansfield + * + * 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 @@ -124,6 +143,12 @@ struct VersionConfigs QString sha1; }; +struct VersionMessages +{ + QString install; + QString update; +}; + struct PackVersion { QString version; @@ -138,6 +163,7 @@ struct PackVersion VersionConfigs configs; QMap colours; + VersionMessages messages; }; void loadVersion(PackVersion & v, QJsonObject & obj); diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp index 03923ed9..7bc6fc6b 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp @@ -45,8 +45,12 @@ #include -AtlPage::AtlPage(NewInstanceDialog* dialog, QWidget *parent) - : QWidget(parent), ui(new Ui::AtlPage), dialog(dialog) +#include + +AtlPage::AtlPage(NewInstanceDialog* dialog, QWidget* parent) + : QWidget(parent) + , ui(new Ui::AtlPage) + , dialog(dialog) { ui->setupUi(this); @@ -211,3 +215,8 @@ QString AtlPage::chooseVersion(Meta::VersionListPtr vlist, QString minecraftVers vselect.exec(); return vselect.selectedVersion()->descriptor(); } + +void AtlPage::displayMessage(QString message) +{ + QMessageBox::information(this, tr("Installing"), message); +} diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlPage.h b/launcher/ui/pages/modplatform/atlauncher/AtlPage.h index eac86b51..aa6d5da1 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlPage.h +++ b/launcher/ui/pages/modplatform/atlauncher/AtlPage.h @@ -85,6 +85,7 @@ private: QString chooseVersion(Meta::VersionListPtr vlist, QString minecraftVersion) override; QVector chooseOptionalMods(ATLauncher::PackVersion version, QVector mods) override; + void displayMessage(QString message) override; private slots: void triggerSearch(); From b84d52be3d1109efc2c9e35304831314050bd398 Mon Sep 17 00:00:00 2001 From: Jamie Mansfield Date: Thu, 5 May 2022 20:58:12 +0100 Subject: [PATCH 43/67] ATLauncher: Display warnings when selecting optional mods --- .../modplatform/atlauncher/ATLPackManifest.cpp | 6 ++++++ .../modplatform/atlauncher/ATLPackManifest.h | 2 ++ .../atlauncher/AtlOptionalModDialog.cpp | 16 +++++++++++++++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/launcher/modplatform/atlauncher/ATLPackManifest.cpp b/launcher/modplatform/atlauncher/ATLPackManifest.cpp index 259c170c..d01ec32c 100644 --- a/launcher/modplatform/atlauncher/ATLPackManifest.cpp +++ b/launcher/modplatform/atlauncher/ATLPackManifest.cpp @@ -198,6 +198,7 @@ static void loadVersionMod(ATLauncher::VersionMod & p, QJsonObject & obj) { } } p.colour = Json::ensureString(obj, QString("colour"), ""); + p.warning = Json::ensureString(obj, QString("warning"), ""); p.client = Json::ensureBoolean(obj, QString("client"), false); @@ -264,6 +265,11 @@ void ATLauncher::loadVersion(PackVersion & v, QJsonObject & obj) v.colours[key] = Json::requireString(colourObj.value(key), "colour"); } + auto warningsObj = Json::ensureObject(obj, "warnings"); + for (const auto &key : warningsObj.keys()) { + v.warnings[key] = Json::requireString(warningsObj.value(key), "warning"); + } + auto messages = Json::ensureObject(obj, "messages"); loadVersionMessages(v.messages, messages); } diff --git a/launcher/modplatform/atlauncher/ATLPackManifest.h b/launcher/modplatform/atlauncher/ATLPackManifest.h index 931a11dc..23e162e3 100644 --- a/launcher/modplatform/atlauncher/ATLPackManifest.h +++ b/launcher/modplatform/atlauncher/ATLPackManifest.h @@ -130,6 +130,7 @@ struct VersionMod QString group; QVector depends; QString colour; + QString warning; bool client; @@ -163,6 +164,7 @@ struct PackVersion VersionConfigs configs; QMap colours; + QMap warnings; VersionMessages messages; }; diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp index aee5a78e..004fdc57 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp @@ -231,7 +231,21 @@ void AtlOptionalModListModel::clearAll() { } void AtlOptionalModListModel::toggleMod(ATLauncher::VersionMod mod, int index) { - setMod(mod, index, !m_selection[mod.name]); + auto enable = !m_selection[mod.name]; + + // If there is a warning for the mod, display that first (if we would be enabling the mod) + if (enable && !mod.warning.isEmpty() && m_version.warnings.contains(mod.warning)) { + auto message = QString("%1

%2") + .arg(m_version.warnings[mod.warning], tr("Are you sure that you want to enable this mod?")); + + // fixme: avoid casting here + auto result = QMessageBox::warning((QWidget*) this->parent(), tr("Warning"), message, QMessageBox::Yes | QMessageBox::No); + if (result != QMessageBox::Yes) { + return; + } + } + + setMod(mod, index, enable); } void AtlOptionalModListModel::setMod(ATLauncher::VersionMod mod, int index, bool enable, bool shouldEmit) { From b2a89ee4b99f1d89dddee2918195f73b6b92c9db Mon Sep 17 00:00:00 2001 From: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Date: Sat, 21 May 2022 16:59:01 +0200 Subject: [PATCH 44/67] change cf icon to a more fancy one taken from QuiltMC/art in the emoji folder, so it's licensed under CC0 --- .../multimc/128x128/instances/flame.png | Bin 3375 -> 6226 bytes .../multimc/32x32/instances/flame.png | Bin 849 -> 0 bytes launcher/resources/multimc/multimc.qrc | 4 +--- 3 files changed, 1 insertion(+), 3 deletions(-) delete mode 100644 launcher/resources/multimc/32x32/instances/flame.png diff --git a/launcher/resources/multimc/128x128/instances/flame.png b/launcher/resources/multimc/128x128/instances/flame.png index 8a50a0b418e8dc27ebb91ab3886d8bea40987104..6482975c494e02522f34a10894bb434f1ccc7194 100644 GIT binary patch literal 6226 zcmc&(c|4SB`=1%h9Lf?|B4&_~7-Ki~7~4TY2xT2(EMu54)-vjdghRG0W1TEnQX;Z6 z9c2qKl0;G}ONff5A%2fK?>X=Jo%6o`zR%|~pLy>4dtKLk?bq|nT(Yw@=iwIP27y34 zmKLV=AP^Y11%rO#1iswEs{BA8_Bx!S3(3XC8s!rbtnB3*;*C*`3?=|L2&88iN$~Os z#E{_L7%UF2Fa4qIo-`cit1s=WVWVP0Fv0laETY3O4$-!bKGA_bI=<3|2Hbj)C_q3k zhU5j03=YB*QIY!6-{qo!``vAXH2gaWDNtY9#l{Y95)y`iYba|dt4JGg!}Y>^{ZRI% zX1_B7D}8By5{ZC9AR;0nlq1xXL&C5Kq>hdbLPZs!s;UG~C=sLZB(F#%JW*y>#19#! z7@|)YjzGeN;NiP6y}U!hN&3>#01p3MI3^PJr*u5=ck=;9Aa)T1QdtG@@8l$$-+v(A zMgB%k@DCw{5dA|4e;DwWBmRK@#t69YpIIWk2>(QEWAoq1gM$^TDje*)GRJoazMu69A;yWu^F#{u@J{wdAn zPfTB0OBG10-JpM$cj_OZKVpIUuAfbaFQA^qYYV zuf)6g4A%q5ZER2$I3g(|Eb9B#k0f`%g#K9lSO(#~XEz-FJ;o?6pI!IpOB2FEe8YV( zzTXW3#Qi{tA%3I?uP}@;76`Atw6UKb4sc`?94H(d7LN&oBb9ZOe|Yr|o(O-;ZoL25 zF2wJOA$H5{Uz+J5{-(R$cY=SbPk`^o9#Ho{13~=JL4d^{JqUvb8c*16hneY@1G?A+ z91xGc#Q_A8&j`Ac#nLMt|8%bCapl}sJx{KRtlMX_KnvDR zpElcv5{Ao0S;^VL2S1MpIg!FORSwXupVhbX>DIk-s)p4XSEnF0DpZBzmvnGcQq+^x zYs@)mGc81x?;|)e-Jf0S<4E{mqi_F4D^n1?jXbr7)pMV5WXal*Ks%pJ%Ms4W9i`g7 z&0?G#$bIIW_m%R%c8!;`#aw;uo6psKz%TtoUSjBMsfUweA58wu-W#xQ9(H%nZR(YD#~W{Eiaq^}quOao;McCr1L}z-%fe#0$@+@RMAlFD zdA?p3e`ZKIqZ8t>&5@~wWvwn6NCU%yi(qkz2m1ZQOwp)qUL_eCZCp@AOSM=wKR_a$q?>+uBh4ouma9AJe zsC-#pU(aFszqkT!x~zcm524!R8t^OHriIQc%Hf5aj1kL3sFxy3>E49w@S6>#OmkGb&HKHk7fiBw{Qb-qpjVusNEelkkVxYHo zs#upd10@gsT;W6^ofj|)u>vu;tUw(KU<)~5mkeE<1Y|neaFsx3y8`J=yE5|+v#m$) zGz6}|yN55+Ptx`2ne2~iWK7Q&8ffn{iY)+N$zQd>(fri)>Be8(DQ{dYJf^=|qqGClSK(a@Bk4wcy6%Aps z7&{>q0cS-23f2{a;fqNihNf+zg%wq6zoqiceU6qwwQ&+)Vj)*X zYv}8Ap@-wC-H8c;2-_|1A&#_aLrq-kdlq#oA$+8-xa$Hf^IO;qv^%kqC853cNJm1O zTR%7Zeg|jlxr;u{*Jb!$Y1n&AN5m_p@EO8Br*hJngb>n(?@VvN>r5o+^4crbfwU8l zs;A|2y&1ReM312gO5b2(9Q)CR=kuYKP3|iT_m!EMWA8G#S5U>t|)rkIRDQDdXNDP1Lg_n$CQ8)o#&aZkS3C~Mc}|y z5O-pRWFXfRdk~VYvgo?E-NFRP@>j-1SCV=V6;Z*qByv|Cw~E1(@#34syNwosa_SWx z!Br<+lg$MSGKI-mYY{TlGLc@z<05R^(eztsY%52lDl}bx(w@Dbgc=w3naAX(@EsR} zZGa4*?e6C+81Nop=MR@Fb^Kn=FMSwvOkaD&;Td=LOI{Qw^Xl3w=2ZM=)%W4gQ~2aE za|Svq+X*ydVNsYeV*8wSL&@V%PVMk?NGhKpI89lqxj8cBbJB;HR<;E!EkP`mZ-=cN zrrlNuu+$eZfqV*O(2GOTpJo*-W=Qqx?_+MKm^Zy_q?;$6DwIiNXmVZ}!t)if2iZI2 zT^Y+w;fvE+FBqA?P{J)T-!H^jP9s*ZYsB1i!mmdrp}8)Mk5msoQO&-HD?X&Pg{I9lXV%_6x+# z*X##+cwFuc=VZc;Jg>daTuoVfCC{**mAjIk%up^Ex9h?GYxwN*DL%+gKrdU8w0 zpR^H7FQs`f^TVGg$i#5Cj51eUva?OG2n^*CT8SV`@Vr@N8~EhbX@?PM?R_rd+Mt%w zv}nwspCQFnrA2CiRpkNWAn2XtH;E~e7tFCQd6pbvgQ;r53-6U@yY8NJ;-{Y7gL_*d zbiKCn7y=?L4qF+L=nYbTO}jj_&5P?@=!p*=F}q^(vDJInxw@#r|A zY%UJ#yrP>l5nu|rdK9&3>v`N>^_gU_QubsG9iM5&kp^^hQQTmgp>lcq0A6NrE@aud zr(2fNSJtS=7L$$_B%8YBzWumYrH8rNcw$fT&b!;Pzd^nh`F~Td&Wo zeWPukud|F1sm;)+!ng80yYBKX&?!0I`wm1rOZcRLhn7LCW(0qEfDL5++Pv_XPWj4y#1kRlVFczl`DOAnDfitaA4`>3a{ zsEc?>D+C=A%YL@FgGCpCr#0WiG|L7%At!q-AO=@i4{6^N3RW6IwZE3WRiYT1VskmC zp+W3HE?cepUPz>q_;51Meg$m~-by(x3VX-5S@AkzB*Zu+ftFFO#aOaDCL#*!E@cy( zH-gN|q1qq{$Xc`&r^AkW_8RM}9``|$=h&5Hde`3O4rhd+y1|l4ftDHexU|w?wXu8R zu#J?VAt-Ml#{Hz*Pqo2*L8=PECQGgtA7ZK2R+tWEod zW`3@AJ(~eOHc}Lh+LRmEegnNKPCeb^BIoI1e&w7K)H?Z<9If>A@DdU(k}}%ICt(Xx zLmIT31}O72y*@!Ab1|x#FFj~LuGTTw4o=;Jm|HKjO!_=f&4t?ti$#kRcpq50P~O;+ zeohm?=6nEln9LurSUx>YLCo=%1zCY)6%Bfi3wwaH(AZxOy}&*b`d2qym>~vU#HIun9}>}Duer#?;P@p zScl3|?iN~he3~vk^y1SO@iJF4NMzVMlZ(v?A=cc{Mel+vBQTTZTkGZQb{EQ8-R4+? zN>P|3_1*fJJu$pKQq{BdK~NjkW*BYI)J*c6`}v#gtP280)OTZ!k3Z2B)D9BIlESAj zv618ls?$L73a`#Nbe@%8HD=HX-0P7BDa+Frtw8HvmDi7^l7R_x3G96Ut)RwMxq6hv z?s=LqQ}=DbkWv+3;Ly=z2dTQW_G*XnDX=OaeYpi8mD*B}YSFj3g~ooN?NmKz?M|Gu zbi<7=OU>NcyZ+RStK`h5fU4QAdA#yD^(+Xkey3pwv5|BXO(M%OF3NlxP}?S^zDw#(tckw>)$d$YS-sQ96(Ht;WsOm$Ic~_+-E{+3 zj=Os-XMq--*RN9IKV+x!VXtg_6Fnw5r&`4s`NOwNU`nzKy}u_qUL{ z6QN~|hSI^!r_<28Gry_cU^(plsMbR6zlV@X<*5og zSvpr$*`?W5G3fgKm-!`QN1`HsJZ7@N0z)Dz_?>~Xdrp>p@ckIGlNE4dxKbR(TivkL zJ*;FTDT4HMIBUci8N%__$t(R(IP7vW2>xyB`Vw`AoPg0v7BB)v>|8Y-GqtXG2ahJVb=0p2ZVxh7Jx1H7iBh=& zDeLs&>MYR5ugL04Zq0m7c77+Lo6ZYE;`n0yE`Hf=-Xc%}F6wCI2pAnkkCNvTG@uQq zN#t4XekJPKt4W&dZ<1{1z=b?ZzOl?iu-VK*?qksjEOT+cYdw^xL1@~wtxBXhsQ5_h zfR!lB7G!&M(CtwgN6S;DUC%Fd-FAE6K!iJJgPQ%#aX0IpYfs)fuc{lq!)FDuQMqwm zH5a^Hc0NqJRMeya&4G?s+=gX=@@F7s9;KDJK4p!%PIh>~ATz5lOmvIrz6k{@P@d4^ z+lcxy|Cz1%C6rJWb+TC~YofKrP#&y~KB>m;%&x3JHWrhcJZV=UK=J)#GIKw<{~U~f zCy|d9G4~7RkQG{=ImavhtlTJ+1rjTXe0SC_;pEa@cM0mZZ`|*z9*xWI5MuzttFJr? z;*h1htBNj7uZu4>!y29n)z7{(w89bOVLU<&+P+Z1re#Iia9x}@FsDl|Wvi8(5`9>H zPnUAU6UP*ua%#6O&(MQMCa3||LkJ2oG`qw33} z+DNJE$7}pIilL6JsK+)!IWHo8*8Kek3c*VSrPE56ZwQt(V$&sU{N76-B)?Ei*GCJm zZN~jBc5cy^1kWj|A7)t zphBGLK#7yoi%ggRwL_AbvgTEF=5v{I+#b&ePd?9bMtN)(n$E{+-+0gcKA58kY{8OH;K^Wv2Fn$Aw06$z zUNhz6pqGza;sUEUhVj@FgPaG-T`#Ghf#(vRH<*M8%7bqM&x@fm$y1@~>G?Zs_7H!= zv(H{G&0%;>tU&ZR{4?g4$VXA^kvImyd42BC93#dndoq=e3S;SJ3mE-uiglLd**;Md zOt#f9I9Q~cR56lRtxq<;cjH9t(Fl+Yjn6S=DMOc%<0gY@JGBDO2l_KdS>q$f?A^y6 z)GX6VJBWWGwO4B=u23FqUCKVp%bLmYnVny1BSJ?G0fBd}-RCmSiacH7i@GFW^y!<_ zYTA@>N`^kxfTjy%?s4bZu(NPfn>`@<7+1;HEKq+F`|iu;?&^M7qqyDRxyaL%G*{pp z&9injYZ-@w<8v|vj2acdjy_SIiBrbv@d8FBXsokbaFNuQT^JCXKC*eEM$PS*n6b8t%7cLy=jg>PXyf7c zXzX~OgsCl{z93bDt_*}Z-B!*GsN@M=7i0v#L>W6gm9fC*%SkM2)X_rrS#gy_0b`{} z8#jQK15UIckp*WWh&?05RGYh2ph3n2pSK@OS@_KIL0O952}rNcftjw>3%$l%B~oRL zf#8C7Y2{t0yx43|zKQPmn
GjYyjZo7-;X`iCqg<8k}pw0`R*8rY^sAxqTWON*= zWgp(#2KgEIY2dCFhZ|SP0aV0WT-6s-?D%5|Q>yg}h@DbHDIh|nC_`NP{s&jakuAe_ YzkS2@~ literal 3375 zcmV+~4bbw5P)xjk|4;AkyYG4O2I0iRjPm#s|XYX zse&97r5;L=Byc<~uW%?N1f_!1;t`?Ng(B!F*eWV&T|un~Sf$#-qOu7hTOj1Ucl*Z# z2_)gYS?=7K>3q)NkGwbY`~Jv$bLY-oeghE^5fKp)5fKp)5fKp)5fKsp7YN-6s6q}U zi}rLx(vB3(F%YCUfWa2BSuf(0>dw&*gvtOijzK8{kcI4`SwoYMaZUL2% z1-uUYOi}oq9OZm5mjjC8drGKUfFDRvtT1{D>Ts{Z_?{9@1JvhbP&lz8m>>%{S6O@y z38eulCUaIPl&B5dqcFZ_gwg;22;NhyFhUk`mSPR%gx3I)O^Ow2lj1juHIx%(18h^S zA-GMBrIvCXg@o4tF;A9xHKsm&JkU`_cnwgOa!+(bu&0c}Yk*Q^a{?zLxIqpj%M&dH zgx3I)HXbQ8q%EC2(sG^f8bHwBL#-a3X}L@&4ItwfNQQVPNKenSTqcwTpp*f?*&ZsM zNwmRWJ??H4$w-S#Rg@-60(?fYKx(hU>P8vVgv=c^KeK4$&=J# zvY5fQx|0cv0b~LBz%1Q)xX5k{Mv5X{)|o^g3?K^_3Oq|dJFPWPj$k7x#)%Tv=t{!( z29WvmM6j9@ovWbp8iM&`v&s*hLSGv|PN6MH-UpiN&cy}3t|&kEs_sN86VshQO~~<_ zL6Q}O;o?g^LDEOeW{>VvPQ{n^R5_Mf)MYvT80c}z0b&gF29xjnY@R;r-PKU2LfpQ8RNb(VSR`sZ0otQ;e-ASAd-?RWS zPK*@K5}u0>!oN-bas2ZXaG1M+i*={rDiWzXg>?9$1(1_y1Kh7W4+ZSgoj^K#VE_!~ zLvZHMGe&x}L)ih^4In3RC2+0oJd_~l?)R(psu24 zdSZ3KP}~oe3QO0zfZi5BXsoi8(-dx81gV5t9l=03*}nxOv>8C=(+u<7e%{l(BTV?v zt6?yM6mzA|2HlRMuLUr;1vHdcYts|%d=nbBhJ9PSQtd?nqr4Kcf<6OC4Q1Cm4}m*Y zLrxR(=kg<7s?U;>XyT<{CTKB$9M2ix@fdlJJK^RfX2(QER=xNDP0#ZzjUN!qlN0?j zLa3_`;LT%Ey@oJyIT$rGHm!R=9nbfbo=J@PUW?X*9s@{^*7@D^mlGYn>WI~W^ZI#` z$|($xg&vl%I}LgaAZV&wqwP=Of?uRRu7{?9I>}r)g){YtpFr0wK&=5p7L0r@vtv8- zhL&BFrc(ngNHR@_aAkBEAX{nlt|K5f!@G|~D~EI$Kuuu;VL*#c_Rnv6K6L)2d--He9&NZ( zhp;XKsM#mis2$YKbr$3D@$Tee6+vGevbshB)wBSo8}-YSwwFWW4$k9P=PDhddJJGF z+A(9ew7%q`f$qeysH#I$*Dc_`N}^`DWPtX)+=*k2Ch{KdYa!^G3s7T#qb|KGvehN- z#IZ`b_b;B%V}JzzQ4)8LOXc@+o9mb+21z5YCAAo!qFTll*8`UwghRXBiRDIz(kccH zA7SiB>Oxh8?agnw6UVLogcSs3UQ6m+7Vg{twW^s^-1xlXumBv{?@k;mq&Vueq|O%L zZO1Xr=~Z>P*Jm$6!r|{RaTH4TW_E0>wll5D2zI*{EHm^OAcr-8vp=)5FM&P}oc!6* z{qX*u9ED})rhM|VimmxkSxQ=;*jG#bt7hO{22ATIUxkFdIo&)iuIV=-iNQ#^iPEW z!xlmP7Us`CzR9{ym6)t`9>h9Y4IoO`4Xm~u;M7O8P6MIv3mCluF1rgpe%ku}zn3z2 z`&^&<;lk@q{M@_~GO?8bI8*iSAm*ge@PzA%88 z$rdEm$8fiQ;!Jqgr!cPvd{fO6n2`mSj)6&^!UY*rv9mjSC|Ink`3HY=0!<|MAsC32 zoOY}SDXp*PPbJ~)KftQ{;n?Amzl+s{E;qteQ=plJO*`ebu3g4>Z9&WuS3YadsfRT}nxWA;TdBSDH=#6x&mqpl`= zX8-_E%q9f4LWVC~_}~fWi$I+4uP03-W`ZOxX7hpCgfhpM1^^JVScYU~Mh73-1#2{w z;FBp>vmn!>mr%m*Rigbt=xw>S(KjFXLw!t(`4N<<~%}r-Xpu5xzEn zSW1!{Mj}aH@aMn|cxSE$Sydug4y&q}8LY}$4Ep=!zF!$%8vrVnjvZhGg1@Fc_|>Vf z&%-CHH;U3v0ZZPcoPMIn;rd5K_}%~jVgcn8GaSJb5H&ZU#}32ukT zQ^wU|4leCkb01&Uus(Fte zVHwC!v}Ms5;op;QtSt!fhyDFk6sQ5c3+7R*2x8L8vk(_)6^(cF9I zp>8@b+_~rc&;R>z?>+Z^@V|$Ow?R&m%L0u+F))dZUUd=AvH*yS3KVyMypZ${6iupw zTZsTz;!<0g2Sy^Y*$92&;%FiO@!x`atZZNi*bz~h#Yd^?WGo)P_^tq;2JxZji&9F% z%H??d;syXf{?O)IgSIN$-uF2$pEAtD=WAOtTK?fmkfYH zdVqZf3Lj7)&9sP%3W;ILVje?jrWI&679prG&u%6-p$73K1t2aOPz=PX55SyMSLrm9 znHtI|idbM2MP|&4!yFmdRU4X_(EJ(j(POx{#S(21jjvcuz0ClmnKGbCSFrssXj(Ln zbR0A$4FK%dt&}xDO^1G`a79`T$2A53p*U(jV!t#`w1RC=Tpj81C1C*7#T4)& zsJ21n8Q9Yf65yH~hIbDkrx2X?VDsK^6X9vX0DcOB!#@29G@pO`l!DQ_aN7yx$DrvY zIM#)arLuT|0V8{xDS(~(sQg#VxXbYVhVdt`vC>~FJ>dU^#`ljQ?kSz>!mQ!`!1M~J zHZ-Q)b!6oI0$}Kxal=Tz|CqJ<4L@hknfb@GAyHjSk(yaTPLYSV4`A$3bm4H1|AAb& zX&B$Pa@EaTEdDWvv|*7O)n}!*_JhoL+!72;CZ3PW_1&Y=y;J}DvDhIZ*&?!5MADaS b(Z!BG!B5Qc$A}(=00000NkvXXu0mjfi>7~8 diff --git a/launcher/resources/multimc/multimc.qrc b/launcher/resources/multimc/multimc.qrc index e22fe7ee..2337acd6 100644 --- a/launcher/resources/multimc/multimc.qrc +++ b/launcher/resources/multimc/multimc.qrc @@ -6,8 +6,7 @@ scalable/reddit-alien.svg - - 32x32/instances/flame.png + 128x128/instances/flame.png @@ -272,7 +271,6 @@ 32x32/instances/ftb_logo.png 128x128/instances/ftb_logo.png - 32x32/instances/flame.png 128x128/instances/flame.png 32x32/instances/gear.png From 35f71f5793ee91a71e00464932ff95eb5e5e4d5e Mon Sep 17 00:00:00 2001 From: Lenny McLennington Date: Wed, 11 May 2022 21:44:06 +0100 Subject: [PATCH 45/67] Support paste.gg, hastebin, and mclo.gs --- launcher/Application.cpp | 33 +++++- launcher/net/PasteUpload.cpp | 165 ++++++++++++++++++++++++--- launcher/net/PasteUpload.h | 28 ++++- launcher/ui/GuiUtil.cpp | 5 +- launcher/ui/pages/global/APIPage.cpp | 51 ++++++++- launcher/ui/pages/global/APIPage.h | 1 + launcher/ui/pages/global/APIPage.ui | 64 +++-------- 7 files changed, 272 insertions(+), 75 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index ce62c41a..b36fd89a 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -36,6 +36,7 @@ #include "Application.h" #include "BuildConfig.h" +#include "net/PasteUpload.h" #include "ui/MainWindow.h" #include "ui/InstanceWindow.h" @@ -671,8 +672,36 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_settings->registerSetting("UpdateDialogGeometry", ""); - // pastebin URL - m_settings->registerSetting("PastebinURL", "https://0x0.st"); + // This code feels so stupid is there a less stupid way of doing this? + { + m_settings->registerSetting("PastebinURL", ""); + QString pastebinURL = m_settings->get("PastebinURL").toString(); + + // If PastebinURL hasn't been set before then use the new default: mclo.gs + if (pastebinURL == "") { + m_settings->registerSetting("PastebinType", PasteUpload::PasteType::Mclogs); + m_settings->registerSetting("PastebinCustomAPIBase", ""); + } + // Otherwise: use 0x0.st + else { + // The default custom endpoint would usually be "" (meaning there is no custom endpoint specified) + // But if the user had customised the paste URL then that should be carried over into the custom endpoint. + QString defaultCustomEndpoint = (pastebinURL == "https://0x0.st") ? "" : pastebinURL; + m_settings->registerSetting("PastebinType", PasteUpload::PasteType::NullPointer); + m_settings->registerSetting("PastebinCustomAPIBase", defaultCustomEndpoint); + + m_settings->reset("PastebinURL"); + } + + bool ok; + unsigned int pasteType = m_settings->get("PastebinType").toUInt(&ok); + // If PastebinType is invalid then reset the related settings. + if (!ok || !(PasteUpload::PasteType::First <= pasteType && pasteType <= PasteUpload::PasteType::Last)) + { + m_settings->reset("PastebinType"); + m_settings->reset("PastebinCustomAPIBase"); + } + } m_settings->registerSetting("CloseAfterLaunch", false); m_settings->registerSetting("QuitAfterGameStop", false); diff --git a/launcher/net/PasteUpload.cpp b/launcher/net/PasteUpload.cpp index 3d106c92..d583216d 100644 --- a/launcher/net/PasteUpload.cpp +++ b/launcher/net/PasteUpload.cpp @@ -42,8 +42,22 @@ #include #include -PasteUpload::PasteUpload(QWidget *window, QString text, QString url) : m_window(window), m_uploadUrl(url), m_text(text.toUtf8()) +std::array PasteUpload::PasteTypes = { + {{"0x0", "https://0x0.st", ""}, + {"hastebin", "https://hastebin.com", "/documents"}, + {"paste (paste.gg)", "https://paste.gg", "/api/v1/pastes"}, + {"mclogs", "https://api.mclo.gs", "/1/log"}}}; + +PasteUpload::PasteUpload(QWidget *window, QString text, QString baseUrl, PasteType pasteType) : m_window(window), m_baseUrl(baseUrl), m_pasteType(pasteType), m_text(text.toUtf8()) { + if (m_baseUrl == "") + m_baseUrl = PasteTypes.at(pasteType).defaultBase; + + // HACK: Paste's docs say the standard API path is at /api/ but the official instance paste.gg doesn't follow that?? + if (pasteType == PasteGG && m_baseUrl == PasteTypes.at(pasteType).defaultBase) + m_uploadUrl = "https://api.paste.gg/v1/pastes"; + else + m_uploadUrl = m_baseUrl + PasteTypes.at(pasteType).endpointPath; } PasteUpload::~PasteUpload() @@ -53,26 +67,73 @@ PasteUpload::~PasteUpload() void PasteUpload::executeTask() { QNetworkRequest request{QUrl(m_uploadUrl)}; + QNetworkReply *rep{}; + request.setHeader(QNetworkRequest::UserAgentHeader, BuildConfig.USER_AGENT_UNCACHED); - QHttpMultiPart *multiPart = new QHttpMultiPart{QHttpMultiPart::FormDataType}; + switch (m_pasteType) { + case NullPointer: { + QHttpMultiPart *multiPart = + new QHttpMultiPart{QHttpMultiPart::FormDataType}; - QHttpPart filePart; - filePart.setBody(m_text); - filePart.setHeader(QNetworkRequest::ContentTypeHeader, "text/plain"); - filePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"file\"; filename=\"log.txt\""); + QHttpPart filePart; + filePart.setBody(m_text); + filePart.setHeader(QNetworkRequest::ContentTypeHeader, "text/plain"); + filePart.setHeader(QNetworkRequest::ContentDispositionHeader, + "form-data; name=\"file\"; filename=\"log.txt\""); + multiPart->append(filePart); - multiPart->append(filePart); + rep = APPLICATION->network()->post(request, multiPart); + multiPart->setParent(rep); - QNetworkReply *rep = APPLICATION->network()->post(request, multiPart); - multiPart->setParent(rep); + break; + } + case Hastebin: { + request.setHeader(QNetworkRequest::UserAgentHeader, BuildConfig.USER_AGENT_UNCACHED); + rep = APPLICATION->network()->post(request, m_text); + break; + } + case Mclogs: { + QUrlQuery postData; + postData.addQueryItem("content", m_text); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + rep = APPLICATION->network()->post(request, postData.toString().toUtf8()); + break; + } + case PasteGG: { + QJsonObject obj; + QJsonDocument doc; + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - m_reply = std::shared_ptr(rep); - setStatus(tr("Uploading to %1").arg(m_uploadUrl)); + obj.insert("expires", QDateTime::currentDateTimeUtc().addDays(100).toString(Qt::DateFormat::ISODate)); + + QJsonArray files; + QJsonObject logFileInfo; + QJsonObject logFileContentInfo; + logFileContentInfo.insert("format", "text"); + logFileContentInfo.insert("value", QString::fromUtf8(m_text)); + logFileInfo.insert("name", "log.txt"); + logFileInfo.insert("content", logFileContentInfo); + files.append(logFileInfo); + + obj.insert("files", files); + + doc.setObject(obj); + rep = APPLICATION->network()->post(request, doc.toJson()); + break; + } + } connect(rep, &QNetworkReply::uploadProgress, this, &Task::setProgress); - connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(downloadError(QNetworkReply::NetworkError))); - connect(rep, SIGNAL(finished()), this, SLOT(downloadFinished())); + connect(rep, &QNetworkReply::finished, this, &PasteUpload::downloadFinished); + // This function call would be a lot shorter if we were using the latest Qt + connect(rep, + static_cast(&QNetworkReply::error), + this, &PasteUpload::downloadError); + + m_reply = std::shared_ptr(rep); + + setStatus(tr("Uploading to %1").arg(m_uploadUrl)); } void PasteUpload::downloadError(QNetworkReply::NetworkError error) @@ -102,6 +163,82 @@ void PasteUpload::downloadFinished() return; } - m_pasteLink = QString::fromUtf8(data).trimmed(); + switch (m_pasteType) + { + case NullPointer: + m_pasteLink = QString::fromUtf8(data).trimmed(); + break; + case Hastebin: { + QJsonDocument jsonDoc{QJsonDocument::fromJson(data)}; + QJsonObject jsonObj{jsonDoc.object()}; + if (jsonObj.contains("key") && jsonObj["key"].isString()) + { + QString key = jsonDoc.object()["key"].toString(); + m_pasteLink = m_baseUrl + "/" + key; + } + else + { + emitFailed(tr("Error: %1 returned a malformed response body").arg(m_uploadUrl)); + qCritical() << m_uploadUrl << " returned malformed response body: " << data; + return; + } + break; + } + case Mclogs: { + QJsonDocument jsonDoc{QJsonDocument::fromJson(data)}; + QJsonObject jsonObj{jsonDoc.object()}; + if (jsonObj.contains("success") && jsonObj["success"].isBool()) + { + bool success = jsonObj["success"].toBool(); + if (success) + { + m_pasteLink = jsonObj["url"].toString(); + } + else + { + QString error = jsonObj["error"].toString(); + emitFailed(tr("Error: %1 returned an error: %2").arg(m_uploadUrl, error)); + qCritical() << m_uploadUrl << " returned error: " << error; + qCritical() << "Response body: " << data; + return; + } + } + else + { + emitFailed(tr("Error: %1 returned a malformed response body").arg(m_uploadUrl)); + qCritical() << m_uploadUrl << " returned malformed response body: " << data; + return; + } + break; + } + case PasteGG: + QJsonDocument jsonDoc{QJsonDocument::fromJson(data)}; + QJsonObject jsonObj{jsonDoc.object()}; + if (jsonObj.contains("status") && jsonObj["status"].isString()) + { + QString status = jsonObj["status"].toString(); + if (status == "success") + { + m_pasteLink = m_baseUrl + "/p/anonymous/" + jsonObj["result"].toObject()["id"].toString(); + } + else + { + QString error = jsonObj["error"].toString(); + QString message = (jsonObj.contains("message") && jsonObj["message"].isString()) ? jsonObj["message"].toString() : "none"; + emitFailed(tr("Error: %1 returned an error code: %2\nError message: %3").arg(m_uploadUrl, error, message)); + qCritical() << m_uploadUrl << " returned error: " << error; + qCritical() << "Error message: " << message; + qCritical() << "Response body: " << data; + return; + } + } + else + { + emitFailed(tr("Error: %1 returned a malformed response body").arg(m_uploadUrl)); + qCritical() << m_uploadUrl << " returned malformed response body: " << data; + return; + } + break; + } emitSucceeded(); } diff --git a/launcher/net/PasteUpload.h b/launcher/net/PasteUpload.h index ea3a06d3..e276234f 100644 --- a/launcher/net/PasteUpload.h +++ b/launcher/net/PasteUpload.h @@ -36,14 +36,38 @@ #include "tasks/Task.h" #include +#include #include #include +#include class PasteUpload : public Task { Q_OBJECT public: - PasteUpload(QWidget *window, QString text, QString url); + enum PasteType : unsigned int { + // 0x0.st + NullPointer, + // hastebin.com + Hastebin, + // paste.gg + PasteGG, + // mclo.gs + Mclogs, + // Helpful to get the range of valid values on the enum for input sanitisation: + First = NullPointer, + Last = Mclogs + }; + + struct PasteTypeInfo { + const QString name; + const QString defaultBase; + const QString endpointPath; + }; + + static std::array PasteTypes; + + PasteUpload(QWidget *window, QString text, QString url, PasteType pasteType); virtual ~PasteUpload(); QString pasteLink() @@ -56,7 +80,9 @@ protected: private: QWidget *m_window; QString m_pasteLink; + QString m_baseUrl; QString m_uploadUrl; + PasteType m_pasteType; QByteArray m_text; std::shared_ptr m_reply; public diff --git a/launcher/ui/GuiUtil.cpp b/launcher/ui/GuiUtil.cpp index 9eb658e2..5e9d1eda 100644 --- a/launcher/ui/GuiUtil.cpp +++ b/launcher/ui/GuiUtil.cpp @@ -16,8 +16,9 @@ QString GuiUtil::uploadPaste(const QString &text, QWidget *parentWidget) { ProgressDialog dialog(parentWidget); - auto pasteUrlSetting = APPLICATION->settings()->get("PastebinURL").toString(); - std::unique_ptr paste(new PasteUpload(parentWidget, text, pasteUrlSetting)); + auto pasteTypeSetting = static_cast(APPLICATION->settings()->get("PastebinType").toUInt()); + auto pasteCustomAPIBaseSetting = APPLICATION->settings()->get("PastebinCustomAPIBase").toString(); + std::unique_ptr paste(new PasteUpload(parentWidget, text, pasteCustomAPIBaseSetting, pasteTypeSetting)); dialog.execWithTask(paste.get()); if (!paste->wasSuccessful()) diff --git a/launcher/ui/pages/global/APIPage.cpp b/launcher/ui/pages/global/APIPage.cpp index 8b806bcf..b2827a19 100644 --- a/launcher/ui/pages/global/APIPage.cpp +++ b/launcher/ui/pages/global/APIPage.cpp @@ -3,6 +3,7 @@ * PolyMC - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (c) 2022 Jamie Mansfield + * Copyright (c) 2022 Lenny McLennington * * 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 @@ -46,15 +47,34 @@ #include "settings/SettingsObject.h" #include "tools/BaseProfiler.h" #include "Application.h" +#include "net/PasteUpload.h" APIPage::APIPage(QWidget *parent) : QWidget(parent), ui(new Ui::APIPage) { + // this is here so you can reorder the entries in the combobox without messing stuff up + unsigned int comboBoxEntries[] = { + PasteUpload::PasteType::Mclogs, + PasteUpload::PasteType::NullPointer, + PasteUpload::PasteType::PasteGG, + PasteUpload::PasteType::Hastebin + }; + static QRegularExpression validUrlRegExp("https?://.+"); + ui->setupUi(this); - ui->urlChoices->setValidator(new QRegularExpressionValidator(validUrlRegExp, ui->urlChoices)); - ui->tabWidget->tabBar()->hide();\ + + for (auto pasteType : comboBoxEntries) { + ui->pasteTypeComboBox->addItem(PasteUpload::PasteTypes.at(pasteType).name, pasteType); + } + + connect(ui->pasteTypeComboBox, static_cast(&QComboBox::currentIndexChanged), this, &APIPage::updateBaseURLPlaceholder); + // This function needs to be called even when the ComboBox's index is still in its default state. + updateBaseURLPlaceholder(ui->pasteTypeComboBox->currentIndex()); + ui->baseURLEntry->setValidator(new QRegularExpressionValidator(validUrlRegExp, ui->baseURLEntry)); + ui->tabWidget->tabBar()->hide(); + loadSettings(); } @@ -63,11 +83,28 @@ APIPage::~APIPage() delete ui; } +void APIPage::updateBaseURLPlaceholder(int index) +{ + ui->baseURLEntry->setPlaceholderText(PasteUpload::PasteTypes.at(ui->pasteTypeComboBox->itemData(index).toUInt()).defaultBase); +} + void APIPage::loadSettings() { auto s = APPLICATION->settings(); - QString pastebinURL = s->get("PastebinURL").toString(); - ui->urlChoices->setCurrentText(pastebinURL); + + unsigned int pasteType = s->get("PastebinType").toUInt(); + QString pastebinURL = s->get("PastebinCustomAPIBase").toString(); + + ui->baseURLEntry->setText(pastebinURL); + int pasteTypeIndex = ui->pasteTypeComboBox->findData(pasteType); + if (pasteTypeIndex == -1) + { + pasteTypeIndex = ui->pasteTypeComboBox->findData(PasteUpload::PasteType::Mclogs); + ui->baseURLEntry->clear(); + } + + ui->pasteTypeComboBox->setCurrentIndex(pasteTypeIndex); + QString msaClientID = s->get("MSAClientIDOverride").toString(); ui->msaClientID->setText(msaClientID); QString curseKey = s->get("CFKeyOverride").toString(); @@ -77,8 +114,10 @@ void APIPage::loadSettings() void APIPage::applySettings() { auto s = APPLICATION->settings(); - QString pastebinURL = ui->urlChoices->currentText(); - s->set("PastebinURL", pastebinURL); + + s->set("PastebinType", ui->pasteTypeComboBox->currentData().toUInt()); + s->set("PastebinCustomAPIBase", ui->baseURLEntry->text()); + QString msaClientID = ui->msaClientID->text(); s->set("MSAClientIDOverride", msaClientID); QString curseKey = ui->curseKey->text(); diff --git a/launcher/ui/pages/global/APIPage.h b/launcher/ui/pages/global/APIPage.h index 20356009..0bb84c89 100644 --- a/launcher/ui/pages/global/APIPage.h +++ b/launcher/ui/pages/global/APIPage.h @@ -73,6 +73,7 @@ public: void retranslate() override; private: + void updateBaseURLPlaceholder(int index); void loadSettings(); void applySettings(); diff --git a/launcher/ui/pages/global/APIPage.ui b/launcher/ui/pages/global/APIPage.ui index eaa44c88..d986c2e2 100644 --- a/launcher/ui/pages/global/APIPage.ui +++ b/launcher/ui/pages/global/APIPage.ui @@ -6,8 +6,8 @@ 0 0 - 603 - 530 + 512 + 538 @@ -36,59 +36,30 @@ - &Pastebin URL + Pastebin Service - - - Qt::Horizontal - - - - - - - - 10 - - + - <html><head/><body><p>Note: only input that starts with <span style=" font-weight:600;">http://</span> or <span style=" font-weight:600;">https://</span> will be accepted.</p></body></html> - - - false + Paste Service Type - - - true - - - QComboBox::NoInsert - - - - https://0x0.st - - - + - + - <html><head/><body><p>Here you can choose from a predefined list of paste services, or input the URL of a different paste service of your choice, provided it supports the same protocol as 0x0.st, that is POST a file parameter to the URL and return a link in the response body.</p></body></html> + Base URL - - Qt::RichText - - - true - - - true + + + + + + @@ -101,13 +72,6 @@ &Microsoft Authentication - - - - Qt::Horizontal - - - From caf6d027282392a58b935185d787c4c22a861409 Mon Sep 17 00:00:00 2001 From: Lenny McLennington Date: Fri, 13 May 2022 17:48:19 +0100 Subject: [PATCH 46/67] Change paste settings and add copyright headers - There's now a notice reminding people to change the base URL if they had a custom base URL and change the paste type (that was something I personally had problems with when I was testing, so a reminder was helpful for me). - Broke down some of the long lines on APIPage.cpp to be more readable. - Added copyright headers where they were missing. - Changed the paste service display names to the names they are more commonly known by. - Changed the default hastebin base URL to https://hst.sh due to the acquisition of https://hastebin.com by Toptal. --- launcher/Application.cpp | 5 ++-- launcher/net/PasteUpload.cpp | 10 +++++--- launcher/net/PasteUpload.h | 3 ++- launcher/ui/GuiUtil.cpp | 37 +++++++++++++++++++++++++++- launcher/ui/pages/global/APIPage.cpp | 37 +++++++++++++++++++++++----- launcher/ui/pages/global/APIPage.h | 4 +++ launcher/ui/pages/global/APIPage.ui | 13 ++++++++++ 7 files changed, 95 insertions(+), 14 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index b36fd89a..40c6e760 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -2,6 +2,7 @@ /* * PolyMC - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022 Lenny McLennington * * 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 @@ -672,7 +673,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_settings->registerSetting("UpdateDialogGeometry", ""); - // This code feels so stupid is there a less stupid way of doing this? + // HACK: This code feels so stupid is there a less stupid way of doing this? { m_settings->registerSetting("PastebinURL", ""); QString pastebinURL = m_settings->get("PastebinURL").toString(); @@ -694,7 +695,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) } bool ok; - unsigned int pasteType = m_settings->get("PastebinType").toUInt(&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)) { diff --git a/launcher/net/PasteUpload.cpp b/launcher/net/PasteUpload.cpp index d583216d..3855190a 100644 --- a/launcher/net/PasteUpload.cpp +++ b/launcher/net/PasteUpload.cpp @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* * PolyMC - Minecraft Launcher + * Copyright (C) 2022 Lenny McLennington + * Copyright (C) 2022 Swirl * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -43,10 +45,10 @@ #include std::array PasteUpload::PasteTypes = { - {{"0x0", "https://0x0.st", ""}, - {"hastebin", "https://hastebin.com", "/documents"}, - {"paste (paste.gg)", "https://paste.gg", "/api/v1/pastes"}, - {"mclogs", "https://api.mclo.gs", "/1/log"}}}; + {{"0x0.st", "https://0x0.st", ""}, + {"hastebin", "https://hst.sh", "/documents"}, + {"paste.gg", "https://paste.gg", "/api/v1/pastes"}, + {"mclo.gs", "https://api.mclo.gs", "/1/log"}}}; PasteUpload::PasteUpload(QWidget *window, QString text, QString baseUrl, PasteType pasteType) : m_window(window), m_baseUrl(baseUrl), m_pasteType(pasteType), m_text(text.toUtf8()) { diff --git a/launcher/net/PasteUpload.h b/launcher/net/PasteUpload.h index e276234f..eb315c2b 100644 --- a/launcher/net/PasteUpload.h +++ b/launcher/net/PasteUpload.h @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* * PolyMC - Minecraft Launcher + * Copyright (C) 2022 Lenny McLennington * * 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 @@ -45,7 +46,7 @@ class PasteUpload : public Task { Q_OBJECT public: - enum PasteType : unsigned int { + enum PasteType : int { // 0x0.st NullPointer, // hastebin.com diff --git a/launcher/ui/GuiUtil.cpp b/launcher/ui/GuiUtil.cpp index 5e9d1eda..320f1502 100644 --- a/launcher/ui/GuiUtil.cpp +++ b/launcher/ui/GuiUtil.cpp @@ -1,3 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 Lenny McLennington + * + * 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 . + * + * 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. + */ + #include "GuiUtil.h" #include @@ -16,7 +51,7 @@ QString GuiUtil::uploadPaste(const QString &text, QWidget *parentWidget) { ProgressDialog dialog(parentWidget); - auto pasteTypeSetting = static_cast(APPLICATION->settings()->get("PastebinType").toUInt()); + auto pasteTypeSetting = static_cast(APPLICATION->settings()->get("PastebinType").toInt()); auto pasteCustomAPIBaseSetting = APPLICATION->settings()->get("PastebinCustomAPIBase").toString(); std::unique_ptr paste(new PasteUpload(parentWidget, text, pasteCustomAPIBaseSetting, pasteTypeSetting)); diff --git a/launcher/ui/pages/global/APIPage.cpp b/launcher/ui/pages/global/APIPage.cpp index b2827a19..2841544f 100644 --- a/launcher/ui/pages/global/APIPage.cpp +++ b/launcher/ui/pages/global/APIPage.cpp @@ -53,8 +53,8 @@ APIPage::APIPage(QWidget *parent) : QWidget(parent), ui(new Ui::APIPage) { - // this is here so you can reorder the entries in the combobox without messing stuff up - unsigned int comboBoxEntries[] = { + // This is here so you can reorder the entries in the combobox without messing stuff up + int comboBoxEntries[] = { PasteUpload::PasteType::Mclogs, PasteUpload::PasteType::NullPointer, PasteUpload::PasteType::PasteGG, @@ -69,13 +69,18 @@ APIPage::APIPage(QWidget *parent) : ui->pasteTypeComboBox->addItem(PasteUpload::PasteTypes.at(pasteType).name, pasteType); } - connect(ui->pasteTypeComboBox, static_cast(&QComboBox::currentIndexChanged), this, &APIPage::updateBaseURLPlaceholder); + void (QComboBox::*currentIndexChangedSignal)(int) (&QComboBox::currentIndexChanged); + connect(ui->pasteTypeComboBox, currentIndexChangedSignal, this, &APIPage::updateBaseURLPlaceholder); // This function needs to be called even when the ComboBox's index is still in its default state. updateBaseURLPlaceholder(ui->pasteTypeComboBox->currentIndex()); ui->baseURLEntry->setValidator(new QRegularExpressionValidator(validUrlRegExp, ui->baseURLEntry)); ui->tabWidget->tabBar()->hide(); loadSettings(); + + resetBaseURLNote(); + connect(ui->pasteTypeComboBox, currentIndexChangedSignal, this, &APIPage::updateBaseURLNote); + connect(ui->baseURLEntry, &QLineEdit::textEdited, this, &APIPage::resetBaseURLNote); } APIPage::~APIPage() @@ -83,16 +88,36 @@ APIPage::~APIPage() delete ui; } +void APIPage::resetBaseURLNote() +{ + ui->baseURLNote->hide(); + baseURLPasteType = ui->pasteTypeComboBox->currentIndex(); +} + +void APIPage::updateBaseURLNote(int index) +{ + if (baseURLPasteType == index) + { + ui->baseURLNote->hide(); + } + else if (!ui->baseURLEntry->text().isEmpty()) + { + ui->baseURLNote->show(); + } +} + void APIPage::updateBaseURLPlaceholder(int index) { - ui->baseURLEntry->setPlaceholderText(PasteUpload::PasteTypes.at(ui->pasteTypeComboBox->itemData(index).toUInt()).defaultBase); + int pasteType = ui->pasteTypeComboBox->itemData(index).toInt(); + QString pasteDefaultURL = PasteUpload::PasteTypes.at(pasteType).defaultBase; + ui->baseURLEntry->setPlaceholderText(pasteDefaultURL); } void APIPage::loadSettings() { auto s = APPLICATION->settings(); - unsigned int pasteType = s->get("PastebinType").toUInt(); + int pasteType = s->get("PastebinType").toInt(); QString pastebinURL = s->get("PastebinCustomAPIBase").toString(); ui->baseURLEntry->setText(pastebinURL); @@ -115,7 +140,7 @@ void APIPage::applySettings() { auto s = APPLICATION->settings(); - s->set("PastebinType", ui->pasteTypeComboBox->currentData().toUInt()); + s->set("PastebinType", ui->pasteTypeComboBox->currentData().toInt()); s->set("PastebinCustomAPIBase", ui->baseURLEntry->text()); QString msaClientID = ui->msaClientID->text(); diff --git a/launcher/ui/pages/global/APIPage.h b/launcher/ui/pages/global/APIPage.h index 0bb84c89..17e62ae7 100644 --- a/launcher/ui/pages/global/APIPage.h +++ b/launcher/ui/pages/global/APIPage.h @@ -3,6 +3,7 @@ * PolyMC - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (c) 2022 Jamie Mansfield + * Copyright (c) 2022 Lenny McLennington * * 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 @@ -73,6 +74,9 @@ public: void retranslate() override; private: + int baseURLPasteType; + void resetBaseURLNote(); + void updateBaseURLNote(int index); void updateBaseURLPlaceholder(int index); void loadSettings(); void applySettings(); diff --git a/launcher/ui/pages/global/APIPage.ui b/launcher/ui/pages/global/APIPage.ui index d986c2e2..b6af1958 100644 --- a/launcher/ui/pages/global/APIPage.ui +++ b/launcher/ui/pages/global/APIPage.ui @@ -61,6 +61,19 @@ + + true + + + + + + + Note: you probably want to change or clear the Base URL after changing the paste service type. + + + true + From e2ad3b01837e52a55e859412474978fa8a1e9625 Mon Sep 17 00:00:00 2001 From: Lenny McLennington Date: Tue, 17 May 2022 05:00:06 +0100 Subject: [PATCH 47/67] Add migration wizard, fix migration from custom paste instance - Very basic wizard just to allow the user to choose whether to keep their old paste settings or use the new default settings. - People who used custom 0x0 instances would just be kept on those settings and won't see the wizard. --- launcher/Application.cpp | 31 ++++---- launcher/CMakeLists.txt | 3 + launcher/ui/setupwizard/PasteWizardPage.cpp | 42 +++++++++++ launcher/ui/setupwizard/PasteWizardPage.h | 27 +++++++ launcher/ui/setupwizard/PasteWizardPage.ui | 80 +++++++++++++++++++++ 5 files changed, 169 insertions(+), 14 deletions(-) create mode 100644 launcher/ui/setupwizard/PasteWizardPage.cpp create mode 100644 launcher/ui/setupwizard/PasteWizardPage.h create mode 100644 launcher/ui/setupwizard/PasteWizardPage.ui diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 40c6e760..438c7d61 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -63,6 +63,7 @@ #include "ui/setupwizard/SetupWizard.h" #include "ui/setupwizard/LanguageWizardPage.h" #include "ui/setupwizard/JavaWizardPage.h" +#include "ui/setupwizard/PasteWizardPage.h" #include "ui/dialogs/CustomMessageBox.h" @@ -676,21 +677,17 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) // HACK: This code feels so stupid is there a less stupid way of doing this? { m_settings->registerSetting("PastebinURL", ""); + m_settings->registerSetting("PastebinType", PasteUpload::PasteType::Mclogs); + m_settings->registerSetting("PastebinCustomAPIBase", ""); + QString pastebinURL = m_settings->get("PastebinURL").toString(); - // If PastebinURL hasn't been set before then use the new default: mclo.gs - if (pastebinURL == "") { - m_settings->registerSetting("PastebinType", PasteUpload::PasteType::Mclogs); - m_settings->registerSetting("PastebinCustomAPIBase", ""); - } - // Otherwise: use 0x0.st - else { - // The default custom endpoint would usually be "" (meaning there is no custom endpoint specified) - // But if the user had customised the paste URL then that should be carried over into the custom endpoint. - QString defaultCustomEndpoint = (pastebinURL == "https://0x0.st") ? "" : pastebinURL; - m_settings->registerSetting("PastebinType", PasteUpload::PasteType::NullPointer); - m_settings->registerSetting("PastebinCustomAPIBase", defaultCustomEndpoint); - + bool userHadNoPastebin = pastebinURL == ""; + bool userHadDefaultPastebin = pastebinURL == "https://0x0.st"; + if (!(userHadNoPastebin || userHadDefaultPastebin)) + { + m_settings->set("PastebinType", PasteUpload::PasteType::NullPointer); + m_settings->set("PastebinCustomAPIBase", pastebinURL); m_settings->reset("PastebinURL"); } @@ -929,7 +926,8 @@ bool Application::createSetupWizard() return true; return false; }(); - bool wizardRequired = javaRequired || languageRequired; + bool pasteInterventionRequired = settings()->get("PastebinURL") != ""; + bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired; if(wizardRequired) { @@ -943,6 +941,11 @@ bool Application::createSetupWizard() { m_setupWizard->addPage(new JavaWizardPage(m_setupWizard)); } + + if (pasteInterventionRequired) + { + m_setupWizard->addPage(new PasteWizardPage(m_setupWizard)); + } connect(m_setupWizard, &QDialog::finished, this, &Application::setupWizardFinished); m_setupWizard->show(); return true; diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 8e75be20..15534c71 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -661,6 +661,8 @@ SET(LAUNCHER_SOURCES ui/setupwizard/JavaWizardPage.h ui/setupwizard/LanguageWizardPage.cpp ui/setupwizard/LanguageWizardPage.h + ui/setupwizard/PasteWizardPage.cpp + ui/setupwizard/PasteWizardPage.h # GUI - themes ui/themes/FusionTheme.cpp @@ -890,6 +892,7 @@ SET(LAUNCHER_SOURCES ) qt5_wrap_ui(LAUNCHER_UI + ui/setupwizard/PasteWizardPage.ui ui/pages/global/AccountListPage.ui ui/pages/global/JavaPage.ui ui/pages/global/LauncherPage.ui diff --git a/launcher/ui/setupwizard/PasteWizardPage.cpp b/launcher/ui/setupwizard/PasteWizardPage.cpp new file mode 100644 index 00000000..0f47da4b --- /dev/null +++ b/launcher/ui/setupwizard/PasteWizardPage.cpp @@ -0,0 +1,42 @@ +#include "PasteWizardPage.h" +#include "ui_PasteWizardPage.h" + +#include "Application.h" +#include "net/PasteUpload.h" + +PasteWizardPage::PasteWizardPage(QWidget *parent) : + BaseWizardPage(parent), + ui(new Ui::PasteWizardPage) +{ + ui->setupUi(this); +} + +PasteWizardPage::~PasteWizardPage() +{ + delete ui; +} + +void PasteWizardPage::initializePage() +{ +} + +bool PasteWizardPage::validatePage() +{ + auto s = APPLICATION->settings(); + QString prevPasteURL = s->get("PastebinURL").toString(); + s->reset("PastebinURL"); + if (ui->previousSettingsRadioButton->isChecked()) + { + bool usingDefaultBase = prevPasteURL == PasteUpload::PasteTypes.at(PasteUpload::PasteType::NullPointer).defaultBase; + s->set("PastebinType", PasteUpload::PasteType::NullPointer); + if (!usingDefaultBase) + s->set("PastebinCustomAPIBase", prevPasteURL); + } + + return true; +} + +void PasteWizardPage::retranslate() +{ + ui->retranslateUi(this); +} diff --git a/launcher/ui/setupwizard/PasteWizardPage.h b/launcher/ui/setupwizard/PasteWizardPage.h new file mode 100644 index 00000000..513a14cb --- /dev/null +++ b/launcher/ui/setupwizard/PasteWizardPage.h @@ -0,0 +1,27 @@ +#ifndef PASTEDEFAULTSCONFIRMATIONWIZARD_H +#define PASTEDEFAULTSCONFIRMATIONWIZARD_H + +#include +#include "BaseWizardPage.h" + +namespace Ui { +class PasteWizardPage; +} + +class PasteWizardPage : public BaseWizardPage +{ + Q_OBJECT + +public: + explicit PasteWizardPage(QWidget *parent = nullptr); + ~PasteWizardPage(); + + void initializePage() override; + bool validatePage() override; + void retranslate() override; + +private: + Ui::PasteWizardPage *ui; +}; + +#endif // PASTEDEFAULTSCONFIRMATIONWIZARD_H diff --git a/launcher/ui/setupwizard/PasteWizardPage.ui b/launcher/ui/setupwizard/PasteWizardPage.ui new file mode 100644 index 00000000..247d3a75 --- /dev/null +++ b/launcher/ui/setupwizard/PasteWizardPage.ui @@ -0,0 +1,80 @@ + + + PasteWizardPage + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + + The default paste service has changed to mclo.gs, please choose what you want to do with your settings. + + + true + + + + + + + Qt::Horizontal + + + + + + + Use new default service + + + true + + + buttonGroup + + + + + + + Keep previous settings + + + false + + + buttonGroup + + + + + + + Qt::Vertical + + + + 20 + 156 + + + + + + + + + + + + From de02deac989cc5efc135dc3c817fe72cc2499eca Mon Sep 17 00:00:00 2001 From: LennyMcLennington Date: Fri, 20 May 2022 22:30:00 +0100 Subject: [PATCH 48/67] Make if statement condition more readable Co-authored-by: Sefa Eyeoglu --- launcher/Application.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 438c7d61..91f5ef9d 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -682,9 +682,8 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) QString pastebinURL = m_settings->get("PastebinURL").toString(); - bool userHadNoPastebin = pastebinURL == ""; bool userHadDefaultPastebin = pastebinURL == "https://0x0.st"; - if (!(userHadNoPastebin || userHadDefaultPastebin)) + if (!pastebinURL.isEmpty() && !userHadDefaultPastebin) { m_settings->set("PastebinType", PasteUpload::PasteType::NullPointer); m_settings->set("PastebinCustomAPIBase", pastebinURL); From bfffcb3910b7f8429da16ae503fc79722d32ded6 Mon Sep 17 00:00:00 2001 From: txtsd Date: Sun, 22 May 2022 13:42:33 +0530 Subject: [PATCH 49/67] fix(workflow): Avoid invoking ccache on Release builds --- .github/workflows/build.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0590b348..38868b39 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,6 +39,7 @@ jobs: INSTALL_PORTABLE_DIR: "install-portable" INSTALL_APPIMAGE_DIR: "install-appdir" BUILD_DIR: "build" + CCACHE_VAR: "" steps: ## @@ -80,6 +81,12 @@ jobs: ccache -p # Show config ccache -z # Zero stats + - name: Use ccache on Debug builds only + if: inputs.build_type == 'Debug' + shell: bash + run: | + echo "CCACHE_VAR=ccache" >> $GITHUB_ENV + - name: Retrieve ccache cache (Windows) if: runner.os == 'Windows' && inputs.build_type == 'Debug' uses: actions/cache@v3.0.2 @@ -128,18 +135,18 @@ jobs: - name: Configure CMake (macOS) if: runner.os == 'macOS' run: | - cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DQt5_DIR=/usr/local/opt/qt@5 -DCMAKE_PREFIX_PATH=/usr/local/opt/qt@5 -DLauncher_BUILD_PLATFORM=macOS -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -G Ninja + cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DQt5_DIR=/usr/local/opt/qt@5 -DCMAKE_PREFIX_PATH=/usr/local/opt/qt@5 -DLauncher_BUILD_PLATFORM=macOS -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -G Ninja - name: Configure CMake (Windows) if: runner.os == 'Windows' shell: msys2 {0} run: | - cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -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 }} -G Ninja - 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=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -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 }} -G Ninja ## # BUILD From 90007e2d9d4f63cfc9dc73888af34a17657b5102 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 22 May 2022 16:03:21 +0200 Subject: [PATCH 50/67] fix: temporarily ignore stringop-overflow warning --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index e07d2aa6..e6d66b8d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,10 @@ set(CMAKE_CXX_FLAGS " -Wall -pedantic -Werror -Wno-deprecated-declarations -D_GL if(UNIX AND APPLE) set(CMAKE_CXX_FLAGS " -stdlib=libc++ ${CMAKE_CXX_FLAGS}") endif() +# FIXME: GCC 12 complains about some random stuff in QuaZip. Need to fix this later +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(CMAKE_CXX_FLAGS "-Wno-error=stringop-overflow ${CMAKE_CXX_FLAGS}") +endif() set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Werror=return-type") # Fix build with Qt 5.13 From c988b4d213b4125d298c893637a2362a7f192fce Mon Sep 17 00:00:00 2001 From: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Date: Sun, 22 May 2022 17:16:00 +0200 Subject: [PATCH 51/67] fix appimage not having imageformats fixes stuff like the iris icon --- .github/workflows/build.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5b70256a..6cbd5c21 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -113,12 +113,15 @@ jobs: if: runner.os == 'Linux' && matrix.appimage == true run: | sudo add-apt-repository ppa:savoury1/qt-5-15 + sudo add-apt-repository ppa:savoury1/kde-5-80 + sudo add-apt-repository ppa:savoury1/gpg + sudo add-apt-repository ppa:savoury1/ffmpeg4 - name: Install Qt (Linux) if: runner.os == 'Linux' run: | sudo apt-get -y update - sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5 ninja-build + sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5 ninja-build qt5-image-formats-plugins - name: Prepare AppImage (Linux) if: runner.os == 'Linux' && matrix.appimage == true From 0922a7f410d8675778bcf4720438efaa128b662b Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 22 May 2022 20:50:37 +0200 Subject: [PATCH 52/67] refactor: use -O2 for release and -O1 for debug builds --- CMakeLists.txt | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e6d66b8d..f54dd7ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,21 +34,24 @@ set(CMAKE_C_STANDARD_REQUIRED true) set(CMAKE_CXX_STANDARD 11) set(CMAKE_C_STANDARD 11) include(GenerateExportHeader) -set(CMAKE_CXX_FLAGS " -Wall -pedantic -Werror -Wno-deprecated-declarations -D_GLIBCXX_USE_CXX11_ABI=0 -fstack-protector-strong --param=ssp-buffer-size=4 -O3 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS}") +set(CMAKE_CXX_FLAGS "-Wall -pedantic -Werror -Wno-deprecated-declarations -D_GLIBCXX_USE_CXX11_ABI=0 -fstack-protector-strong --param=ssp-buffer-size=4 ${CMAKE_CXX_FLAGS}") if(UNIX AND APPLE) - set(CMAKE_CXX_FLAGS " -stdlib=libc++ ${CMAKE_CXX_FLAGS}") + set(CMAKE_CXX_FLAGS "-stdlib=libc++ ${CMAKE_CXX_FLAGS}") endif() -# FIXME: GCC 12 complains about some random stuff in QuaZip. Need to fix this later +# FIXME: GCC 12 complains about some random stuff in bundled QuaZip. Need to fix this later if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(CMAKE_CXX_FLAGS "-Wno-error=stringop-overflow ${CMAKE_CXX_FLAGS}") endif() -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Werror=return-type") # Fix build with Qt 5.13 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_NO_DEPRECATED_WARNINGS=Y") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_DISABLE_DEPRECATED_BEFORE=0x050C00") +# set CXXFLAGS for build targets +set(CMAKE_CXX_FLAGS_DEBUG "-O1 ${CMAKE_CXX_FLAGS}") +set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS}") + option(ENABLE_LTO "Enable Link Time Optimization" off) if(ENABLE_LTO) From 309dcc82cade6aee1af04534c8e307b56fcac848 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 22 May 2022 20:57:52 +0200 Subject: [PATCH 53/67] Revert "fix: temporarily ignore stringop-overflow warning" This reverts commit 90007e2d9d4f63cfc9dc73888af34a17657b5102. --- CMakeLists.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f54dd7ba..ef4adf90 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,10 +38,6 @@ set(CMAKE_CXX_FLAGS "-Wall -pedantic -Werror -Wno-deprecated-declarations -D_GLI if(UNIX AND APPLE) set(CMAKE_CXX_FLAGS "-stdlib=libc++ ${CMAKE_CXX_FLAGS}") endif() -# FIXME: GCC 12 complains about some random stuff in bundled QuaZip. Need to fix this later -if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - set(CMAKE_CXX_FLAGS "-Wno-error=stringop-overflow ${CMAKE_CXX_FLAGS}") -endif() # Fix build with Qt 5.13 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_NO_DEPRECATED_WARNINGS=Y") From f00dbdc215c2de3b6906d8182388c27bbc657e24 Mon Sep 17 00:00:00 2001 From: dada513 Date: Wed, 13 Apr 2022 23:00:32 +0200 Subject: [PATCH 54/67] Make Metaserver changable in settings Co-authored-by: Sefa Eyeoglu Co-authored-by: flow --- launcher/Application.cpp | 2 + launcher/meta/BaseEntity.cpp | 11 +++- launcher/ui/pages/global/APIPage.cpp | 10 ++++ launcher/ui/pages/global/APIPage.ui | 75 +++++++++++++++++++++++++--- 4 files changed, 89 insertions(+), 9 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 91f5ef9d..ba4096b6 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -699,6 +699,8 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_settings->reset("PastebinCustomAPIBase"); } } + // meta URL + m_settings->registerSetting("MetaURLOverride", ""); m_settings->registerSetting("CloseAfterLaunch", false); m_settings->registerSetting("QuitAfterGameStop", false); diff --git a/launcher/meta/BaseEntity.cpp b/launcher/meta/BaseEntity.cpp index 84155922..de4e1012 100644 --- a/launcher/meta/BaseEntity.cpp +++ b/launcher/meta/BaseEntity.cpp @@ -75,7 +75,16 @@ Meta::BaseEntity::~BaseEntity() QUrl Meta::BaseEntity::url() const { - return QUrl(BuildConfig.META_URL).resolved(localFilename()); + auto s = APPLICATION->settings(); + QString metaOverride = s->get("MetaURLOverride").toString(); + if(metaOverride.isEmpty()) + { + return QUrl(BuildConfig.META_URL).resolved(localFilename()); + } + else + { + return QUrl(metaOverride).resolved(localFilename()); + } } bool Meta::BaseEntity::loadLocalFile() diff --git a/launcher/ui/pages/global/APIPage.cpp b/launcher/ui/pages/global/APIPage.cpp index 2841544f..af58b8cd 100644 --- a/launcher/ui/pages/global/APIPage.cpp +++ b/launcher/ui/pages/global/APIPage.cpp @@ -132,6 +132,8 @@ void APIPage::loadSettings() QString msaClientID = s->get("MSAClientIDOverride").toString(); ui->msaClientID->setText(msaClientID); + QString metaURL = s->get("MetaURLOverride").toString(); + ui->metaURL->setText(metaURL); QString curseKey = s->get("CFKeyOverride").toString(); ui->curseKey->setText(curseKey); } @@ -145,6 +147,14 @@ void APIPage::applySettings() QString msaClientID = ui->msaClientID->text(); s->set("MSAClientIDOverride", msaClientID); + QUrl metaURL = ui->metaURL->text(); + // Don't allow HTTP, since meta is basically RCE with all the jar files. + if(!metaURL.isEmpty() && metaURL.scheme() == "http") + { + metaURL.setScheme("https"); + } + + s->set("MetaURLOverride", metaURL); QString curseKey = ui->curseKey->text(); s->set("CFKeyOverride", curseKey); } diff --git a/launcher/ui/pages/global/APIPage.ui b/launcher/ui/pages/global/APIPage.ui index b6af1958..8d80df65 100644 --- a/launcher/ui/pages/global/APIPage.ui +++ b/launcher/ui/pages/global/APIPage.ui @@ -6,8 +6,8 @@ 0 0 - 512 - 538 + 800 + 600 @@ -85,6 +85,13 @@ &Microsoft Authentication + + + + Qt::Horizontal + + + @@ -125,12 +132,9 @@ - - - true - + - &CurseForge Core API + Meta&data Server @@ -140,8 +144,63 @@ + + + + You can set this to a third-party metadata server to use patched libraries or other hacks. + + + Qt::RichText + + + true + + + + + + + (Default) + + + + + Enter a custom URL for meta here. + + + Qt::RichText + + + true + + + true + + + + + + + + + + true + + + &CurseForge Core API + + + + + + Qt::Horizontal + + + + + Note: you probably don't need to set this if CurseForge already works. @@ -158,7 +217,7 @@ - + Enter a custom API Key for CurseForge here. From b181f4bc30f36778f9680eb54e6f3514739161e8 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 22 May 2022 13:41:44 +0200 Subject: [PATCH 55/67] fix: improve spacing in APIPage --- launcher/ui/pages/global/APIPage.ui | 34 +++++++++++------------------ 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/launcher/ui/pages/global/APIPage.ui b/launcher/ui/pages/global/APIPage.ui index 8d80df65..24189c5c 100644 --- a/launcher/ui/pages/global/APIPage.ui +++ b/launcher/ui/pages/global/APIPage.ui @@ -85,13 +85,6 @@ &Microsoft Authentication - - - - Qt::Horizontal - - - @@ -137,13 +130,6 @@ Meta&data Server - - - - Qt::Horizontal - - - @@ -192,13 +178,6 @@ &CurseForge Core API - - - - Qt::Horizontal - - - @@ -235,6 +214,19 @@ + + + + Qt::Vertical + + + + 20 + 40 + + + + From f2e205313485e458e2f5186f743d527d28609c5e Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 22 May 2022 13:55:19 +0200 Subject: [PATCH 56/67] feat: add trailing slash to meta URL if it is missing --- launcher/ui/pages/global/APIPage.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/launcher/ui/pages/global/APIPage.cpp b/launcher/ui/pages/global/APIPage.cpp index af58b8cd..6ad243dd 100644 --- a/launcher/ui/pages/global/APIPage.cpp +++ b/launcher/ui/pages/global/APIPage.cpp @@ -148,6 +148,13 @@ void APIPage::applySettings() QString msaClientID = ui->msaClientID->text(); s->set("MSAClientIDOverride", msaClientID); QUrl metaURL = ui->metaURL->text(); + // Add required trailing slash + if (!metaURL.isEmpty() && !metaURL.path().endsWith('/')) + { + QString path = metaURL.path(); + path.append('/'); + metaURL.setPath(path); + } // Don't allow HTTP, since meta is basically RCE with all the jar files. if(!metaURL.isEmpty() && metaURL.scheme() == "http") { From 0b85051a2363f4fad29477e3a0ccd3fda18fee01 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 22 May 2022 21:41:41 +0200 Subject: [PATCH 57/67] fix: more generous optimizations for debug builds --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ef4adf90..a8c28e99 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,7 +45,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_NO_DEPRECATED_WARNINGS=Y") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_DISABLE_DEPRECATED_BEFORE=0x050C00") # set CXXFLAGS for build targets -set(CMAKE_CXX_FLAGS_DEBUG "-O1 ${CMAKE_CXX_FLAGS}") +set(CMAKE_CXX_FLAGS_DEBUG "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS}") set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS}") option(ENABLE_LTO "Enable Link Time Optimization" off) From cb69869836d2b4ed4b50a43694e95c4a801332f7 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 22 May 2022 22:04:24 +0200 Subject: [PATCH 58/67] revert: remove CurseForge workaround We have been asked by CurseForge to remove this workaround as it violates their terms of service. This is just a partial revert, as the UI changes were otherwise unrelated. This reverts commit 92e8aaf36f72b7527322add169b253d0698939d0, reversing changes made to 88a93945d4c9a11bf53016133335d359b819585e. --- launcher/modplatform/flame/FileResolvingTask.cpp | 16 +--------------- launcher/modplatform/flame/FlameModIndex.cpp | 9 +-------- launcher/modplatform/flame/PackManifest.cpp | 15 +++++---------- 3 files changed, 7 insertions(+), 33 deletions(-) diff --git a/launcher/modplatform/flame/FileResolvingTask.cpp b/launcher/modplatform/flame/FileResolvingTask.cpp index 0deb99c4..95924a68 100644 --- a/launcher/modplatform/flame/FileResolvingTask.cpp +++ b/launcher/modplatform/flame/FileResolvingTask.cpp @@ -31,21 +31,7 @@ void Flame::FileResolvingTask::netJobFinished() for (auto& bytes : results) { auto& out = m_toProcess.files[index]; try { - bool fail = (!out.parseFromBytes(bytes)); - if(fail){ - //failed :( probably disabled mod, try to add to the list - auto doc = Json::requireDocument(bytes); - if (!doc.isObject()) { - throw JSONValidationError(QString("data is not an object? that's not supposed to happen")); - } - auto obj = Json::ensureObject(doc.object(), "data"); - //FIXME : HACK, MAY NOT WORK FOR LONG - out.url = QUrl(QString("https://media.forgecdn.net/files/%1/%2/%3") - .arg(QString::number(QString::number(out.fileId).leftRef(4).toInt()) - ,QString::number(QString::number(out.fileId).rightRef(3).toInt()) - ,QUrl::toPercentEncoding(out.fileName)), QUrl::TolerantMode); - } - failed &= fail; + failed &= (!out.parseFromBytes(bytes)); } catch (const JSONValidationError& e) { qCritical() << "Resolving of" << out.projectId << out.fileId << "failed because of a parsing error:"; qCritical() << e.cause(); diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index 9846b156..ba0824cf 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -56,15 +56,8 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, file.fileId = Json::requireInteger(obj, "id"); file.date = Json::requireString(obj, "fileDate"); file.version = Json::requireString(obj, "displayName"); + file.downloadUrl = Json::requireString(obj, "downloadUrl"); file.fileName = Json::requireString(obj, "fileName"); - file.downloadUrl = Json::ensureString(obj, "downloadUrl", ""); - if(file.downloadUrl.isEmpty()){ - //FIXME : HACK, MAY NOT WORK FOR LONG - file.downloadUrl = QString("https://media.forgecdn.net/files/%1/%2/%3") - .arg(QString::number(QString::number(file.fileId.toInt()).leftRef(4).toInt()) - ,QString::number(QString::number(file.fileId.toInt()).rightRef(3).toInt()) - ,QUrl::toPercentEncoding(file.fileName)); - } unsortedVersions.append(file); } diff --git a/launcher/modplatform/flame/PackManifest.cpp b/launcher/modplatform/flame/PackManifest.cpp index 3217a756..e4f90c1a 100644 --- a/launcher/modplatform/flame/PackManifest.cpp +++ b/launcher/modplatform/flame/PackManifest.cpp @@ -71,6 +71,11 @@ bool Flame::File::parseFromBytes(const QByteArray& bytes) fileName = Json::requireString(obj, "fileName"); + QString rawUrl = Json::requireString(obj, "downloadUrl"); + url = QUrl(rawUrl, QUrl::TolerantMode); + if (!url.isValid()) { + throw JSONValidationError(QString("Invalid URL: %1").arg(rawUrl)); + } // This is a piece of a Flame project JSON pulled out into the file metadata (here) for convenience // It is also optional type = File::Type::SingleFile; @@ -82,17 +87,7 @@ bool Flame::File::parseFromBytes(const QByteArray& bytes) // this is probably a mod, dunno what else could modpacks download targetFolder = "mods"; } - QString rawUrl = Json::ensureString(obj, "downloadUrl"); - if(rawUrl.isEmpty()){ - //either there somehow is an emtpy string as a link, or it's null either way it's invalid - //soft failing - return false; - } - url = QUrl(rawUrl, QUrl::TolerantMode); - if (!url.isValid()) { - throw JSONValidationError(QString("Invalid URL: %1").arg(rawUrl)); - } resolved = true; return true; } From d72c75db239dbff7e41c0d4a20df5337b9685a16 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 22 May 2022 22:56:52 +0200 Subject: [PATCH 59/67] chore: bump version --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a8c28e99..e2635c3f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,8 +73,8 @@ set(Launcher_HELP_URL "https://polymc.org/wiki/help-pages/%1" CACHE STRING "URL ######## Set version numbers ######## set(Launcher_VERSION_MAJOR 1) -set(Launcher_VERSION_MINOR 2) -set(Launcher_VERSION_HOTFIX 2) +set(Launcher_VERSION_MINOR 3) +set(Launcher_VERSION_HOTFIX 0) # Build number set(Launcher_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.") From f28a0aa666565354e657dec59249aa1fd237cdb0 Mon Sep 17 00:00:00 2001 From: Jamie Mansfield Date: Mon, 23 May 2022 19:42:04 +0100 Subject: [PATCH 60/67] ATLauncher: Handle main class depends --- .../atlauncher/ATLPackInstallTask.cpp | 20 ++++++++++++++++--- .../atlauncher/ATLPackManifest.cpp | 8 +++++++- .../modplatform/atlauncher/ATLPackManifest.h | 8 +++++++- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp index 9b14f355..e6fd1334 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp @@ -414,10 +414,24 @@ bool PackInstallTask::createLibrariesComponent(QString instanceRoot, std::shared bool PackInstallTask::createPackComponent(QString instanceRoot, std::shared_ptr profile) { - if(m_version.mainClass == QString() && m_version.extraArguments == QString()) { + if (m_version.mainClass.mainClass.isEmpty() && m_version.extraArguments.isEmpty()) { return true; } + auto mainClass = m_version.mainClass.mainClass; + + auto hasMainClassDepends = !m_version.mainClass.depends.isEmpty(); + if (hasMainClassDepends) { + QSet mods; + for (const auto& item : m_version.mods) { + mods.insert(item.name); + } + + if (hasMainClassDepends && !mods.contains(m_version.mainClass.depends)) { + mainClass = ""; + } + } + auto uuid = QUuid::createUuid(); auto id = uuid.toString().remove('{').remove('}'); auto target_id = "org.multimc.atlauncher." + id; @@ -442,8 +456,8 @@ bool PackInstallTask::createPackComponent(QString instanceRoot, std::shared_ptr< auto f = std::make_shared(); f->name = m_pack + " " + m_version_name; - if(m_version.mainClass != QString() && !mainClasses.contains(m_version.mainClass)) { - f->mainClass = m_version.mainClass; + if (!mainClass.isEmpty() && !mainClasses.contains(mainClass)) { + f->mainClass = mainClass; } // Parse out tweakers diff --git a/launcher/modplatform/atlauncher/ATLPackManifest.cpp b/launcher/modplatform/atlauncher/ATLPackManifest.cpp index d01ec32c..cec9896b 100644 --- a/launcher/modplatform/atlauncher/ATLPackManifest.cpp +++ b/launcher/modplatform/atlauncher/ATLPackManifest.cpp @@ -212,6 +212,12 @@ static void loadVersionMessages(ATLauncher::VersionMessages& m, QJsonObject& obj m.update = Json::ensureString(obj, "update", ""); } +static void loadVersionMainClass(ATLauncher::PackVersionMainClass& m, QJsonObject& obj) +{ + m.mainClass = Json::ensureString(obj, "mainClass", ""); + m.depends = Json::ensureString(obj, "depends", ""); +} + void ATLauncher::loadVersion(PackVersion & v, QJsonObject & obj) { v.version = Json::requireString(obj, "version"); @@ -220,7 +226,7 @@ void ATLauncher::loadVersion(PackVersion & v, QJsonObject & obj) if(obj.contains("mainClass")) { auto main = Json::requireObject(obj, "mainClass"); - v.mainClass = Json::ensureString(main, "mainClass", ""); + loadVersionMainClass(v.mainClass, main); } if(obj.contains("extraArguments")) { diff --git a/launcher/modplatform/atlauncher/ATLPackManifest.h b/launcher/modplatform/atlauncher/ATLPackManifest.h index 23e162e3..bf88d91d 100644 --- a/launcher/modplatform/atlauncher/ATLPackManifest.h +++ b/launcher/modplatform/atlauncher/ATLPackManifest.h @@ -150,12 +150,18 @@ struct VersionMessages QString update; }; +struct PackVersionMainClass +{ + QString mainClass; + QString depends; +}; + struct PackVersion { QString version; QString minecraft; bool noConfigs; - QString mainClass; + PackVersionMainClass mainClass; QString extraArguments; VersionLoader loader; From 101ca60b2bb1d3c3047bc5842461c68d05708e39 Mon Sep 17 00:00:00 2001 From: Jamie Mansfield Date: Mon, 23 May 2022 20:14:23 +0100 Subject: [PATCH 61/67] ATLauncher: Handle extra arguments depends --- .../atlauncher/ATLPackInstallTask.cpp | 16 +++++++++++++--- .../modplatform/atlauncher/ATLPackManifest.cpp | 8 +++++++- .../modplatform/atlauncher/ATLPackManifest.h | 8 +++++++- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp index e6fd1334..b2dda4e4 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp @@ -414,14 +414,16 @@ bool PackInstallTask::createLibrariesComponent(QString instanceRoot, std::shared bool PackInstallTask::createPackComponent(QString instanceRoot, std::shared_ptr profile) { - if (m_version.mainClass.mainClass.isEmpty() && m_version.extraArguments.isEmpty()) { + if (m_version.mainClass.mainClass.isEmpty() && m_version.extraArguments.arguments.isEmpty()) { return true; } auto mainClass = m_version.mainClass.mainClass; + auto extraArguments = m_version.extraArguments.arguments; auto hasMainClassDepends = !m_version.mainClass.depends.isEmpty(); - if (hasMainClassDepends) { + auto hasExtraArgumentsDepends = !m_version.extraArguments.depends.isEmpty(); + if (hasMainClassDepends || hasExtraArgumentsDepends) { QSet mods; for (const auto& item : m_version.mods) { mods.insert(item.name); @@ -430,6 +432,14 @@ bool PackInstallTask::createPackComponent(QString instanceRoot, std::shared_ptr< if (hasMainClassDepends && !mods.contains(m_version.mainClass.depends)) { mainClass = ""; } + + if (hasExtraArgumentsDepends && !mods.contains(m_version.extraArguments.depends)) { + extraArguments = ""; + } + } + + if (mainClass.isEmpty() && extraArguments.isEmpty()) { + return true; } auto uuid = QUuid::createUuid(); @@ -461,7 +471,7 @@ bool PackInstallTask::createPackComponent(QString instanceRoot, std::shared_ptr< } // Parse out tweakers - auto args = m_version.extraArguments.split(" "); + auto args = extraArguments.split(" "); QString previous; for(auto arg : args) { if(arg.startsWith("--tweakClass=") || previous == "--tweakClass") { diff --git a/launcher/modplatform/atlauncher/ATLPackManifest.cpp b/launcher/modplatform/atlauncher/ATLPackManifest.cpp index cec9896b..3af02a09 100644 --- a/launcher/modplatform/atlauncher/ATLPackManifest.cpp +++ b/launcher/modplatform/atlauncher/ATLPackManifest.cpp @@ -218,6 +218,12 @@ static void loadVersionMainClass(ATLauncher::PackVersionMainClass& m, QJsonObjec m.depends = Json::ensureString(obj, "depends", ""); } +static void loadVersionExtraArguments(ATLauncher::PackVersionExtraArguments& a, QJsonObject& obj) +{ + a.arguments = Json::ensureString(obj, "arguments", ""); + a.depends = Json::ensureString(obj, "depends", ""); +} + void ATLauncher::loadVersion(PackVersion & v, QJsonObject & obj) { v.version = Json::requireString(obj, "version"); @@ -231,7 +237,7 @@ void ATLauncher::loadVersion(PackVersion & v, QJsonObject & obj) if(obj.contains("extraArguments")) { auto arguments = Json::requireObject(obj, "extraArguments"); - v.extraArguments = Json::ensureString(arguments, "arguments", ""); + loadVersionExtraArguments(v.extraArguments, arguments); } if(obj.contains("loader")) { diff --git a/launcher/modplatform/atlauncher/ATLPackManifest.h b/launcher/modplatform/atlauncher/ATLPackManifest.h index bf88d91d..43510c50 100644 --- a/launcher/modplatform/atlauncher/ATLPackManifest.h +++ b/launcher/modplatform/atlauncher/ATLPackManifest.h @@ -156,13 +156,19 @@ struct PackVersionMainClass QString depends; }; +struct PackVersionExtraArguments +{ + QString arguments; + QString depends; +}; + struct PackVersion { QString version; QString minecraft; bool noConfigs; PackVersionMainClass mainClass; - QString extraArguments; + PackVersionExtraArguments extraArguments; VersionLoader loader; QVector libraries; From 4ee5264e24e21d89185d424072dc39cb6b2dd10f Mon Sep 17 00:00:00 2001 From: Jamie Mansfield Date: Mon, 23 May 2022 21:37:09 +0100 Subject: [PATCH 62/67] ATLauncher: Delete files from configs if they conflict with a mod --- .../modplatform/atlauncher/ATLPackInstallTask.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp index b2dda4e4..62c7bf6d 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp @@ -781,6 +781,17 @@ bool PackInstallTask::extractMods( for (auto iter = toCopy.begin(); iter != toCopy.end(); iter++) { auto &from = iter.key(); auto &to = iter.value(); + + // If the file already exists, assume the mod is the correct copy - and remove + // the copy from the Configs.zip + QFileInfo fileInfo(to); + if (fileInfo.exists()) { + if (!QFile::remove(to)) { + qWarning() << "Failed to delete" << to; + return false; + } + } + FS::copy fileCopyOperation(from, to); if(!fileCopyOperation()) { qWarning() << "Failed to copy" << from << "to" << to; From fce5c575480c88f81f325f3759889d0cde9a28e0 Mon Sep 17 00:00:00 2001 From: Kenneth Chew Date: Mon, 23 May 2022 17:27:35 -0400 Subject: [PATCH 63/67] Silence CMake QuaZip not found warnings These are expected most of the time, and thus just noise. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e2635c3f..fcc2512d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -143,7 +143,7 @@ if(Launcher_QT_VERSION_MAJOR EQUAL 5) find_package(Qt5 REQUIRED COMPONENTS Core Widgets Concurrent Network Test Xml) if(NOT Launcher_FORCE_BUNDLED_LIBS) - find_package(QuaZip-Qt5 1.3) + find_package(QuaZip-Qt5 1.3 QUIET) endif() if (NOT QuaZip-Qt5_FOUND) set(QUAZIP_QT_MAJOR_VERSION ${QT_VERSION_MAJOR} CACHE STRING "Qt version to use (4, 5 or 6), defaults to ${QT_VERSION_MAJOR}" FORCE) From 67c5aa0be9c20e67bb96e917cb881a9d953b9ce4 Mon Sep 17 00:00:00 2001 From: byquanton <32410361+byquanton@users.noreply.github.com> Date: Tue, 24 May 2022 17:44:23 +0200 Subject: [PATCH 64/67] Update org.polymc.PolyMC.metainfo.xml.in Should fix Flatpak/Flathub build --- program_info/org.polymc.PolyMC.metainfo.xml.in | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/program_info/org.polymc.PolyMC.metainfo.xml.in b/program_info/org.polymc.PolyMC.metainfo.xml.in index ff4af1c3..ea665655 100644 --- a/program_info/org.polymc.PolyMC.metainfo.xml.in +++ b/program_info/org.polymc.PolyMC.metainfo.xml.in @@ -28,23 +28,23 @@ The main PolyMC window - https://polymc.org/img/screenshots/LauncherDark.png + https://polymc.org/img/screenshots/LauncherDark.png Modpack installation - https://polymc.org/img/screenshots/ModpackInstallDark.png + https://polymc.org/img/screenshots/ModpackInstallDark.png Mod installation - https://polymc.org/img/screenshots/ModInstallDark.png + https://polymc.org/img/screenshots/ModInstallDark.png Instance management - https://polymc.org/img/screenshots/PropertiesDark.png + https://polymc.org/img/screenshots/PropertiesDark.png Cat :) - https://polymc.org/img/screenshots/LauncherCatDark.png + https://polymc.org/img/screenshots/LauncherCatDark.png From 8a1ba03bcb6d43f9aae0555125447b4d5cd2dc1a Mon Sep 17 00:00:00 2001 From: Ryan Cao <70191398+ryanccn@users.noreply.github.com> Date: Wed, 25 May 2022 11:46:15 +0800 Subject: [PATCH 65/67] show default metaserver --- launcher/ui/pages/global/APIPage.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/launcher/ui/pages/global/APIPage.cpp b/launcher/ui/pages/global/APIPage.cpp index 6ad243dd..5d812d07 100644 --- a/launcher/ui/pages/global/APIPage.cpp +++ b/launcher/ui/pages/global/APIPage.cpp @@ -48,6 +48,7 @@ #include "tools/BaseProfiler.h" #include "Application.h" #include "net/PasteUpload.h" +#include "BuildConfig.h" APIPage::APIPage(QWidget *parent) : QWidget(parent), @@ -76,6 +77,8 @@ APIPage::APIPage(QWidget *parent) : ui->baseURLEntry->setValidator(new QRegularExpressionValidator(validUrlRegExp, ui->baseURLEntry)); ui->tabWidget->tabBar()->hide(); + ui->metaURL->setPlaceholderText(BuildConfig.META_URL); + loadSettings(); resetBaseURLNote(); From e50ec31351fb226028fef5d746ecc59c9d51fecb Mon Sep 17 00:00:00 2001 From: Ryan Cao <70191398+ryanccn@users.noreply.github.com> Date: Wed, 25 May 2022 14:44:47 +0800 Subject: [PATCH 66/67] fix --- launcher/ui/pages/global/APIPage.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/pages/global/APIPage.ui b/launcher/ui/pages/global/APIPage.ui index 24189c5c..cf15065b 100644 --- a/launcher/ui/pages/global/APIPage.ui +++ b/launcher/ui/pages/global/APIPage.ui @@ -146,7 +146,7 @@ - (Default) + From 938cae1130464fcedaa776d9f54898dece14f9a7 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Wed, 25 May 2022 23:04:49 +0200 Subject: [PATCH 67/67] revert: remove CurseForge workaround for packs too Partial revert. Handles missing download URLs. --- launcher/modplatform/flame/FlamePackIndex.cpp | 12 ++++-------- launcher/modplatform/flame/FlamePackIndex.h | 1 - 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/launcher/modplatform/flame/FlamePackIndex.cpp b/launcher/modplatform/flame/FlamePackIndex.cpp index 6d48a3bf..bece7843 100644 --- a/launcher/modplatform/flame/FlamePackIndex.cpp +++ b/launcher/modplatform/flame/FlamePackIndex.cpp @@ -65,16 +65,12 @@ 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.fileName = Json::requireString(version, "fileName"); file.downloadUrl = Json::ensureString(version, "downloadUrl"); - if(file.downloadUrl.isEmpty()){ - //FIXME : HACK, MAY NOT WORK FOR LONG - file.downloadUrl = QString("https://media.forgecdn.net/files/%1/%2/%3") - .arg(QString::number(QString::number(file.fileId).leftRef(4).toInt()) - ,QString::number(QString::number(file.fileId).rightRef(3).toInt()) - ,QUrl::toPercentEncoding(file.fileName)); + + // only add if we have a download URL (third party distribution is enabled) + if (!file.downloadUrl.isEmpty()) { + unsortedVersions.append(file); } - unsortedVersions.append(file); } auto orderSortPredicate = [](const IndexedVersion& a, const IndexedVersion& b) -> bool { return a.fileId > b.fileId; }; diff --git a/launcher/modplatform/flame/FlamePackIndex.h b/launcher/modplatform/flame/FlamePackIndex.h index a8bb15be..7ffa29c3 100644 --- a/launcher/modplatform/flame/FlamePackIndex.h +++ b/launcher/modplatform/flame/FlamePackIndex.h @@ -18,7 +18,6 @@ struct IndexedVersion { QString version; QString mcVersion; QString downloadUrl; - QString fileName; }; struct IndexedPack