Merge pull request #9629 from abextm/splash

Add loading splash screen
This commit is contained in:
Adam
2019-08-10 17:51:37 -04:00
committed by GitHub
17 changed files with 661 additions and 114 deletions

View File

@@ -73,8 +73,9 @@ public class Notifier
private static final int MINIMUM_FLASH_DURATION_MILLIS = 2000; private static final int MINIMUM_FLASH_DURATION_MILLIS = 2000;
private static final int MINIMUM_FLASH_DURATION_TICKS = MINIMUM_FLASH_DURATION_MILLIS / Constants.CLIENT_TICK_LENGTH; private static final int MINIMUM_FLASH_DURATION_TICKS = MINIMUM_FLASH_DURATION_MILLIS / Constants.CLIENT_TICK_LENGTH;
private static final String appName = RuneLiteProperties.getTitle();
private final Client client; private final Client client;
private final String appName;
private final RuneLiteConfig runeLiteConfig; private final RuneLiteConfig runeLiteConfig;
private final ClientUI clientUI; private final ClientUI clientUI;
private final ScheduledExecutorService executorService; private final ScheduledExecutorService executorService;
@@ -89,12 +90,10 @@ public class Notifier
final ClientUI clientUI, final ClientUI clientUI,
final Client client, final Client client,
final RuneLiteConfig runeliteConfig, final RuneLiteConfig runeliteConfig,
final RuneLiteProperties runeLiteProperties,
final ScheduledExecutorService executorService, final ScheduledExecutorService executorService,
final ChatMessageManager chatMessageManager) final ChatMessageManager chatMessageManager)
{ {
this.client = client; this.client = client;
this.appName = runeLiteProperties.getTitle();
this.clientUI = clientUI; this.clientUI = clientUI;
this.runeLiteConfig = runeliteConfig; this.runeLiteConfig = runeliteConfig;
this.executorService = executorService; this.executorService = executorService;

View File

@@ -37,6 +37,7 @@ import java.util.Locale;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.inject.Provider; import javax.inject.Provider;
import javax.inject.Singleton; import javax.inject.Singleton;
import javax.swing.SwingUtilities;
import joptsimple.ArgumentAcceptingOptionSpec; import joptsimple.ArgumentAcceptingOptionSpec;
import joptsimple.OptionParser; import joptsimple.OptionParser;
import joptsimple.OptionSet; import joptsimple.OptionSet;
@@ -60,6 +61,8 @@ import net.runelite.client.rs.ClientLoader;
import net.runelite.client.rs.ClientUpdateCheckMode; import net.runelite.client.rs.ClientUpdateCheckMode;
import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.ClientUI;
import net.runelite.client.ui.DrawManager; 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.OverlayManager;
import net.runelite.client.ui.overlay.OverlayRenderer; import net.runelite.client.ui.overlay.OverlayRenderer;
import net.runelite.client.ui.overlay.WidgetOverlay; import net.runelite.client.ui.overlay.WidgetOverlay;
@@ -77,6 +80,7 @@ public class RuneLite
public static final File RUNELITE_DIR = new File(System.getProperty("user.home"), ".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 PROFILES_DIR = new File(RUNELITE_DIR, "profiles");
public static final File SCREENSHOT_DIR = new File(RUNELITE_DIR, "screenshots"); public static final File SCREENSHOT_DIR = new File(RUNELITE_DIR, "screenshots");
public static final File LOGS_DIR = new File(RUNELITE_DIR, "logs");
@Getter @Getter
private static Injector injector; private static Injector injector;
@@ -197,40 +201,61 @@ public class RuneLite
} }
}); });
final ClientLoader clientLoader = new ClientLoader(options.valueOf(updateMode)); SplashScreen.init();
SplashScreen.stage(0, "Retrieving client", "");
new Thread(() -> try
{ {
clientLoader.get(); final ClientLoader clientLoader = new ClientLoader(options.valueOf(updateMode));
ClassPreloader.preload();
}, "Preloader").start();
final boolean developerMode = options.has("developer-mode") && RuneLiteProperties.getLauncherVersion() == null; new Thread(() ->
if (developerMode)
{
boolean assertions = false;
assert assertions = true;
if (!assertions)
{ {
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)
{
SwingUtilities.invokeLater(() ->
new FatalErrorDialog("Developers should enable assertions; Add `-ea` to your JVM arguments`")
.addBuildingGuide()
.open());
return;
}
} }
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);
}
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();
} }
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 public void start() throws Exception
@@ -244,6 +269,8 @@ public class RuneLite
injector.injectMembers(client); injector.injectMembers(client);
} }
SplashScreen.stage(.57, null, "Loading configuration");
// Load user configuration // Load user configuration
configManager.load(); configManager.load();
@@ -257,6 +284,8 @@ public class RuneLite
// This will initialize configuration // This will initialize configuration
pluginManager.loadCorePlugins(); pluginManager.loadCorePlugins();
SplashScreen.stage(.70, null, "Finalizing configuration");
// Plugins have provided their config, so set default config // Plugins have provided their config, so set default config
// to main settings // to main settings
pluginManager.loadDefaultPluginConfiguration(); pluginManager.loadDefaultPluginConfiguration();
@@ -264,8 +293,10 @@ public class RuneLite
// Start client session // Start client session
clientSessionManager.start(); clientSessionManager.start();
SplashScreen.stage(.75, null, "Starting core interface");
// Initialize UI // Initialize UI
clientUI.open(this); clientUI.init(this);
// Initialize Discord service // Initialize Discord service
discordService.init(); discordService.init();
@@ -301,6 +332,10 @@ public class RuneLite
// Start plugins // Start plugins
pluginManager.startCorePlugins(); pluginManager.startCorePlugins();
SplashScreen.stop();
clientUI.show();
} }
public void shutdown() public void shutdown()

View File

@@ -75,7 +75,6 @@ public class RuneLiteModule extends AbstractModule
bind(ItemManager.class); bind(ItemManager.class);
bind(Scheduler.class); bind(Scheduler.class);
bind(PluginManager.class); bind(PluginManager.class);
bind(RuneLiteProperties.class);
bind(SessionManager.class); bind(SessionManager.class);
bind(Callbacks.class).to(Hooks.class); bind(Callbacks.class).to(Hooks.class);

View File

@@ -28,12 +28,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Properties; import java.util.Properties;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
@Singleton
@Slf4j
public class RuneLiteProperties public class RuneLiteProperties
{ {
private static final String RUNELITE_TITLE = "runelite.title"; private static final String RUNELITE_TITLE = "runelite.title";
@@ -45,58 +40,60 @@ public class RuneLiteProperties
private static final String WIKI_LINK = "runelite.wiki.link"; private static final String WIKI_LINK = "runelite.wiki.link";
private static final String PATREON_LINK = "runelite.patreon.link"; private static final String PATREON_LINK = "runelite.patreon.link";
private static final String LAUNCHER_VERSION_PROPERTY = "runelite.launcher.version"; 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 final Properties properties = new Properties(); private static final Properties properties = new Properties();
@Inject static
public RuneLiteProperties()
{ {
try (InputStream in = getClass().getResourceAsStream("runelite.properties")) try (InputStream in = RuneLiteProperties.class.getResourceAsStream("runelite.properties"))
{ {
properties.load(in); properties.load(in);
} }
catch (IOException ex) catch (IOException ex)
{ {
log.warn("unable to load propertries", ex); throw new RuntimeException(ex);
} }
} }
public String getTitle() public static String getTitle()
{ {
return properties.getProperty(RUNELITE_TITLE); return properties.getProperty(RUNELITE_TITLE);
} }
public String getVersion() public static String getVersion()
{ {
return properties.getProperty(RUNELITE_VERSION); return properties.getProperty(RUNELITE_VERSION);
} }
public String getRunescapeVersion() public static String getRunescapeVersion()
{ {
return properties.getProperty(RUNESCAPE_VERSION); return properties.getProperty(RUNESCAPE_VERSION);
} }
public String getDiscordAppId() public static String getDiscordAppId()
{ {
return properties.getProperty(DISCORD_APP_ID); return properties.getProperty(DISCORD_APP_ID);
} }
public String getDiscordInvite() public static String getDiscordInvite()
{ {
return properties.getProperty(DISCORD_INVITE); return properties.getProperty(DISCORD_INVITE);
} }
public String getGithubLink() public static String getGithubLink()
{ {
return properties.getProperty(GITHUB_LINK); return properties.getProperty(GITHUB_LINK);
} }
public String getWikiLink() public static String getWikiLink()
{ {
return properties.getProperty(WIKI_LINK); return properties.getProperty(WIKI_LINK);
} }
public String getPatreonLink() public static String getPatreonLink()
{ {
return properties.getProperty(PATREON_LINK); return properties.getProperty(PATREON_LINK);
} }
@@ -106,4 +103,19 @@ public class RuneLiteProperties
{ {
return System.getProperty(LAUNCHER_VERSION_PROPERTY); 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);
}
} }

View File

@@ -49,7 +49,6 @@ import net.runelite.discord.DiscordUser;
public class DiscordService implements AutoCloseable public class DiscordService implements AutoCloseable
{ {
private final EventBus eventBus; private final EventBus eventBus;
private final RuneLiteProperties runeLiteProperties;
private final ScheduledExecutorService executorService; private final ScheduledExecutorService executorService;
private final DiscordRPC discordRPC; private final DiscordRPC discordRPC;
@@ -62,12 +61,10 @@ public class DiscordService implements AutoCloseable
@Inject @Inject
private DiscordService( private DiscordService(
final EventBus eventBus, final EventBus eventBus,
final RuneLiteProperties runeLiteProperties,
final ScheduledExecutorService executorService) final ScheduledExecutorService executorService)
{ {
this.eventBus = eventBus; this.eventBus = eventBus;
this.runeLiteProperties = runeLiteProperties;
this.executorService = executorService; this.executorService = executorService;
DiscordRPC discordRPC = null; DiscordRPC discordRPC = null;
@@ -106,7 +103,7 @@ public class DiscordService implements AutoCloseable
discordEventHandlers.joinGame = this::joinGame; discordEventHandlers.joinGame = this::joinGame;
discordEventHandlers.spectateGame = this::spectateGame; discordEventHandlers.spectateGame = this::spectateGame;
discordEventHandlers.joinRequest = this::joinRequest; discordEventHandlers.joinRequest = this::joinRequest;
discordRPC.Discord_Initialize(runeLiteProperties.getDiscordAppId(), discordEventHandlers, true, null); discordRPC.Discord_Initialize(RuneLiteProperties.getDiscordAppId(), discordEventHandlers, true, null);
executorService.scheduleAtFixedRate(discordRPC::Discord_RunCallbacks, 0, 2, TimeUnit.SECONDS); executorService.scheduleAtFixedRate(discordRPC::Discord_RunCallbacks, 0, 2, TimeUnit.SECONDS);
} }

View File

@@ -70,6 +70,7 @@ import net.runelite.client.events.PluginChanged;
import net.runelite.client.task.Schedule; import net.runelite.client.task.Schedule;
import net.runelite.client.task.ScheduledMethod; import net.runelite.client.task.ScheduledMethod;
import net.runelite.client.task.Scheduler; import net.runelite.client.task.Scheduler;
import net.runelite.client.ui.SplashScreen;
import net.runelite.client.util.GameEventManager; import net.runelite.client.util.GameEventManager;
@Singleton @Singleton
@@ -200,6 +201,7 @@ public class PluginManager
public void startCorePlugins() public void startCorePlugins()
{ {
List<Plugin> scannedPlugins = new ArrayList<>(plugins); List<Plugin> scannedPlugins = new ArrayList<>(plugins);
int loaded = 0;
for (Plugin plugin : scannedPlugins) for (Plugin plugin : scannedPlugins)
{ {
try try
@@ -211,11 +213,15 @@ public class PluginManager
log.warn("Unable to start plugin {}. {}", plugin.getClass().getSimpleName(), ex); log.warn("Unable to start plugin {}. {}", plugin.getClass().getSimpleName(), ex);
plugins.remove(plugin); plugins.remove(plugin);
} }
loaded++;
SplashScreen.stage(.80, 1, null, "Starting plugins", loaded, scannedPlugins.size(), false);
} }
} }
List<Plugin> scanAndInstantiate(ClassLoader classLoader, String packageName) throws IOException List<Plugin> scanAndInstantiate(ClassLoader classLoader, String packageName) throws IOException
{ {
SplashScreen.stage(.59, null, "Loading Plugins");
MutableGraph<Class<? extends Plugin>> graph = GraphBuilder MutableGraph<Class<? extends Plugin>> graph = GraphBuilder
.directed() .directed()
.build(); .build();
@@ -280,20 +286,22 @@ public class PluginManager
List<Class<? extends Plugin>> sortedPlugins = topologicalSort(graph); List<Class<? extends Plugin>> sortedPlugins = topologicalSort(graph);
sortedPlugins = Lists.reverse(sortedPlugins); sortedPlugins = Lists.reverse(sortedPlugins);
int loaded = 0;
for (Class<? extends Plugin> pluginClazz : sortedPlugins) for (Class<? extends Plugin> pluginClazz : sortedPlugins)
{ {
Plugin plugin; Plugin plugin;
try try
{ {
plugin = instantiate(scannedPlugins, (Class<Plugin>) pluginClazz); plugin = instantiate(scannedPlugins, (Class<Plugin>) pluginClazz);
scannedPlugins.add(plugin);
} }
catch (PluginInstantiationException ex) catch (PluginInstantiationException ex)
{ {
log.warn("Error instantiating plugin!", 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; return scannedPlugins;

View File

@@ -68,9 +68,6 @@ public class ChatNotificationsPlugin extends Plugin
@Inject @Inject
private Notifier notifier; private Notifier notifier;
@Inject
private RuneLiteProperties runeLiteProperties;
//Custom Highlights //Custom Highlights
private Pattern usernameMatcher = null; private Pattern usernameMatcher = null;
private String usernameReplacer = ""; private String usernameReplacer = "";
@@ -148,7 +145,7 @@ public class ChatNotificationsPlugin extends Plugin
break; break;
case CONSOLE: case CONSOLE:
// Don't notify for notification messages // Don't notify for notification messages
if (chatMessage.getName().equals(runeLiteProperties.getTitle())) if (chatMessage.getName().equals(RuneLiteProperties.getTitle()))
{ {
return; return;
} }

View File

@@ -91,9 +91,6 @@ public class DiscordPlugin extends Plugin
@Inject @Inject
private ClientToolbar clientToolbar; private ClientToolbar clientToolbar;
@Inject
private RuneLiteProperties properties;
@Inject @Inject
private DiscordState discordState; private DiscordState discordState;
@@ -125,7 +122,7 @@ public class DiscordPlugin extends Plugin
.tab(false) .tab(false)
.tooltip("Join Discord") .tooltip("Join Discord")
.icon(icon) .icon(icon)
.onClick(() -> LinkBrowser.browse(properties.getDiscordInvite())) .onClick(() -> LinkBrowser.browse(RuneLiteProperties.getDiscordInvite()))
.build(); .build();
clientToolbar.addNavigation(discordButton); clientToolbar.addNavigation(discordButton);

View File

@@ -58,16 +58,14 @@ class DiscordState
private final DiscordService discordService; private final DiscordService discordService;
private final DiscordConfig config; private final DiscordConfig config;
private PartyService party; private PartyService party;
private final RuneLiteProperties properties;
private DiscordPresence lastPresence; private DiscordPresence lastPresence;
@Inject @Inject
private DiscordState(final DiscordService discordService, final DiscordConfig config, final PartyService party, final RuneLiteProperties properties) private DiscordState(final DiscordService discordService, final DiscordConfig config, final PartyService party)
{ {
this.discordService = discordService; this.discordService = discordService;
this.config = config; this.config = config;
this.party = party; this.party = party;
this.properties = properties;
} }
/** /**
@@ -173,12 +171,12 @@ class DiscordState
} }
// Replace snapshot with + to make tooltip shorter (so it will span only 1 line) // Replace snapshot with + to make tooltip shorter (so it will span only 1 line)
final String versionShortHand = properties.getVersion().replace("-SNAPSHOT", "+"); final String versionShortHand = RuneLiteProperties.getVersion().replace("-SNAPSHOT", "+");
final DiscordPresence.DiscordPresenceBuilder presenceBuilder = DiscordPresence.builder() final DiscordPresence.DiscordPresenceBuilder presenceBuilder = DiscordPresence.builder()
.state(MoreObjects.firstNonNull(state, "")) .state(MoreObjects.firstNonNull(state, ""))
.details(MoreObjects.firstNonNull(details, "")) .details(MoreObjects.firstNonNull(details, ""))
.largeImageText(properties.getTitle() + " v" + versionShortHand) .largeImageText(RuneLiteProperties.getTitle() + " v" + versionShortHand)
.startTimestamp(event.getStart()) .startTimestamp(event.getStart())
.smallImageKey(imageKey) .smallImageKey(imageKey)
.partyMax(PARTY_MAX) .partyMax(PARTY_MAX)

View File

@@ -79,9 +79,6 @@ public class InfoPanel extends PluginPanel
@Nullable @Nullable
private Client client; private Client client;
@Inject
private RuneLiteProperties runeLiteProperties;
@Inject @Inject
private EventBus eventBus; private EventBus eventBus;
@@ -117,7 +114,7 @@ public class InfoPanel extends PluginPanel
final Font smallFont = FontManager.getRunescapeSmallFont(); final Font smallFont = FontManager.getRunescapeSmallFont();
JLabel version = new JLabel(htmlLabel("RuneLite version: ", runeLiteProperties.getVersion())); JLabel version = new JLabel(htmlLabel("RuneLite version: ", RuneLiteProperties.getVersion()));
version.setFont(smallFont); version.setFont(smallFont);
JLabel revision = new JLabel(); JLabel revision = new JLabel();
@@ -176,10 +173,10 @@ public class InfoPanel extends PluginPanel
} }
}); });
actionsContainer.add(buildLinkPanel(GITHUB_ICON, "Report an issue or", "make a suggestion", runeLiteProperties.getGithubLink())); actionsContainer.add(buildLinkPanel(GITHUB_ICON, "Report an issue or", "make a suggestion", RuneLiteProperties.getGithubLink()));
actionsContainer.add(buildLinkPanel(DISCORD_ICON, "Talk to us on our", "discord server", runeLiteProperties.getDiscordInvite())); actionsContainer.add(buildLinkPanel(DISCORD_ICON, "Talk to us on our", "discord server", RuneLiteProperties.getDiscordInvite()));
actionsContainer.add(buildLinkPanel(PATREON_ICON, "Become a patron to", "help support RuneLite", runeLiteProperties.getPatreonLink())); actionsContainer.add(buildLinkPanel(PATREON_ICON, "Become a patron to", "help support RuneLite", RuneLiteProperties.getPatreonLink()));
actionsContainer.add(buildLinkPanel(WIKI_ICON, "Information about", "RuneLite and plugins", runeLiteProperties.getWikiLink())); actionsContainer.add(buildLinkPanel(WIKI_ICON, "Information about", "RuneLite and plugins", RuneLiteProperties.getWikiLink()));
add(versionPanel, BorderLayout.NORTH); add(versionPanel, BorderLayout.NORTH);
add(actionsContainer, BorderLayout.CENTER); add(actionsContainer, BorderLayout.CENTER);

View File

@@ -34,6 +34,7 @@ import io.sigpipe.jbsdiff.InvalidHeaderException;
import io.sigpipe.jbsdiff.Patch; import io.sigpipe.jbsdiff.Patch;
import java.applet.Applet; import java.applet.Applet;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.FilterInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
@@ -48,11 +49,14 @@ import java.util.Map;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.jar.JarEntry; import java.util.jar.JarEntry;
import java.util.jar.JarInputStream; import java.util.jar.JarInputStream;
import javax.swing.SwingUtilities;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client; import net.runelite.api.Client;
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;
import net.runelite.client.ui.FatalErrorDialog;
import net.runelite.client.ui.SplashScreen;
import net.runelite.http.api.RuneLiteAPI; import net.runelite.http.api.RuneLiteAPI;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
@@ -62,7 +66,7 @@ import org.apache.commons.compress.compressors.CompressorException;
public class ClientLoader implements Supplier<Applet> public class ClientLoader implements Supplier<Applet>
{ {
private ClientUpdateCheckMode updateCheckMode; private ClientUpdateCheckMode updateCheckMode;
private Applet client = null; private Object client = null;
public ClientLoader(ClientUpdateCheckMode updateCheckMode) public ClientLoader(ClientUpdateCheckMode updateCheckMode)
{ {
@@ -76,10 +80,15 @@ public class ClientLoader implements Supplier<Applet>
{ {
client = doLoad(); 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) if (updateCheckMode == NONE)
{ {
@@ -88,6 +97,7 @@ public class ClientLoader implements Supplier<Applet>
try try
{ {
SplashScreen.stage(0, null, "Fetching applet viewer config");
RSConfig config = ClientConfigLoader.fetch(); RSConfig config = ClientConfigLoader.fetch();
Map<String, byte[]> zipFile = new HashMap<>(); Map<String, byte[]> zipFile = new HashMap<>();
@@ -102,7 +112,26 @@ public class ClientLoader implements Supplier<Applet>
try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute()) 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]; byte[] tmp = new byte[4096];
ByteArrayOutputStream buffer = new ByteArrayOutputStream(756 * 1024); ByteArrayOutputStream buffer = new ByteArrayOutputStream(756 * 1024);
@@ -146,9 +175,19 @@ public class ClientLoader implements Supplier<Applet>
if (updateCheckMode == AUTO) if (updateCheckMode == AUTO)
{ {
SplashScreen.stage(.35, null, "Patching");
Map<String, String> hashes; Map<String, String> hashes;
try (InputStream is = ClientLoader.class.getResourceAsStream("/patch/hashes.json")) 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>>() hashes = new Gson().fromJson(new InputStreamReader(is), new TypeToken<HashMap<String, String>>()
{ {
}.getType()); }.getType());
@@ -197,11 +236,14 @@ public class ClientLoader implements Supplier<Applet>
file.setValue(patchOs.toByteArray()); file.setValue(patchOs.toByteArray());
++patchCount; ++patchCount;
SplashScreen.stage(.38, .45, null, "Patching", patchCount, zipFile.size(), false);
} }
log.debug("Patched {} classes", patchCount); log.debug("Patched {} classes", patchCount);
} }
SplashScreen.stage(.465, "Starting", "Starting Old School RuneScape");
String initialClass = config.getInitialClass(); String initialClass = config.getInitialClass();
ClassLoader rsClassLoader = new ClassLoader(ClientLoader.class.getClassLoader()) ClassLoader rsClassLoader = new ClassLoader(ClientLoader.class.getClassLoader())
@@ -230,21 +272,18 @@ public class ClientLoader implements Supplier<Applet>
log.info("client-patch {}", ((Client) rs).getBuildID()); log.info("client-patch {}", ((Client) rs).getBuildID());
} }
SplashScreen.stage(.5, null, "Starting core classes");
return rs; return rs;
} }
catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException
| CompressorException | InvalidHeaderException | CertificateException | VerificationException | CompressorException | InvalidHeaderException | CertificateException | VerificationException
| SecurityException e) | 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); log.error("Error loading RS!", e);
return null;
SwingUtilities.invokeLater(() -> FatalErrorDialog.showNetErrorWindow("loading the client", e));
return e;
} }
} }

View File

@@ -24,7 +24,7 @@
*/ */
package net.runelite.client.rs; package net.runelite.client.rs;
class VerificationException extends Exception public class VerificationException extends Exception
{ {
public VerificationException(String message) public VerificationException(String message)
{ {

View File

@@ -53,7 +53,6 @@ import javax.swing.JButton;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JFrame; import javax.swing.JFrame;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import static javax.swing.JOptionPane.ERROR_MESSAGE;
import static javax.swing.JOptionPane.INFORMATION_MESSAGE; import static javax.swing.JOptionPane.INFORMATION_MESSAGE;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JRootPane; import javax.swing.JRootPane;
@@ -111,7 +110,6 @@ public class ClientUI
@Getter @Getter
private TrayIcon trayIcon; private TrayIcon trayIcon;
private final RuneLiteProperties properties;
private final RuneLiteConfig config; private final RuneLiteConfig config;
private final KeyManager keyManager; private final KeyManager keyManager;
private final MouseManager mouseManager; private final MouseManager mouseManager;
@@ -138,7 +136,6 @@ public class ClientUI
@Inject @Inject
private ClientUI( private ClientUI(
RuneLiteProperties properties,
RuneLiteConfig config, RuneLiteConfig config,
KeyManager keyManager, KeyManager keyManager,
MouseManager mouseManager, MouseManager mouseManager,
@@ -146,7 +143,6 @@ public class ClientUI
ConfigManager configManager, ConfigManager configManager,
Provider<ClientThread> clientThreadProvider) Provider<ClientThread> clientThreadProvider)
{ {
this.properties = properties;
this.config = config; this.config = config;
this.keyManager = keyManager; this.keyManager = keyManager;
this.mouseManager = mouseManager; this.mouseManager = mouseManager;
@@ -261,7 +257,7 @@ public class ClientUI
return; return;
} }
final Client client = (Client)this.client; final Client client = (Client) this.client;
final ClientThread clientThread = clientThreadProvider.get(); final ClientThread clientThread = clientThreadProvider.get();
// Keep scheduling event until we get our name // Keep scheduling event until we get our name
@@ -286,18 +282,17 @@ public class ClientUI
return false; return false;
} }
frame.setTitle(properties.getTitle() + " - " + name); frame.setTitle(RuneLiteProperties.getTitle() + " - " + name);
return true; return true;
}); });
} }
/** /**
* Initialize UI. * Initialize UI.
*
* @param runelite runelite instance that will be shut down on exit * @param runelite runelite instance that will be shut down on exit
* @throws Exception exception that can occur during creation of the UI * @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(() -> SwingUtilities.invokeAndWait(() ->
{ {
@@ -316,7 +311,7 @@ public class ClientUI
// Try to enable fullscreen on OSX // Try to enable fullscreen on OSX
OSXUtil.tryEnableFullscreen(frame); OSXUtil.tryEnableFullscreen(frame);
frame.setTitle(properties.getTitle()); frame.setTitle(RuneLiteProperties.getTitle());
frame.setIconImage(ICON); frame.setIconImage(ICON);
frame.getLayeredPane().setCursor(Cursor.getDefaultCursor()); // Prevent substance from using a resize cursor for pointing frame.getLayeredPane().setCursor(Cursor.getDefaultCursor()); // Prevent substance from using a resize cursor for pointing
frame.setLocationRelativeTo(frame.getOwner()); frame.setLocationRelativeTo(frame.getOwner());
@@ -453,13 +448,19 @@ public class ClientUI
titleToolbar.addComponent(sidebarNavigationButton, sidebarNavigationJButton); titleToolbar.addComponent(sidebarNavigationButton, sidebarNavigationJButton);
toggleSidebar(); toggleSidebar();
});
}
public void show()
{
SwingUtilities.invokeLater(() ->
{
// Layout frame // Layout frame
frame.pack(); frame.pack();
frame.revalidateMinimumSize(); frame.revalidateMinimumSize();
// Create tray icon (needs to be created after frame is packed) // Create tray icon (needs to be created after frame is packed)
trayIcon = SwingUtil.createTrayIcon(ICON, properties.getTitle(), frame); trayIcon = SwingUtil.createTrayIcon(ICON, RuneLiteProperties.getTitle(), frame);
// Move frame around (needs to be done after frame is packed) // Move frame around (needs to be done after frame is packed)
if (config.rememberScreenBounds()) if (config.rememberScreenBounds())
@@ -516,14 +517,7 @@ public class ClientUI
}); });
// Show out of date dialog if needed // Show out of date dialog if needed
if (client == null) if (client != null && !(client instanceof Client))
{
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))
{ {
SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(frame, SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(frame,
"RuneLite has not yet been updated to work with the latest\n" "RuneLite has not yet been updated to work with the latest\n"
@@ -603,10 +597,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 * FIXME: This is working properly only on Windows, Linux and Mac are displaying cursor incorrectly
* @param image cursor image * @param image cursor image
* @param name cursor name * @param name cursor name
*/ */
public void setCursor(final BufferedImage image, final String name) public void setCursor(final BufferedImage image, final String name)
{ {
@@ -839,12 +833,12 @@ public class ClientUI
if (player != null && player.getName() != null) if (player != null && player.getName() != null)
{ {
frame.setTitle(properties.getTitle() + " - " + player.getName()); frame.setTitle(RuneLiteProperties.getTitle() + " - " + player.getName());
} }
} }
else else
{ {
frame.setTitle(properties.getTitle()); frame.setTitle(RuneLiteProperties.getTitle());
} }
if (frame.isAlwaysOnTopSupported()) if (frame.isAlwaysOnTopSupported())

View File

@@ -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();
}
}

View File

@@ -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;
}
}
}

View File

@@ -6,3 +6,6 @@ runelite.discord.invite=https://discord.gg/R4BQ8tU
runelite.github.link=https://github.com/runelite runelite.github.link=https://github.com/runelite
runelite.wiki.link=https://github.com/runelite/runelite/wiki 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/

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB