runelite-client: Add fatal error dialog
This commit is contained in:
@@ -37,6 +37,7 @@ import java.util.Locale;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Provider;
|
||||
import javax.inject.Singleton;
|
||||
import javax.swing.SwingUtilities;
|
||||
import joptsimple.ArgumentAcceptingOptionSpec;
|
||||
import joptsimple.OptionParser;
|
||||
import joptsimple.OptionSet;
|
||||
@@ -60,6 +61,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.FatalErrorDialog;
|
||||
import net.runelite.client.ui.SplashScreen;
|
||||
import net.runelite.client.ui.overlay.OverlayManager;
|
||||
import net.runelite.client.ui.overlay.OverlayRenderer;
|
||||
@@ -78,6 +80,7 @@ public class RuneLite
|
||||
public static final File RUNELITE_DIR = new File(System.getProperty("user.home"), ".runelite");
|
||||
public static final File PROFILES_DIR = new File(RUNELITE_DIR, "profiles");
|
||||
public static final File SCREENSHOT_DIR = new File(RUNELITE_DIR, "screenshots");
|
||||
public static final File LOGS_DIR = new File(RUNELITE_DIR, "logs");
|
||||
|
||||
@Getter
|
||||
private static Injector injector;
|
||||
@@ -219,7 +222,11 @@ public class RuneLite
|
||||
assert assertions = true;
|
||||
if (!assertions)
|
||||
{
|
||||
throw new RuntimeException("Developers should enable assertions; Add `-ea` to your JVM arguments`");
|
||||
SwingUtilities.invokeLater(() ->
|
||||
new FatalErrorDialog("Developers should enable assertions; Add `-ea` to your JVM arguments`")
|
||||
.addBuildingGuide()
|
||||
.open());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,6 +245,13 @@ public class RuneLite
|
||||
final long uptime = rb.getUptime();
|
||||
log.info("Client initialization took {}ms. Uptime: {}ms", end - start, uptime);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.warn("Failure during startup", e);
|
||||
SwingUtilities.invokeLater(() ->
|
||||
new FatalErrorDialog("RuneLite has encountered an unexpected error during startup.")
|
||||
.open());
|
||||
}
|
||||
finally
|
||||
{
|
||||
SplashScreen.stop();
|
||||
|
||||
@@ -40,6 +40,9 @@ public class RuneLiteProperties
|
||||
private static final String WIKI_LINK = "runelite.wiki.link";
|
||||
private static final String PATREON_LINK = "runelite.patreon.link";
|
||||
private static final String LAUNCHER_VERSION_PROPERTY = "runelite.launcher.version";
|
||||
private static final String TROUBLESHOOTING_LINK = "runelite.wiki.troubleshooting.link";
|
||||
private static final String BUILDING_LINK = "runelite.wiki.building.link";
|
||||
private static final String DNS_CHANGE_LINK = "runelite.dnschange.link";
|
||||
|
||||
private static final Properties properties = new Properties();
|
||||
|
||||
@@ -100,4 +103,19 @@ public class RuneLiteProperties
|
||||
{
|
||||
return System.getProperty(LAUNCHER_VERSION_PROPERTY);
|
||||
}
|
||||
|
||||
public static String getTroubleshootingLink()
|
||||
{
|
||||
return properties.getProperty(TROUBLESHOOTING_LINK);
|
||||
}
|
||||
|
||||
public static String getBuildingLink()
|
||||
{
|
||||
return properties.getProperty(BUILDING_LINK);
|
||||
}
|
||||
|
||||
public static String getDNSChangeLink()
|
||||
{
|
||||
return properties.getProperty(DNS_CHANGE_LINK);
|
||||
}
|
||||
}
|
||||
@@ -49,11 +49,13 @@ import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarInputStream;
|
||||
import javax.swing.SwingUtilities;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
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.FatalErrorDialog;
|
||||
import net.runelite.client.ui.SplashScreen;
|
||||
import net.runelite.http.api.RuneLiteAPI;
|
||||
import okhttp3.Request;
|
||||
@@ -64,7 +66,7 @@ import org.apache.commons.compress.compressors.CompressorException;
|
||||
public class ClientLoader implements Supplier<Applet>
|
||||
{
|
||||
private ClientUpdateCheckMode updateCheckMode;
|
||||
private Applet client = null;
|
||||
private Object client = null;
|
||||
|
||||
public ClientLoader(ClientUpdateCheckMode updateCheckMode)
|
||||
{
|
||||
@@ -78,10 +80,15 @@ public class ClientLoader implements Supplier<Applet>
|
||||
{
|
||||
client = doLoad();
|
||||
}
|
||||
return client;
|
||||
|
||||
if (client instanceof Throwable)
|
||||
{
|
||||
throw new RuntimeException((Throwable) client);
|
||||
}
|
||||
return (Applet) client;
|
||||
}
|
||||
|
||||
private Applet doLoad()
|
||||
private Object doLoad()
|
||||
{
|
||||
if (updateCheckMode == NONE)
|
||||
{
|
||||
@@ -172,6 +179,15 @@ public class ClientLoader implements Supplier<Applet>
|
||||
Map<String, String> hashes;
|
||||
try (InputStream is = ClientLoader.class.getResourceAsStream("/patch/hashes.json"))
|
||||
{
|
||||
if (is == null)
|
||||
{
|
||||
SwingUtilities.invokeLater(() ->
|
||||
new FatalErrorDialog("The client-patch is missing from the classpath. If you are building " +
|
||||
"the client you need to re-run maven")
|
||||
.addBuildingGuide()
|
||||
.open());
|
||||
throw new NullPointerException();
|
||||
}
|
||||
hashes = new Gson().fromJson(new InputStreamReader(is), new TypeToken<HashMap<String, String>>()
|
||||
{
|
||||
}.getType());
|
||||
@@ -264,15 +280,10 @@ public class ClientLoader implements Supplier<Applet>
|
||||
| CompressorException | InvalidHeaderException | CertificateException | VerificationException
|
||||
| SecurityException e)
|
||||
{
|
||||
if (e instanceof ClassNotFoundException)
|
||||
{
|
||||
log.error("Unable to load client - class not found. This means you"
|
||||
+ " are not running RuneLite with Maven as the client patch"
|
||||
+ " is not in your classpath.");
|
||||
}
|
||||
|
||||
log.error("Error loading RS!", e);
|
||||
return null;
|
||||
|
||||
SwingUtilities.invokeLater(() -> FatalErrorDialog.showNetErrorWindow("loading the client", e));
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
*/
|
||||
package net.runelite.client.rs;
|
||||
|
||||
class VerificationException extends Exception
|
||||
public class VerificationException extends Exception
|
||||
{
|
||||
public VerificationException(String message)
|
||||
{
|
||||
|
||||
@@ -53,7 +53,6 @@ import javax.swing.JButton;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JOptionPane;
|
||||
import static javax.swing.JOptionPane.ERROR_MESSAGE;
|
||||
import static javax.swing.JOptionPane.INFORMATION_MESSAGE;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JRootPane;
|
||||
@@ -518,14 +517,7 @@ public class ClientUI
|
||||
});
|
||||
|
||||
// Show out of date dialog if needed
|
||||
if (client == null)
|
||||
{
|
||||
SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(frame,
|
||||
"Error loading client! Check your logs for more details.",
|
||||
"Unable to load client",
|
||||
ERROR_MESSAGE));
|
||||
}
|
||||
else if (!(client instanceof Client))
|
||||
if (client != null && !(client instanceof Client))
|
||||
{
|
||||
SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(frame,
|
||||
"RuneLite has not yet been updated to work with the latest\n"
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* 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.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.Desktop;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.client.RuneLite;
|
||||
import net.runelite.client.RuneLiteProperties;
|
||||
import net.runelite.client.rs.VerificationException;
|
||||
import net.runelite.client.util.LinkBrowser;
|
||||
|
||||
@Slf4j
|
||||
public class FatalErrorDialog extends JDialog
|
||||
{
|
||||
private static final AtomicBoolean alreadyOpen = new AtomicBoolean(false);
|
||||
|
||||
private final JPanel rightColumn = new JPanel();
|
||||
private final Font font = new Font(Font.DIALOG, Font.PLAIN, 12);
|
||||
|
||||
public FatalErrorDialog(String message)
|
||||
{
|
||||
if (alreadyOpen.getAndSet(true))
|
||||
{
|
||||
throw new IllegalStateException("Fatal error during fatal error: " + message);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
}
|
||||
|
||||
UIManager.put("Button.select", ColorScheme.DARKER_GRAY_COLOR);
|
||||
|
||||
try
|
||||
{
|
||||
BufferedImage logo = ImageIO.read(SplashScreen.class.getResourceAsStream("runelite_transparent.png"));
|
||||
setIconImage(logo);
|
||||
|
||||
JLabel runelite = new JLabel();
|
||||
runelite.setIcon(new ImageIcon(logo));
|
||||
runelite.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
runelite.setBackground(ColorScheme.DARK_GRAY_COLOR);
|
||||
runelite.setOpaque(true);
|
||||
rightColumn.add(runelite);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
}
|
||||
|
||||
addWindowListener(new WindowAdapter()
|
||||
{
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e)
|
||||
{
|
||||
System.exit(-1);
|
||||
}
|
||||
});
|
||||
|
||||
setTitle("Fatal error starting RuneLite");
|
||||
setLayout(new BorderLayout());
|
||||
|
||||
Container pane = getContentPane();
|
||||
pane.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
|
||||
JPanel leftPane = new JPanel();
|
||||
leftPane.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
leftPane.setLayout(new BorderLayout());
|
||||
|
||||
JLabel title = new JLabel("There was a fatal error starting RuneLite");
|
||||
title.setForeground(Color.WHITE);
|
||||
title.setFont(font.deriveFont(16.f));
|
||||
title.setBorder(new EmptyBorder(10, 10, 10, 10));
|
||||
leftPane.add(title, BorderLayout.NORTH);
|
||||
|
||||
leftPane.setPreferredSize(new Dimension(400, 200));
|
||||
JTextArea textArea = new JTextArea(message);
|
||||
textArea.setFont(font);
|
||||
textArea.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
textArea.setForeground(Color.LIGHT_GRAY);
|
||||
textArea.setLineWrap(true);
|
||||
textArea.setWrapStyleWord(true);
|
||||
textArea.setBorder(new EmptyBorder(10, 10, 10, 10));
|
||||
textArea.setEditable(false);
|
||||
leftPane.add(textArea, BorderLayout.CENTER);
|
||||
|
||||
pane.add(leftPane, BorderLayout.CENTER);
|
||||
|
||||
rightColumn.setLayout(new BoxLayout(rightColumn, BoxLayout.Y_AXIS));
|
||||
rightColumn.setBackground(ColorScheme.DARK_GRAY_COLOR);
|
||||
rightColumn.setMaximumSize(new Dimension(200, Integer.MAX_VALUE));
|
||||
|
||||
addButton("Open logs folder", () ->
|
||||
{
|
||||
try
|
||||
{
|
||||
Desktop.getDesktop().open(RuneLite.LOGS_DIR);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
log.warn("Unable to open logs", e);
|
||||
}
|
||||
});
|
||||
addButton("Get help on Discord", () -> LinkBrowser.browse(RuneLiteProperties.getDiscordInvite()));
|
||||
addButton("Troubleshooting steps", () -> LinkBrowser.browse(RuneLiteProperties.getTroubleshootingLink()));
|
||||
|
||||
pane.add(rightColumn, BorderLayout.EAST);
|
||||
}
|
||||
|
||||
public void open()
|
||||
{
|
||||
addButton("Exit", () -> System.exit(-1));
|
||||
|
||||
pack();
|
||||
SplashScreen.stop();
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
public FatalErrorDialog addButton(String message, Runnable action)
|
||||
{
|
||||
JButton button = new JButton(message);
|
||||
button.addActionListener(e -> action.run());
|
||||
button.setFont(font);
|
||||
button.setBackground(ColorScheme.DARK_GRAY_COLOR);
|
||||
button.setForeground(Color.LIGHT_GRAY);
|
||||
button.setBorder(BorderFactory.createCompoundBorder(
|
||||
BorderFactory.createMatteBorder(1, 0, 0, 0, ColorScheme.DARK_GRAY_COLOR.brighter()),
|
||||
new EmptyBorder(4, 4, 4, 4)
|
||||
));
|
||||
button.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
button.setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));
|
||||
button.setFocusPainted(false);
|
||||
button.addChangeListener(ev ->
|
||||
{
|
||||
if (button.getModel().isPressed())
|
||||
{
|
||||
button.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
}
|
||||
else if (button.getModel().isRollover())
|
||||
{
|
||||
button.setBackground(ColorScheme.DARK_GRAY_HOVER_COLOR);
|
||||
}
|
||||
else
|
||||
{
|
||||
button.setBackground(ColorScheme.DARK_GRAY_COLOR);
|
||||
}
|
||||
});
|
||||
|
||||
rightColumn.add(button);
|
||||
rightColumn.revalidate();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public FatalErrorDialog addBuildingGuide()
|
||||
{
|
||||
return addButton("Building guide", () -> LinkBrowser.browse(RuneLiteProperties.getBuildingLink()));
|
||||
}
|
||||
|
||||
public static void showNetErrorWindow(String action, Throwable err)
|
||||
{
|
||||
if (err instanceof VerificationException || err instanceof GeneralSecurityException)
|
||||
{
|
||||
new FatalErrorDialog("RuneLite was unable to verify the security of its connection to the internet while " +
|
||||
action + ". You may have a misbehaving antivirus, internet service provider, a proxy, or an incomplete" +
|
||||
" java installation.")
|
||||
.open();
|
||||
return;
|
||||
}
|
||||
|
||||
if (err instanceof ConnectException)
|
||||
{
|
||||
new FatalErrorDialog("RuneLite is unable to connect to a required server while " + action + ". " +
|
||||
"Please check your internet connection")
|
||||
.open();
|
||||
return;
|
||||
}
|
||||
|
||||
if (err instanceof UnknownHostException)
|
||||
{
|
||||
new FatalErrorDialog("RuneLite is unable to resolve the address of a required server while " + action + ". " +
|
||||
"Your DNS resolver may be misconfigured, pointing to an inaccurate resolver, or your internet connection may " +
|
||||
"be down. ")
|
||||
.addButton("Change your DNS resolver", () -> LinkBrowser.browse(RuneLiteProperties.getDNSChangeLink()))
|
||||
.open();
|
||||
return;
|
||||
}
|
||||
|
||||
new FatalErrorDialog("RuneLite encountered a fatal error while " + action + ".").open();
|
||||
}
|
||||
}
|
||||
@@ -5,4 +5,7 @@ runelite.discord.appid=409416265891971072
|
||||
runelite.discord.invite=https://discord.gg/R4BQ8tU
|
||||
runelite.github.link=https://github.com/runelite
|
||||
runelite.wiki.link=https://github.com/runelite/runelite/wiki
|
||||
runelite.patreon.link=https://www.patreon.com/runelite
|
||||
runelite.patreon.link=https://www.patreon.com/runelite
|
||||
runelite.wiki.troubleshooting.link=https://github.com/runelite/runelite/wiki/Troubleshooting-problems-with-the-client
|
||||
runelite.wiki.building.link=https://github.com/runelite/runelite/wiki/Building-with-IntelliJ-IDEA#client-failing-to-start
|
||||
runelite.dnschange.link=https://1.1.1.1/dns/
|
||||
|
||||
Reference in New Issue
Block a user