rl-client: open FatalErrorDialog early during outages

This commit is contained in:
Max Weber
2022-03-24 13:55:40 -06:00
parent f2f8a05796
commit fb6f9682bd
7 changed files with 115 additions and 14 deletions

View File

@@ -228,8 +228,8 @@ public class RuneLite
try try
{ {
final ClientLoader clientLoader = new ClientLoader(okHttpClient, options.valueOf(updateMode), (String) options.valueOf("jav_config"));
final RuntimeConfigLoader runtimeConfigLoader = new RuntimeConfigLoader(okHttpClient); final RuntimeConfigLoader runtimeConfigLoader = new RuntimeConfigLoader(okHttpClient);
final ClientLoader clientLoader = new ClientLoader(okHttpClient, options.valueOf(updateMode), runtimeConfigLoader, (String) options.valueOf("jav_config"));
new Thread(() -> new Thread(() ->
{ {
@@ -247,6 +247,7 @@ public class RuneLite
{ {
SwingUtilities.invokeLater(() -> SwingUtilities.invokeLater(() ->
new FatalErrorDialog("Developers should enable assertions; Add `-ea` to your JVM arguments`") new FatalErrorDialog("Developers should enable assertions; Add `-ea` to your JVM arguments`")
.addHelpButtons()
.addBuildingGuide() .addBuildingGuide()
.open()); .open());
return; return;
@@ -282,6 +283,7 @@ public class RuneLite
log.error("Failure during startup", e); log.error("Failure during startup", e);
SwingUtilities.invokeLater(() -> SwingUtilities.invokeLater(() ->
new FatalErrorDialog("RuneLite has encountered an unexpected error during startup.") new FatalErrorDialog("RuneLite has encountered an unexpected error during startup.")
.addHelpButtons()
.open()); .open());
} }
finally finally

View File

@@ -49,6 +49,7 @@ public class RuneLiteProperties
private static final String PLUGINHUB_VERSION = "runelite.pluginhub.version"; private static final String PLUGINHUB_VERSION = "runelite.pluginhub.version";
private static final String API_BASE = "runelite.api.base"; private static final String API_BASE = "runelite.api.base";
private static final String RUNELITE_CONFIG = "runelite.config"; private static final String RUNELITE_CONFIG = "runelite.config";
private static final String OSRS_TWITTER_LINK = "runelite.osrstwitter.link";
@Getter(AccessLevel.PACKAGE) @Getter(AccessLevel.PACKAGE)
private static final Properties properties = new Properties(); private static final Properties properties = new Properties();
@@ -136,4 +137,9 @@ public class RuneLiteProperties
{ {
return properties.getProperty(RUNELITE_CONFIG); return properties.getProperty(RUNELITE_CONFIG);
} }
public static String getOSRSTwitterLink()
{
return properties.getProperty(OSRS_TWITTER_LINK);
}
} }

View File

@@ -24,13 +24,46 @@
*/ */
package net.runelite.client; package net.runelite.client;
import com.google.common.base.Strings;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import javax.swing.SwingUtilities;
import lombok.Data; import lombok.Data;
import net.runelite.client.ui.FatalErrorDialog;
import net.runelite.client.util.LinkBrowser;
@Data @Data
public class RuntimeConfig public class RuntimeConfig
{ {
private Map<String, ?> props = Collections.emptyMap(); private Map<String, ?> props = Collections.emptyMap();
private Map<String, String> sysProps = Collections.emptyMap(); private Map<String, String> sysProps = Collections.emptyMap();
private String outageMessage;
private Map<String, String> outageLinks;
public boolean showOutageMessage()
{
if (Strings.isNullOrEmpty(getOutageMessage()))
{
return false;
}
SwingUtilities.invokeLater(() ->
{
FatalErrorDialog fed = new FatalErrorDialog(getOutageMessage());
if (getOutageLinks() != null)
{
for (Map.Entry<String, String> e : getOutageLinks().entrySet())
{
fed.addButton(e.getKey(), () -> LinkBrowser.browse(e.getValue()));
}
}
else
{
fed.addButton("OSRS Twitter", () -> LinkBrowser.browse(RuneLiteProperties.getOSRSTwitterLink()));
}
fed.open();
});
return true;
}
} }

View File

@@ -31,7 +31,10 @@ import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Supplier; import java.util.function.Supplier;
import javax.annotation.Nullable;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.runelite.http.api.RuneLiteAPI; import net.runelite.http.api.RuneLiteAPI;
import okhttp3.Call; import okhttp3.Call;
@@ -41,7 +44,7 @@ import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
@Slf4j @Slf4j
class RuntimeConfigLoader implements Supplier<RuntimeConfig> public class RuntimeConfigLoader implements Supplier<RuntimeConfig>
{ {
private final OkHttpClient okHttpClient; private final OkHttpClient okHttpClient;
private final CompletableFuture<RuntimeConfig> configFuture; private final CompletableFuture<RuntimeConfig> configFuture;
@@ -66,6 +69,19 @@ class RuntimeConfigLoader implements Supplier<RuntimeConfig>
} }
} }
@Nullable
public RuntimeConfig tryGet()
{
try
{
return configFuture.get(0, TimeUnit.SECONDS);
}
catch (InterruptedException | ExecutionException | TimeoutException e)
{
return null;
}
}
private CompletableFuture<RuntimeConfig> fetch() private CompletableFuture<RuntimeConfig> fetch()
{ {
CompletableFuture<RuntimeConfig> future = new CompletableFuture<>(); CompletableFuture<RuntimeConfig> future = new CompletableFuture<>();

View File

@@ -61,6 +61,8 @@ import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client; import net.runelite.api.Client;
import net.runelite.client.RuneLite; import net.runelite.client.RuneLite;
import net.runelite.client.RuneLiteProperties; import net.runelite.client.RuneLiteProperties;
import net.runelite.client.RuntimeConfig;
import net.runelite.client.RuntimeConfigLoader;
import static net.runelite.client.rs.ClientUpdateCheckMode.AUTO; import static net.runelite.client.rs.ClientUpdateCheckMode.AUTO;
import static net.runelite.client.rs.ClientUpdateCheckMode.NONE; import static net.runelite.client.rs.ClientUpdateCheckMode.NONE;
import static net.runelite.client.rs.ClientUpdateCheckMode.VANILLA; import static net.runelite.client.rs.ClientUpdateCheckMode.VANILLA;
@@ -75,7 +77,7 @@ import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
@Slf4j @Slf4j
@SuppressWarnings("deprecation") @SuppressWarnings({"deprecation", "removal"})
public class ClientLoader implements Supplier<Applet> public class ClientLoader implements Supplier<Applet>
{ {
private static final int NUM_ATTEMPTS = 6; private static final int NUM_ATTEMPTS = 6;
@@ -87,16 +89,18 @@ public class ClientLoader implements Supplier<Applet>
private final ClientConfigLoader clientConfigLoader; private final ClientConfigLoader clientConfigLoader;
private ClientUpdateCheckMode updateCheckMode; private ClientUpdateCheckMode updateCheckMode;
private final WorldSupplier worldSupplier; private final WorldSupplier worldSupplier;
private final RuntimeConfigLoader runtimeConfigLoader;
private final String javConfigUrl; private final String javConfigUrl;
private Object client; private Object client;
public ClientLoader(OkHttpClient okHttpClient, ClientUpdateCheckMode updateCheckMode, String javConfigUrl) public ClientLoader(OkHttpClient okHttpClient, ClientUpdateCheckMode updateCheckMode, RuntimeConfigLoader runtimeConfigLoader, String javConfigUrl)
{ {
this.okHttpClient = okHttpClient; this.okHttpClient = okHttpClient;
this.clientConfigLoader = new ClientConfigLoader(okHttpClient); this.clientConfigLoader = new ClientConfigLoader(okHttpClient);
this.updateCheckMode = updateCheckMode; this.updateCheckMode = updateCheckMode;
this.worldSupplier = new WorldSupplier(okHttpClient); this.worldSupplier = new WorldSupplier(okHttpClient);
this.runtimeConfigLoader = runtimeConfigLoader;
this.javConfigUrl = javConfigUrl; this.javConfigUrl = javConfigUrl;
} }
@@ -176,12 +180,19 @@ public class ClientLoader implements Supplier<Applet>
return rs; return rs;
} }
catch (OutageException e)
{
return e;
}
catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException
| VerificationException | SecurityException e) | VerificationException | SecurityException e)
{ {
log.error("Error loading RS!", e); log.error("Error loading RS!", e);
SwingUtilities.invokeLater(() -> FatalErrorDialog.showNetErrorWindow("loading the client", e)); if (!checkOutages())
{
SwingUtilities.invokeLater(() -> FatalErrorDialog.showNetErrorWindow("loading the client", e));
}
return e; return e;
} }
} }
@@ -206,6 +217,10 @@ public class ClientLoader implements Supplier<Applet>
catch (IOException e) catch (IOException e)
{ {
log.info("Failed to get jav_config from host \"{}\" ({})", url.host(), e.getMessage()); log.info("Failed to get jav_config from host \"{}\" ({})", url.host(), e.getMessage());
if (checkOutages())
{
throw new OutageException(e);
}
if (!javConfigUrl.equals(RuneLiteProperties.getJavConfig())) if (!javConfigUrl.equals(RuneLiteProperties.getJavConfig()))
{ {
@@ -405,6 +420,10 @@ public class ClientLoader implements Supplier<Applet>
catch (IOException e) catch (IOException e)
{ {
log.warn("Failed to download gamepack from \"{}\"", url, e); log.warn("Failed to download gamepack from \"{}\"", url, e);
if (checkOutages())
{
throw new OutageException(e);
}
// With fallback config do 1 attempt (there are no additional urls to try) // With fallback config do 1 attempt (there are no additional urls to try)
if (!javConfigUrl.equals(RuneLiteProperties.getJavConfig()) || config.isFallback() || attempt >= NUM_ATTEMPTS) if (!javConfigUrl.equals(RuneLiteProperties.getJavConfig()) || config.isFallback() || attempt >= NUM_ATTEMPTS)
@@ -430,6 +449,7 @@ public class ClientLoader implements Supplier<Applet>
SwingUtilities.invokeLater(() -> SwingUtilities.invokeLater(() ->
new FatalErrorDialog("The client-patch is missing from the classpath. If you are building " + new FatalErrorDialog("The client-patch is missing from the classpath. If you are building " +
"the client you need to re-run maven") "the client you need to re-run maven")
.addHelpButtons()
.addBuildingGuide() .addBuildingGuide()
.open()); .open());
throw new NullPointerException(); throw new NullPointerException();
@@ -603,4 +623,22 @@ public class ClientLoader implements Supplier<Applet>
verifyJarEntry(je, chains); verifyJarEntry(je, chains);
} }
} }
private static class OutageException extends RuntimeException
{
private OutageException(Throwable cause)
{
super(cause);
}
}
private boolean checkOutages()
{
RuntimeConfig rtc = runtimeConfigLoader.tryGet();
if (rtc != null)
{
return rtc.showOutageMessage();
}
return false;
}
} }

View File

@@ -137,13 +137,6 @@ public class FatalErrorDialog extends JDialog
rightColumn.setBackground(ColorScheme.DARK_GRAY_COLOR); rightColumn.setBackground(ColorScheme.DARK_GRAY_COLOR);
rightColumn.setMaximumSize(new Dimension(200, Integer.MAX_VALUE)); rightColumn.setMaximumSize(new Dimension(200, Integer.MAX_VALUE));
addButton("Open logs folder", () ->
{
LinkBrowser.open(RuneLite.LOGS_DIR.toString());
});
addButton("Get help on Discord", () -> LinkBrowser.browse(RuneLiteProperties.getDiscordInvite()));
addButton("Troubleshooting steps", () -> LinkBrowser.browse(RuneLiteProperties.getTroubleshootingLink()));
pane.add(rightColumn, BorderLayout.EAST); pane.add(rightColumn, BorderLayout.EAST);
} }
@@ -193,6 +186,13 @@ public class FatalErrorDialog extends JDialog
return this; return this;
} }
public FatalErrorDialog addHelpButtons()
{
return addButton("Open logs folder", () -> LinkBrowser.open(RuneLite.LOGS_DIR.toString()))
.addButton("Get help on Discord", () -> LinkBrowser.browse(RuneLiteProperties.getDiscordInvite()))
.addButton("Troubleshooting steps", () -> LinkBrowser.browse(RuneLiteProperties.getTroubleshootingLink()));
}
public FatalErrorDialog addBuildingGuide() public FatalErrorDialog addBuildingGuide()
{ {
return addButton("Building guide", () -> LinkBrowser.browse(RuneLiteProperties.getBuildingLink())); return addButton("Building guide", () -> LinkBrowser.browse(RuneLiteProperties.getBuildingLink()));
@@ -205,6 +205,7 @@ public class FatalErrorDialog extends JDialog
new FatalErrorDialog("RuneLite was unable to verify the security of its connection to the internet while " + 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" + action + ". You may have a misbehaving antivirus, internet service provider, a proxy, or an incomplete" +
" java installation.") " java installation.")
.addHelpButtons()
.open(); .open();
return; return;
} }
@@ -213,6 +214,7 @@ public class FatalErrorDialog extends JDialog
{ {
new FatalErrorDialog("RuneLite is unable to connect to a required server while " + action + ". " + new FatalErrorDialog("RuneLite is unable to connect to a required server while " + action + ". " +
"Please check your internet connection") "Please check your internet connection")
.addHelpButtons()
.open(); .open();
return; return;
} }
@@ -222,11 +224,14 @@ public class FatalErrorDialog extends JDialog
new FatalErrorDialog("RuneLite is unable to resolve the address of a required server while " + action + ". " + 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 " + "Your DNS resolver may be misconfigured, pointing to an inaccurate resolver, or your internet connection may " +
"be down. ") "be down. ")
.addHelpButtons()
.addButton("Change your DNS resolver", () -> LinkBrowser.browse(RuneLiteProperties.getDNSChangeLink())) .addButton("Change your DNS resolver", () -> LinkBrowser.browse(RuneLiteProperties.getDNSChangeLink()))
.open(); .open();
return; return;
} }
new FatalErrorDialog("RuneLite encountered a fatal error while " + action + ".").open(); new FatalErrorDialog("RuneLite encountered a fatal error while " + action + ".")
.addHelpButtons()
.open();
} }
} }

View File

@@ -19,4 +19,5 @@ runelite.api.base=https://api.runelite.net/runelite-${project.version}
runelite.session=https://api.runelite.net/session runelite.session=https://api.runelite.net/session
runelite.static.base=https://static.runelite.net runelite.static.base=https://static.runelite.net
runelite.ws=https://api.runelite.net/ws runelite.ws=https://api.runelite.net/ws
runelite.config=https://static.runelite.net/config.json runelite.config=https://static.runelite.net/config.json
runelite.osrstwitter.link=https://twitter.com/OldSchoolRS