From 3757c3bae68e951eecb702eee2845e4ce6b5ed6f Mon Sep 17 00:00:00 2001 From: Max Weber Date: Wed, 7 Aug 2019 02:40:39 -0600 Subject: [PATCH] runelite-client: Add loading splash screen --- .../java/net/runelite/client/RuneLite.java | 79 +++--- .../client/plugins/PluginManager.java | 12 +- .../net/runelite/client/rs/ClientLoader.java | 30 ++- .../java/net/runelite/client/ui/ClientUI.java | 15 +- .../net/runelite/client/ui/SplashScreen.java | 232 ++++++++++++++++++ .../client/ui/runelite_transparent.png | Bin 0 -> 31065 bytes 6 files changed, 331 insertions(+), 37 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/ui/SplashScreen.java create mode 100644 runelite-client/src/main/resources/net/runelite/client/ui/runelite_transparent.png diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLite.java b/runelite-client/src/main/java/net/runelite/client/RuneLite.java index e309734264..e8b4d60a43 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLite.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLite.java @@ -60,6 +60,7 @@ import net.runelite.client.rs.ClientLoader; import net.runelite.client.rs.ClientUpdateCheckMode; import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.DrawManager; +import net.runelite.client.ui.SplashScreen; import net.runelite.client.ui.overlay.OverlayManager; import net.runelite.client.ui.overlay.OverlayRenderer; import net.runelite.client.ui.overlay.WidgetOverlay; @@ -197,40 +198,50 @@ public class RuneLite } }); - final ClientLoader clientLoader = new ClientLoader(options.valueOf(updateMode)); + SplashScreen.init(); + SplashScreen.stage(0, "Retrieving client", ""); - new Thread(() -> + try { - clientLoader.get(); - ClassPreloader.preload(); - }, "Preloader").start(); + final ClientLoader clientLoader = new ClientLoader(options.valueOf(updateMode)); - final boolean developerMode = options.has("developer-mode") && RuneLiteProperties.getLauncherVersion() == null; - - if (developerMode) - { - boolean assertions = false; - assert assertions = true; - if (!assertions) + new Thread(() -> { - throw new RuntimeException("Developers should enable assertions; Add `-ea` to your JVM arguments`"); + clientLoader.get(); + ClassPreloader.preload(); + }, "Preloader").start(); + + final boolean developerMode = options.has("developer-mode") && RuneLiteProperties.getLauncherVersion() == null; + + if (developerMode) + { + boolean assertions = false; + assert assertions = true; + if (!assertions) + { + throw new RuntimeException("Developers should enable assertions; Add `-ea` to your JVM arguments`"); + } } + + PROFILES_DIR.mkdirs(); + + final long start = System.currentTimeMillis(); + + injector = Guice.createInjector(new RuneLiteModule( + clientLoader, + developerMode)); + + injector.getInstance(RuneLite.class).start(); + + final long end = System.currentTimeMillis(); + final RuntimeMXBean rb = ManagementFactory.getRuntimeMXBean(); + final long uptime = rb.getUptime(); + log.info("Client initialization took {}ms. Uptime: {}ms", end - start, uptime); + } + finally + { + SplashScreen.stop(); } - - PROFILES_DIR.mkdirs(); - - final long start = System.currentTimeMillis(); - - injector = Guice.createInjector(new RuneLiteModule( - clientLoader, - developerMode)); - - injector.getInstance(RuneLite.class).start(); - - final long end = System.currentTimeMillis(); - final RuntimeMXBean rb = ManagementFactory.getRuntimeMXBean(); - final long uptime = rb.getUptime(); - log.info("Client initialization took {}ms. Uptime: {}ms", end - start, uptime); } public void start() throws Exception @@ -244,6 +255,8 @@ public class RuneLite injector.injectMembers(client); } + SplashScreen.stage(.57, null, "Loading configuration"); + // Load user configuration configManager.load(); @@ -257,6 +270,8 @@ public class RuneLite // This will initialize configuration pluginManager.loadCorePlugins(); + SplashScreen.stage(.70, null, "Finalizing configuration"); + // Plugins have provided their config, so set default config // to main settings pluginManager.loadDefaultPluginConfiguration(); @@ -264,8 +279,10 @@ public class RuneLite // Start client session clientSessionManager.start(); + SplashScreen.stage(.75, null, "Starting core interface"); + // Initialize UI - clientUI.open(this); + clientUI.init(this); // Initialize Discord service discordService.init(); @@ -301,6 +318,10 @@ public class RuneLite // Start plugins pluginManager.startCorePlugins(); + + SplashScreen.stop(); + + clientUI.show(); } public void shutdown() diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java index 73d6bf32e4..b820f2d6b3 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java @@ -70,6 +70,7 @@ import net.runelite.client.events.PluginChanged; import net.runelite.client.task.Schedule; import net.runelite.client.task.ScheduledMethod; import net.runelite.client.task.Scheduler; +import net.runelite.client.ui.SplashScreen; import net.runelite.client.util.GameEventManager; @Singleton @@ -200,6 +201,7 @@ public class PluginManager public void startCorePlugins() { List scannedPlugins = new ArrayList<>(plugins); + int loaded = 0; for (Plugin plugin : scannedPlugins) { try @@ -211,11 +213,15 @@ public class PluginManager log.warn("Unable to start plugin {}. {}", plugin.getClass().getSimpleName(), ex); plugins.remove(plugin); } + + loaded++; + SplashScreen.stage(.80, 1, null, "Starting plugins", loaded, scannedPlugins.size(), false); } } List scanAndInstantiate(ClassLoader classLoader, String packageName) throws IOException { + SplashScreen.stage(.59, null, "Loading Plugins"); MutableGraph> graph = GraphBuilder .directed() .build(); @@ -280,20 +286,22 @@ public class PluginManager List> sortedPlugins = topologicalSort(graph); sortedPlugins = Lists.reverse(sortedPlugins); + int loaded = 0; for (Class pluginClazz : sortedPlugins) { Plugin plugin; try { plugin = instantiate(scannedPlugins, (Class) pluginClazz); + scannedPlugins.add(plugin); } catch (PluginInstantiationException ex) { log.warn("Error instantiating plugin!", ex); - continue; } - scannedPlugins.add(plugin); + loaded++; + SplashScreen.stage(.60, .70, null, "Loading Plugins", loaded, sortedPlugins.size(), false); } return scannedPlugins; diff --git a/runelite-client/src/main/java/net/runelite/client/rs/ClientLoader.java b/runelite-client/src/main/java/net/runelite/client/rs/ClientLoader.java index e5adedb2f9..f666703a66 100644 --- a/runelite-client/src/main/java/net/runelite/client/rs/ClientLoader.java +++ b/runelite-client/src/main/java/net/runelite/client/rs/ClientLoader.java @@ -34,6 +34,7 @@ import io.sigpipe.jbsdiff.InvalidHeaderException; import io.sigpipe.jbsdiff.Patch; import java.applet.Applet; import java.io.ByteArrayOutputStream; +import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -53,6 +54,7 @@ import net.runelite.api.Client; import static net.runelite.client.rs.ClientUpdateCheckMode.AUTO; import static net.runelite.client.rs.ClientUpdateCheckMode.NONE; import static net.runelite.client.rs.ClientUpdateCheckMode.VANILLA; +import net.runelite.client.ui.SplashScreen; import net.runelite.http.api.RuneLiteAPI; import okhttp3.Request; import okhttp3.Response; @@ -88,6 +90,7 @@ public class ClientLoader implements Supplier try { + SplashScreen.stage(0, null, "Fetching applet viewer config"); RSConfig config = ClientConfigLoader.fetch(); Map zipFile = new HashMap<>(); @@ -102,7 +105,26 @@ public class ClientLoader implements Supplier try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute()) { - JarInputStream jis = new JarInputStream(response.body().byteStream()); + int length = (int) response.body().contentLength(); + if (length < 0) + { + length = 3 * 1024 * 1024; + } + final int flength = length; + InputStream istream = new FilterInputStream(response.body().byteStream()) + { + private int read = 0; + + @Override + public int read(byte[] b, int off, int len) throws IOException + { + int thisRead = super.read(b, off, len); + this.read += thisRead; + SplashScreen.stage(.05, .35, null, "Downloading Old School RuneScape", this.read, flength, true); + return thisRead; + } + }; + JarInputStream jis = new JarInputStream(istream); byte[] tmp = new byte[4096]; ByteArrayOutputStream buffer = new ByteArrayOutputStream(756 * 1024); @@ -146,6 +168,7 @@ public class ClientLoader implements Supplier if (updateCheckMode == AUTO) { + SplashScreen.stage(.35, null, "Patching"); Map hashes; try (InputStream is = ClientLoader.class.getResourceAsStream("/patch/hashes.json")) { @@ -197,11 +220,14 @@ public class ClientLoader implements Supplier file.setValue(patchOs.toByteArray()); ++patchCount; + SplashScreen.stage(.38, .45, null, "Patching", patchCount, zipFile.size(), false); } log.debug("Patched {} classes", patchCount); } + SplashScreen.stage(.465, "Starting", "Starting Old School RuneScape"); + String initialClass = config.getInitialClass(); ClassLoader rsClassLoader = new ClassLoader(ClientLoader.class.getClassLoader()) @@ -230,6 +256,8 @@ public class ClientLoader implements Supplier log.info("client-patch {}", ((Client) rs).getBuildID()); } + SplashScreen.stage(.5, null, "Starting core classes"); + return rs; } catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException diff --git a/runelite-client/src/main/java/net/runelite/client/ui/ClientUI.java b/runelite-client/src/main/java/net/runelite/client/ui/ClientUI.java index 5a175716d4..a61b1c656f 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/ClientUI.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/ClientUI.java @@ -261,7 +261,7 @@ public class ClientUI return; } - final Client client = (Client)this.client; + final Client client = (Client) this.client; final ClientThread clientThread = clientThreadProvider.get(); // Keep scheduling event until we get our name @@ -293,11 +293,10 @@ public class ClientUI /** * Initialize UI. - * * @param runelite runelite instance that will be shut down on exit * @throws Exception exception that can occur during creation of the UI */ - public void open(final RuneLite runelite) throws Exception + public void init(final RuneLite runelite) throws Exception { SwingUtilities.invokeAndWait(() -> { @@ -453,7 +452,13 @@ public class ClientUI titleToolbar.addComponent(sidebarNavigationButton, sidebarNavigationJButton); toggleSidebar(); + }); + } + public void show() + { + SwingUtilities.invokeLater(() -> + { // Layout frame frame.pack(); frame.revalidateMinimumSize(); @@ -603,10 +608,10 @@ public class ClientUI } /** - * Changes cursor for client window. Requires ${@link ClientUI#open(RuneLite)} to be called first. + * Changes cursor for client window. Requires ${@link ClientUI#init(RuneLite)} to be called first. * FIXME: This is working properly only on Windows, Linux and Mac are displaying cursor incorrectly * @param image cursor image - * @param name cursor name + * @param name cursor name */ public void setCursor(final BufferedImage image, final String name) { diff --git a/runelite-client/src/main/java/net/runelite/client/ui/SplashScreen.java b/runelite-client/src/main/java/net/runelite/client/ui/SplashScreen.java new file mode 100644 index 0000000000..6e945cb2a4 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/SplashScreen.java @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2019 Abex + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.ui; + +import java.awt.Color; +import java.awt.Container; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import javax.annotation.Nullable; +import javax.imageio.ImageIO; +import javax.swing.ImageIcon; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JProgressBar; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import javax.swing.Timer; +import javax.swing.UIManager; +import javax.swing.border.EmptyBorder; +import javax.swing.plaf.basic.BasicProgressBarUI; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class SplashScreen extends JFrame implements ActionListener +{ + private static final int WIDTH = 200; + private static final int PAD = 10; + + private static SplashScreen INSTANCE; + + private final JLabel action = new JLabel("Loading"); + private final JProgressBar progress = new JProgressBar(); + private final JLabel subAction = new JLabel(); + private final Timer timer; + + private volatile double overallProgress = 0; + private volatile String actionText = "Loading"; + private volatile String subActionText = ""; + private volatile String progressText = null; + + private SplashScreen() throws IOException + { + BufferedImage logo = ImageIO.read(SplashScreen.class.getResourceAsStream("runelite_transparent.png")); + + setTitle("RuneLite Launcher"); + + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setUndecorated(true); + setIconImage(logo); + setLayout(null); + Container pane = getContentPane(); + pane.setBackground(ColorScheme.DARKER_GRAY_COLOR); + + Font font = new Font(Font.DIALOG, Font.PLAIN, 12); + + JLabel logoLabel = new JLabel(new ImageIcon(logo)); + pane.add(logoLabel); + logoLabel.setBounds(0, 0, WIDTH, WIDTH); + + int y = WIDTH; + + pane.add(action); + action.setForeground(Color.WHITE); + action.setBounds(0, y, WIDTH, 16); + action.setHorizontalAlignment(SwingConstants.CENTER); + action.setFont(font); + y += action.getHeight() + PAD; + + pane.add(progress); + progress.setForeground(ColorScheme.BRAND_ORANGE); + progress.setBackground(ColorScheme.BRAND_ORANGE.darker().darker()); + progress.setBorder(new EmptyBorder(0, 0, 0, 0)); + progress.setBounds(0, y, WIDTH, 14); + progress.setFont(font); + progress.setUI(new BasicProgressBarUI() + { + @Override + protected Color getSelectionBackground() + { + return Color.BLACK; + } + + @Override + protected Color getSelectionForeground() + { + return Color.BLACK; + } + }); + y += 12 + PAD; + + pane.add(subAction); + subAction.setForeground(Color.LIGHT_GRAY); + subAction.setBounds(0, y, WIDTH, 16); + subAction.setHorizontalAlignment(SwingConstants.CENTER); + subAction.setFont(font); + y += subAction.getHeight() + PAD; + + setSize(WIDTH, y); + setLocationRelativeTo(null); + + timer = new Timer(100, this); + timer.setRepeats(true); + timer.start(); + + setVisible(true); + } + + @Override + public void actionPerformed(ActionEvent e) + { + action.setText(actionText); + subAction.setText(subActionText); + progress.setMaximum(1000); + progress.setValue((int) (overallProgress * 1000)); + + String progressText = this.progressText; + if (progressText == null) + { + progress.setStringPainted(false); + } + else + { + progress.setStringPainted(true); + progress.setString(progressText); + } + } + + public static void init() + { + try + { + SwingUtilities.invokeAndWait(() -> + { + if (INSTANCE != null) + { + return; + } + + try + { + UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); + INSTANCE = new SplashScreen(); + } + catch (Exception e) + { + log.warn("Unable to start splash screen", e); + } + }); + } + catch (InterruptedException | InvocationTargetException bs) + { + throw new RuntimeException(bs); + } + } + + public static void stop() + { + SwingUtilities.invokeLater(() -> + { + if (INSTANCE == null) + { + return; + } + + INSTANCE.timer.stop(); + INSTANCE.dispose(); + INSTANCE = null; + }); + } + + public static void stage(double overallProgress, @Nullable String actionText, String subActionText) + { + stage(overallProgress, actionText, subActionText, null); + } + + public static void stage(double startProgress, double endProgress, + @Nullable String actionText, String subActionText, + int done, int total, boolean mib) + { + String progress; + if (mib) + { + final double MiB = 1024 * 1042; + progress = String.format("%.1f / %.1f MiB", done / MiB, total / MiB); + } + else + { + progress = done + " / " + total; + } + stage(startProgress + ((endProgress - startProgress) * done / total), actionText, subActionText, progress); + } + + public static void stage(double overallProgress, @Nullable String actionText, String subActionText, @Nullable String progressText) + { + if (INSTANCE != null) + { + INSTANCE.overallProgress = overallProgress; + if (actionText != null) + { + INSTANCE.actionText = actionText; + } + INSTANCE.subActionText = subActionText; + INSTANCE.progressText = progressText; + } + } +} diff --git a/runelite-client/src/main/resources/net/runelite/client/ui/runelite_transparent.png b/runelite-client/src/main/resources/net/runelite/client/ui/runelite_transparent.png new file mode 100644 index 0000000000000000000000000000000000000000..1d96a54c6db8272037994c775c330d7b083d1a0c GIT binary patch literal 31065 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