diff --git a/runelite-client/src/main/java/net/runelite/client/ClientSessionManager.java b/runelite-client/src/main/java/net/runelite/client/ClientSessionManager.java index 1500082b6b..7c696792ab 100644 --- a/runelite-client/src/main/java/net/runelite/client/ClientSessionManager.java +++ b/runelite-client/src/main/java/net/runelite/client/ClientSessionManager.java @@ -24,88 +24,114 @@ */ package net.runelite.client; -import io.reactivex.rxjava3.schedulers.Schedulers; -import java.time.temporal.ChronoUnit; +import java.io.IOException; import java.util.UUID; -import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import javax.annotation.Nullable; import javax.inject.Inject; import javax.inject.Singleton; import lombok.extern.slf4j.Slf4j; -import net.runelite.client.eventbus.EventBus; +import net.runelite.api.Client; +import net.runelite.api.GameState; +import net.runelite.client.eventbus.Subscribe; import net.runelite.client.events.ClientShutdown; -import net.runelite.client.task.Schedule; +import net.runelite.client.util.RunnableExceptionLogger; import okhttp3.OkHttpClient; @Singleton @Slf4j public class ClientSessionManager { + private final ScheduledExecutorService executorService; + private final Client client; private final SessionClient sessionClient; + + private ScheduledFuture scheduledFuture; private UUID sessionId; - @Inject - ClientSessionManager(EventBus eventBus, + ClientSessionManager(ScheduledExecutorService executorService, + @Nullable Client client, OkHttpClient okHttpClient) { + this.executorService = executorService; + this.client = client; this.sessionClient = new SessionClient(okHttpClient); + } - eventBus.subscribe(ClientShutdown.class, this, (e) -> + public void start() + { + try { - Future f = shutdown(); - if (f != null) + sessionId = sessionClient.open(); + log.debug("Opened session {}", sessionId); + } + catch (IOException ex) + { + log.warn("error opening session", ex); + } + + scheduledFuture = executorService.scheduleWithFixedDelay(RunnableExceptionLogger.wrap(this::ping), 1, 10, TimeUnit.MINUTES); + } + + @Subscribe + private void onClientShutdown(ClientShutdown e) + { + scheduledFuture.cancel(true); + + e.waitFor(executorService.submit(() -> + { + try { - e.waitFor(f); + UUID localUuid = sessionId; + if (localUuid != null) + { + sessionClient.delete(localUuid); + } + } + catch (IOException ex) + { + log.warn(null, ex); } sessionId = null; - }); + })); } - void start() + private void ping() { - sessionClient.openSession() - .subscribeOn(Schedulers.io()) - .observeOn(Schedulers.single()) - .doOnError(this::error) - .subscribe(this::setUuid); - } - - @Schedule(period = 10, unit = ChronoUnit.MINUTES, asynchronous = true) - public void ping() - { - if (sessionId == null) + try { - start(); + if (sessionId == null) + { + sessionId = sessionClient.open(); + log.debug("Opened session {}", sessionId); + return; + } + } + catch (IOException ex) + { + log.warn("unable to open session", ex); return; } - sessionClient.pingSession(sessionId) - .subscribeOn(Schedulers.io()) - .observeOn(Schedulers.single()) - .doOnError(this::error) - .subscribe(); - } - - private Future shutdown() - { - if (sessionId != null) + boolean loggedIn = false; + if (client != null) { - return sessionClient.delete(sessionId) - .toFuture(); - + GameState gameState = client.getGameState(); + loggedIn = gameState.getState() >= GameState.LOADING.getState(); } - return null; - } - private void setUuid(UUID uuid) - { - this.sessionId = uuid; - log.debug("Opened session {}.", sessionId); - } + try + { + sessionClient.ping(sessionId, loggedIn); + } + catch (IOException ex) + { + log.warn("Resetting session", ex); + sessionId = null; + } - private void error(Throwable error) - { - log.debug("Error in client session."); - log.trace(null, error); } -} \ No newline at end of file +} diff --git a/runelite-client/src/main/java/net/runelite/client/Notifier.java b/runelite-client/src/main/java/net/runelite/client/Notifier.java index 70eabdac0d..872866c415 100644 --- a/runelite-client/src/main/java/net/runelite/client/Notifier.java +++ b/runelite-client/src/main/java/net/runelite/client/Notifier.java @@ -24,6 +24,7 @@ */ package net.runelite.client; +import com.google.common.base.Strings; import com.google.common.escape.Escaper; import com.google.common.escape.Escapers; import com.google.inject.Inject; @@ -113,7 +114,7 @@ public class Notifier private final ChatMessageManager chatMessageManager; private final EventBus eventBus; private final Path notifyIconPath; - private final boolean terminalNotifierAvailable; + private boolean terminalNotifierAvailable; private Instant flashStart; private long mouseLastPressedMillis; private long lastClipMTime = CLIP_MTIME_UNLOADED; @@ -137,7 +138,10 @@ public class Notifier this.notifyIconPath = RuneLite.RUNELITE_DIR.toPath().resolve("icon.png"); // First check if we are running in launcher - this.terminalNotifierAvailable = true; + if (!Strings.isNullOrEmpty(RuneLiteProperties.getLauncherVersion()) && OSType.getOSType() == OSType.MacOS) + { + executorService.execute(() -> terminalNotifierAvailable = isTerminalNotifierAvailable()); + } storeIcon(); } @@ -149,7 +153,7 @@ public class Notifier public void notify(String message, TrayIcon.MessageType type) { - eventBus.post(NotificationFired.class, new NotificationFired(message, type)); + eventBus.post(new NotificationFired(message, type)); if (!runeLiteConfig.sendNotificationsWhenFocused() && clientUI.isFocused()) { @@ -364,7 +368,7 @@ public class Notifier private static Process sendCommand(final List commands) throws IOException { - return new ProcessBuilder(commands.toArray(new String[0])) + return new ProcessBuilder(commands.toArray(new String[commands.size()])) .redirectErrorStream(true) .start(); } @@ -373,7 +377,7 @@ public class Notifier { if (OSType.getOSType() == OSType.Linux && !Files.exists(notifyIconPath)) { - try (InputStream stream = Notifier.class.getResourceAsStream("/openosrs.png")) + try (InputStream stream = Notifier.class.getResourceAsStream("/runelite.png")) { Files.copy(stream, notifyIconPath); } @@ -386,21 +390,19 @@ public class Notifier private boolean isTerminalNotifierAvailable() { - if (OSType.getOSType() == OSType.MacOS) + try { - try - { - final Process exec = Runtime.getRuntime().exec(new String[]{"terminal-notifier", "-help"}); - exec.waitFor(); - return exec.exitValue() == 0; - } - catch (IOException | InterruptedException e) + final Process exec = Runtime.getRuntime().exec(new String[]{"terminal-notifier", "-help"}); + if (!exec.waitFor(2, TimeUnit.SECONDS)) { return false; } + return exec.exitValue() == 0; + } + catch (IOException | InterruptedException e) + { + return false; } - - return false; } private static String toUrgency(TrayIcon.MessageType type) 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 109ec10d71..7418be6c5e 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLite.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLite.java @@ -26,41 +26,27 @@ package net.runelite.client; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; -import com.github.zafarkhaja.semver.Version; import com.google.common.annotations.VisibleForTesting; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; -import io.reactivex.rxjava3.core.Completable; -import io.reactivex.rxjava3.schedulers.Schedulers; import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStreamReader; import java.lang.management.ManagementFactory; import java.lang.management.RuntimeMXBean; -import java.net.Authenticator; -import java.net.PasswordAuthentication; -import java.nio.charset.StandardCharsets; import java.nio.file.Paths; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.cert.X509Certificate; import java.util.Locale; -import java.util.Map; -import java.util.Optional; -import java.util.Properties; -import java.util.UUID; import javax.annotation.Nullable; import javax.inject.Provider; import javax.inject.Singleton; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; +import javax.swing.SwingUtilities; import joptsimple.ArgumentAcceptingOptionSpec; -import joptsimple.OptionException; import joptsimple.OptionParser; import joptsimple.OptionSet; import joptsimple.ValueConversionException; @@ -69,49 +55,34 @@ import joptsimple.util.EnumConverter; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; -import net.runelite.api.events.GameStateChanged; -import net.runelite.api.events.ScriptCallbackEvent; import net.runelite.client.account.SessionManager; import net.runelite.client.callback.Hooks; import net.runelite.client.chat.ChatMessageManager; import net.runelite.client.chat.CommandManager; import net.runelite.client.config.ConfigManager; -import net.runelite.client.config.LauncherConfig; -import net.runelite.client.config.OpenOSRSConfig; import net.runelite.client.discord.DiscordService; import net.runelite.client.eventbus.EventBus; -import net.runelite.client.events.ExternalPluginsLoaded; +import net.runelite.client.externalplugins.ExternalPluginManager; import net.runelite.client.game.FriendChatManager; import net.runelite.client.game.ItemManager; import net.runelite.client.game.LootManager; -import net.runelite.client.game.PlayerManager; -import net.runelite.client.game.WorldService; -import net.runelite.client.game.XpDropManager; import net.runelite.client.game.chatbox.ChatboxPanelManager; -import net.runelite.client.graphics.ModelOutlineRenderer; import net.runelite.client.menus.MenuManager; -import net.runelite.client.plugins.ExternalPluginManager; import net.runelite.client.plugins.PluginManager; import net.runelite.client.rs.ClientLoader; import net.runelite.client.rs.ClientUpdateCheckMode; -import net.runelite.client.task.Scheduler; import net.runelite.client.ui.ClientUI; -import net.runelite.client.ui.RuneLiteSplashScreen; +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; import net.runelite.client.ui.overlay.WidgetOverlay; -import net.runelite.client.ui.overlay.arrow.ArrowMinimapOverlay; -import net.runelite.client.ui.overlay.arrow.ArrowWorldOverlay; import net.runelite.client.ui.overlay.infobox.InfoBoxManager; -import net.runelite.client.ui.overlay.tooltip.TooltipManager; import net.runelite.client.ui.overlay.tooltip.TooltipOverlay; import net.runelite.client.ui.overlay.worldmap.WorldMapOverlay; -import net.runelite.client.util.Groups; -import net.runelite.client.util.WorldUtil; import net.runelite.client.ws.PartyService; import net.runelite.http.api.RuneLiteAPI; -import net.runelite.http.api.worlds.World; -import net.runelite.http.api.worlds.WorldResult; import okhttp3.Cache; import okhttp3.OkHttpClient; import org.slf4j.LoggerFactory; @@ -120,42 +91,41 @@ import org.slf4j.LoggerFactory; @Slf4j public class RuneLite { - public static final String SYSTEM_VERSION = "0.0.1"; - public static final File RUNELITE_DIR = new File(System.getProperty("user.home"), ".runelite"); public static final File CACHE_DIR = new File(RUNELITE_DIR, "cache"); + public static final File PLUGINS_DIR = new File(RUNELITE_DIR, "plugins"); public static final File PROFILES_DIR = new File(RUNELITE_DIR, "profiles"); - public static final File EXTERNALPLUGIN_DIR = new File(RUNELITE_DIR, "externalmanager"); public static final File SCREENSHOT_DIR = new File(RUNELITE_DIR, "screenshots"); public static final File LOGS_DIR = new File(RUNELITE_DIR, "logs"); - public static final File DEFAULT_CONFIG_FILE = new File(RUNELITE_DIR, "runeliteplus.properties"); - public static final Locale SYSTEM_LOCALE = Locale.getDefault(); - public static boolean allowPrivateServer = false; - public static String uuid = UUID.randomUUID().toString(); + public static final File DEFAULT_SESSION_FILE = new File(RUNELITE_DIR, "session"); + public static final File DEFAULT_CONFIG_FILE = new File(RUNELITE_DIR, "settings.properties"); private static final int MAX_OKHTTP_CACHE_SIZE = 20 * 1024 * 1024; // 20mb @Getter private static Injector injector; - @Inject - public DiscordService discordService; - - @Inject - private WorldService worldService; - @Inject private PluginManager pluginManager; @Inject private ExternalPluginManager externalPluginManager; + @Inject + private EventBus eventBus; + @Inject private ConfigManager configManager; + @Inject + private DrawManager drawManager; + @Inject private SessionManager sessionManager; + @Inject + private DiscordService discordService; + @Inject private ClientSessionManager clientSessionManager; @@ -163,10 +133,10 @@ public class RuneLite private ClientUI clientUI; @Inject - private OverlayManager overlayManager; + private Provider infoBoxManager; @Inject - private TooltipManager tooltipManager; + private OverlayManager overlayManager; @Inject private Provider partyService; @@ -178,7 +148,7 @@ public class RuneLite private Provider overlayRenderer; @Inject - private Provider friendChatManager; + private Provider friendsChatManager; @Inject private Provider chatMessageManager; @@ -189,58 +159,25 @@ public class RuneLite @Inject private Provider commandManager; - @Inject - private Provider infoBoxManager; - @Inject private Provider tooltipOverlay; @Inject private Provider worldMapOverlay; - @Inject - private Provider arrowWorldOverlay; - - @Inject - private Provider arrowMinimapOverlay; - @Inject private Provider lootManager; - @Inject - private Provider xpDropManager; - - @Inject - private Provider playerManager; - @Inject private Provider chatboxPanelManager; @Inject - private Groups groups; - - @Inject - private Hooks hooks; - - @Inject - private EventBus eventBus; + private Provider hooks; @Inject @Nullable private Client client; - @Inject - private OpenOSRSConfig openOSRSConfig; - - @Inject - private LauncherConfig launcherConfig; - - @Inject - private Provider modelOutlineRenderer; - - @Inject - private Scheduler scheduler; - public static void main(String[] args) throws Exception { Locale.setDefault(Locale.ENGLISH); @@ -249,15 +186,13 @@ public class RuneLite parser.accepts("developer-mode", "Enable developer tools"); parser.accepts("debug", "Show extra debugging output"); parser.accepts("safe-mode", "Disables external plugins and the GPU plugin"); - parser.accepts("no-splash", "Do not show the splash screen"); parser.accepts("insecure-skip-tls-verification", "Disables TLS verification"); - final ArgumentAcceptingOptionSpec proxyInfo = parser - .accepts("proxy") - .withRequiredArg().ofType(String.class); - final ArgumentAcceptingOptionSpec worldInfo = parser - .accepts("world") - .withRequiredArg().ofType(Integer.class); + final ArgumentAcceptingOptionSpec sessionfile = parser.accepts("sessionfile", "Use a specified session file") + .withRequiredArg() + .withValuesConvertedBy(new ConfigFileConverter()) + .defaultsTo(DEFAULT_SESSION_FILE); + final ArgumentAcceptingOptionSpec configfile = parser.accepts("config", "Use a specified config file") .withRequiredArg() .withValuesConvertedBy(new ConfigFileConverter()) @@ -268,7 +203,7 @@ public class RuneLite .withRequiredArg() .ofType(ClientUpdateCheckMode.class) .defaultsTo(ClientUpdateCheckMode.AUTO) - .withValuesConvertedBy(new EnumConverter<>(ClientUpdateCheckMode.class) + .withValuesConvertedBy(new EnumConverter(ClientUpdateCheckMode.class) { @Override public ClientUpdateCheckMode convert(String v) @@ -278,18 +213,7 @@ public class RuneLite }); parser.accepts("help", "Show this text").forHelp(); - - OptionSet options = parser.parse(""); - - try - { - options = parser.parse(args); - } - catch (OptionException e) - { - log.warn("Error parsing launch args: {}", e.getMessage()); - log.warn("Proceeding with no arguments."); - } + OptionSet options = parser.parse(args); if (options.has("help")) { @@ -303,147 +227,92 @@ public class RuneLite logger.setLevel(Level.DEBUG); } - if (options.has("proxy")) + Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> { - String[] proxy = options.valueOf(proxyInfo).split(":"); - - if (proxy.length >= 2) + log.error("Uncaught exception:", throwable); + if (throwable instanceof AbstractMethodError) { - System.setProperty("socksProxyHost", proxy[0]); - System.setProperty("socksProxyPort", proxy[1]); + log.error("Classes are out of date; Build with maven again."); } + }); - if (proxy.length >= 4) - { - System.setProperty("java.net.socks.username", proxy[2]); - System.setProperty("java.net.socks.password", proxy[3]); - - final String user = proxy[2]; - final char[] pass = proxy[3].toCharArray(); - - Authenticator.setDefault(new Authenticator() - { - private final PasswordAuthentication auth = new PasswordAuthentication(user, pass); - - protected PasswordAuthentication getPasswordAuthentication() - { - return auth; - } - }); - } - } - - if (options.has("world")) - { - int world = options.valueOf(worldInfo); - System.setProperty("cli.world", String.valueOf(world)); - } - - final File configFile = resolveLinks(options.valueOf(configfile)); - Properties properties = new Properties(); - try (FileInputStream in = new FileInputStream(configFile)) - { - properties.load(new InputStreamReader(in, StandardCharsets.UTF_8)); - try - { - @SuppressWarnings("unchecked") Map copy = (Map) Map.copyOf(properties); - copy.forEach((groupAndKey, value) -> - { - final String[] split = groupAndKey.split("\\.", 2); - final String groupName = split[0]; - final String key = split[1]; - - if (!groupName.equals("openosrs")) - { - return; - } - - if (key.equals("disableHw") && value.equals("true")) - { - log.info("Disabling HW Accel"); - System.setProperty("sun.java2d.noddraw", "true"); - } - }); - } - catch (Exception ex) - { - log.error("Unexpected error", ex); - } - } - catch (FileNotFoundException ex) - { - log.error("Unable to load settings - no such file"); - } - catch (IllegalArgumentException | IOException ex) - { - log.error("Unable to load settings", ex); - } - - final OkHttpClient.Builder okHttpClientBuilder = RuneLiteAPI.CLIENT.newBuilder() + OkHttpClient.Builder okHttpClientBuilder = RuneLiteAPI.CLIENT.newBuilder() .cache(new Cache(new File(CACHE_DIR, "okhttp"), MAX_OKHTTP_CACHE_SIZE)); final boolean insecureSkipTlsVerification = options.has("insecure-skip-tls-verification"); - if (insecureSkipTlsVerification) + if (insecureSkipTlsVerification || RuneLiteProperties.isInsecureSkipTlsVerification()) { setupInsecureTrustManager(okHttpClientBuilder); } final OkHttpClient okHttpClient = okHttpClientBuilder.build(); - final ClientLoader clientLoader = new ClientLoader(okHttpClient, options.valueOf(updateMode)); - Completable.fromAction(clientLoader::get) - .subscribeOn(Schedulers.computation()) - .subscribe(); + SplashScreen.init(); + SplashScreen.stage(0, "Retrieving client", ""); - Completable.fromAction(ClassPreloader::preload) - .subscribeOn(Schedulers.computation()) - .subscribe(); - - if (!options.has("no-splash")) + try { - RuneLiteSplashScreen.init(); - } + final ClientLoader clientLoader = new ClientLoader(okHttpClient, options.valueOf(updateMode)); - Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> - { - log.error("Uncaught exception:", throwable); - if (throwable instanceof AbstractMethodError) + new Thread(() -> { - RuneLiteSplashScreen.setError("Out of date!", "Classes are out of date; Build with Gradle again."); - return; + 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; + } } - RuneLiteSplashScreen.setError("Error while loading!", "Please check your internet connection and your DNS settings."); - }); + PROFILES_DIR.mkdirs(); - PROFILES_DIR.mkdirs(); + log.info("RuneLite {} (launcher version {}) starting up, args: {}", + RuneLiteProperties.getVersion(), RuneLiteProperties.getLauncherVersion() == null ? "unknown" : RuneLiteProperties.getLauncherVersion(), + args.length == 0 ? "none" : String.join(" ", args)); - log.info("OpenOSRS {} Runelite {} (launcher version {}) starting up, args: {}", - RuneLiteProperties.getPlusVersion(), RuneLiteProperties.getVersion(), RuneLiteProperties.getLauncherVersion() == null ? "unknown" : RuneLiteProperties.getLauncherVersion(), - args.length == 0 ? "none" : String.join(" ", args)); + final long start = System.currentTimeMillis(); - final long start = System.currentTimeMillis(); + injector = Guice.createInjector(new RuneLiteModule( + okHttpClient, + clientLoader, + developerMode, + options.has("safe-mode"), + options.valueOf(sessionfile), + options.valueOf(configfile))); - injector = Guice.createInjector(new RuneLiteModule( - okHttpClient, - clientLoader, - options.has("safe-mode"), - configFile)); + injector.getInstance(RuneLite.class).start(); - 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); + 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.error("Failure during startup", e); + SwingUtilities.invokeLater(() -> + new FatalErrorDialog("RuneLite has encountered an unexpected error during startup.") + .open()); + } + finally + { + SplashScreen.stop(); + } } - @VisibleForTesting - public static void setInjector(Injector injector) - { - RuneLite.injector = injector; - } - - private void start() throws Exception + public void start() throws Exception { // Load RuneLite or Vanilla client final boolean isOutdated = client == null; @@ -454,181 +323,84 @@ public class RuneLite injector.injectMembers(client); } - if (RuneLiteProperties.getLauncherVersion() == null || !openOSRSConfig.shareLogs()) - { - final Logger logger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); - logger.detachAppender("Sentry"); - } + SplashScreen.stage(.57, null, "Loading configuration"); // Load user configuration - RuneLiteSplashScreen.stage(.57, "Loading user config"); configManager.load(); - parseLauncherConfig(); - // Load the session, including saved configuration - RuneLiteSplashScreen.stage(.58, "Loading session data"); sessionManager.loadSession(); // Tell the plugin manager if client is outdated or not pluginManager.setOutdated(isOutdated); - // Load external plugin manager - externalPluginManager.startExternalUpdateManager(); - externalPluginManager.startExternalPluginManager(); - - // Update external plugins - externalPluginManager.update(); - // Load the plugins, but does not start them yet. // This will initialize configuration pluginManager.loadCorePlugins(); - externalPluginManager.loadPlugins(); + externalPluginManager.loadExternalPlugins(); - RuneLiteSplashScreen.stage(.76, "Finalizing configuration"); + SplashScreen.stage(.70, null, "Finalizing configuration"); // Plugins have provided their config, so set default config // to main settings - pluginManager.loadDefaultPluginConfiguration(); + pluginManager.loadDefaultPluginConfiguration(null); // Start client session - RuneLiteSplashScreen.stage(.77, "Starting core interface"); clientSessionManager.start(); + eventBus.register(clientSessionManager); - //Set the world if specified via CLI args - will not work until clientUI.init is called - Optional worldArg = Optional.ofNullable(System.getProperty("cli.world")).map(Integer::parseInt); - worldArg.ifPresent(this::setWorld); + SplashScreen.stage(.75, null, "Starting core interface"); // Initialize UI - RuneLiteSplashScreen.stage(.80, "Initialize UI"); clientUI.init(); // Initialize Discord service discordService.init(); + // Register event listeners + eventBus.register(clientUI); + eventBus.register(pluginManager); + eventBus.register(externalPluginManager); + eventBus.register(overlayManager); + eventBus.register(drawManager); + eventBus.register(configManager); + eventBus.register(discordService); + if (!isOutdated) { // Initialize chat colors chatMessageManager.get().loadColors(); - infoBoxManager.get(); - overlayRenderer.get(); - friendChatManager.get(); - itemManager.get(); - menuManager.get(); - chatMessageManager.get(); - commandManager.get(); - lootManager.get(); - xpDropManager.get(); - playerManager.get(); - chatboxPanelManager.get(); - partyService.get(); - - eventBus.subscribe(GameStateChanged.class, this, hooks::onGameStateChanged); - eventBus.subscribe(ScriptCallbackEvent.class, this, hooks::onScriptCallbackEvent); + eventBus.register(infoBoxManager.get()); + eventBus.register(partyService.get()); + eventBus.register(overlayRenderer.get()); + eventBus.register(friendsChatManager.get()); + eventBus.register(itemManager.get()); + eventBus.register(menuManager.get()); + eventBus.register(chatMessageManager.get()); + eventBus.register(commandManager.get()); + eventBus.register(lootManager.get()); + eventBus.register(chatboxPanelManager.get()); + eventBus.register(hooks.get()); // Add core overlays WidgetOverlay.createOverlays(client).forEach(overlayManager::add); overlayManager.add(worldMapOverlay.get()); overlayManager.add(tooltipOverlay.get()); - overlayManager.add(arrowWorldOverlay.get()); - overlayManager.add(arrowMinimapOverlay.get()); } // Start plugins pluginManager.startPlugins(); - eventBus.post(ExternalPluginsLoaded.class, new ExternalPluginsLoaded()); - // Register additional schedulers - if (this.client != null) - { - scheduler.registerObject(modelOutlineRenderer.get()); - scheduler.registerObject(clientSessionManager); - } - - // Close the splash screen - RuneLiteSplashScreen.close(); + SplashScreen.stop(); clientUI.show(); } - private void setWorld(int cliWorld) + @VisibleForTesting + public static void setInjector(Injector injector) { - int correctedWorld = cliWorld < 300 ? cliWorld + 300 : cliWorld; - - if (correctedWorld <= 300 || client.getWorld() == correctedWorld) - { - return; - } - - final WorldResult worldResult = worldService.getWorlds(); - - if (worldResult == null) - { - log.warn("Failed to lookup worlds."); - return; - } - - final World world = worldResult.findWorld(correctedWorld); - - if (world != null) - { - final net.runelite.api.World rsWorld = client.createWorld(); - rsWorld.setActivity(world.getActivity()); - rsWorld.setAddress(world.getAddress()); - rsWorld.setId(world.getId()); - rsWorld.setPlayerCount(world.getPlayers()); - rsWorld.setLocation(world.getLocation()); - rsWorld.setTypes(WorldUtil.toWorldTypes(world.getTypes())); - - client.changeWorld(rsWorld); - log.debug("Applied new world {}", correctedWorld); - } - else - { - log.warn("World {} not found.", correctedWorld); - } - } - - private void parseLauncherConfig() - { - String launcherVersion = RuneLiteProperties.getLauncherVersion(); - - if (launcherVersion == null || !Version.valueOf(launcherVersion).greaterThanOrEqualTo(Version.valueOf("2.2.0"))) - { - return; - } - - if (launcherConfig.useProxy()) - { - log.info("Setting proxy."); - String[] proxy = launcherConfig.proxyDetails().split(":"); - - if (proxy.length >= 2) - { - System.setProperty("socksProxyHost", proxy[0]); - System.setProperty("socksProxyPort", proxy[1]); - } - - if (proxy.length >= 4) - { - System.setProperty("java.net.socks.username", proxy[2]); - System.setProperty("java.net.socks.password", proxy[3]); - - final String user = proxy[2]; - final char[] pass = proxy[3].toCharArray(); - - Authenticator.setDefault(new Authenticator() - { - private final PasswordAuthentication auth = new PasswordAuthentication(user, pass); - - protected PasswordAuthentication getPasswordAuthentication() - { - return auth; - } - }); - } - } + RuneLite.injector = injector; } private static class ConfigFileConverter implements ValueConverter @@ -670,18 +442,6 @@ public class RuneLite } } - private static File resolveLinks(File f) - { - try - { - return f.toPath().toRealPath().toFile(); - } - catch (IOException e) - { - return f; - } - } - private static void setupInsecureTrustManager(OkHttpClient.Builder okHttpClientBuilder) { try diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java b/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java index 04fecbb7cf..517a7f83e0 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java @@ -24,18 +24,13 @@ */ package net.runelite.client; -import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.inject.AbstractModule; import com.google.inject.Provides; import com.google.inject.name.Names; import java.applet.Applet; import java.io.File; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; import java.util.function.Supplier; import javax.annotation.Nullable; import javax.inject.Singleton; @@ -47,8 +42,6 @@ import net.runelite.client.callback.Hooks; import net.runelite.client.chat.ChatMessageManager; import net.runelite.client.config.ChatColorConfig; import net.runelite.client.config.ConfigManager; -import net.runelite.client.config.LauncherConfig; -import net.runelite.client.config.OpenOSRSConfig; import net.runelite.client.config.RuneLiteConfig; import net.runelite.client.eventbus.EventBus; import net.runelite.client.game.ItemManager; @@ -57,25 +50,27 @@ import net.runelite.client.plugins.PluginManager; import net.runelite.client.task.Scheduler; import net.runelite.client.util.DeferredEventBus; import net.runelite.client.util.ExecutorServiceExceptionLogger; -import net.runelite.client.util.NonScheduledExecutorServiceExceptionLogger; import net.runelite.http.api.chat.ChatClient; import okhttp3.OkHttpClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; @AllArgsConstructor public class RuneLiteModule extends AbstractModule { private final OkHttpClient okHttpClient; private final Supplier clientLoader; + private final boolean developerMode; private final boolean safeMode; + private final File sessionfile; private final File config; @Override protected void configure() { + bindConstant().annotatedWith(Names.named("developerMode")).to(developerMode); bindConstant().annotatedWith(Names.named("safeMode")).to(safeMode); + bind(File.class).annotatedWith(Names.named("sessionfile")).toInstance(sessionfile); bind(File.class).annotatedWith(Names.named("config")).toInstance(config); + bind(ScheduledExecutorService.class).toInstance(new ExecutorServiceExceptionLogger(Executors.newSingleThreadScheduledExecutor())); bind(OkHttpClient.class).toInstance(okHttpClient); bind(MenuManager.class); bind(ChatMessageManager.class); @@ -92,10 +87,6 @@ public class RuneLiteModule extends AbstractModule bind(EventBus.class) .annotatedWith(Names.named("Deferred EventBus")) .to(DeferredEventBus.class); - - bind(Logger.class) - .annotatedWith(Names.named("Core Logger")) - .toInstance(LoggerFactory.getLogger(RuneLite.class)); } @Provides @@ -119,13 +110,6 @@ public class RuneLiteModule extends AbstractModule return configManager.getConfig(RuneLiteConfig.class); } - @Provides - @Singleton - OpenOSRSConfig providePlusConfig(ConfigManager configManager) - { - return configManager.getConfig(OpenOSRSConfig.class); - } - @Provides @Singleton ChatColorConfig provideChatColorConfig(ConfigManager configManager) @@ -133,39 +117,6 @@ public class RuneLiteModule extends AbstractModule return configManager.getConfig(ChatColorConfig.class); } - @Provides - @Singleton - LauncherConfig provideLauncherConfig(ConfigManager configManager) - { - return configManager.getConfig(LauncherConfig.class); - } - - @Provides - @Singleton - ScheduledExecutorService provideScheduledExecutorService() - { - return new ExecutorServiceExceptionLogger(Executors.newSingleThreadScheduledExecutor(new ThreadFactoryBuilder() - .setNameFormat("scheduled-%d") - .build())); - } - - @Provides - @Singleton - ExecutorService provideExecutorService() - { - int poolSize = 2 * Runtime.getRuntime().availableProcessors(); - - // Will start up to poolSize threads (because of allowCoreThreadTimeOut) as necessary, and times out - // unused threads after 1 minute - ThreadPoolExecutor executor = new ThreadPoolExecutor(poolSize, poolSize, - 60L, TimeUnit.SECONDS, - new LinkedBlockingQueue<>(), - new ThreadFactoryBuilder().setNameFormat("worker-%d").build()); - executor.allowCoreThreadTimeOut(true); - - return new NonScheduledExecutorServiceExceptionLogger(executor); - } - @Provides @Singleton ChatClient provideChatClient(OkHttpClient okHttpClient) diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java b/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java index b88f2b5729..ab0911c1f4 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java @@ -24,37 +24,38 @@ */ package net.runelite.client; -import com.google.common.base.Strings; import java.io.IOException; import java.io.InputStream; import java.util.Properties; import javax.annotation.Nullable; +import okhttp3.HttpUrl; public class RuneLiteProperties { - private static final String RUNELITE_TITLE = "open.osrs.title"; + private static final String RUNELITE_TITLE = "runelite.title"; private static final String RUNELITE_VERSION = "runelite.version"; - private static final String RUNELITE_PLUS_VERSION = "open.osrs.version"; - private static final String RUNELITE_PLUS_DATE = "open.osrs.builddate"; private static final String RUNESCAPE_VERSION = "runescape.version"; - private static final String DISCORD_APP_ID = "open.osrs.discord.appid"; + private static final String DISCORD_APP_ID = "runelite.discord.appid"; private static final String DISCORD_INVITE = "runelite.discord.invite"; private static final String GITHUB_LINK = "runelite.github.link"; 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 = "launcher.version"; - private static final String PLUGIN_PATH = "plugin.path"; - private static final String PLUGIN_DEVELOPMENT_PATH = "plugin.development.path"; + private static final String LAUNCHER_VERSION_PROPERTY = "runelite.launcher.version"; + private static final String INSECURE_SKIP_TLS_VERIFICATION_PROPERTY = "runelite.insecure-skip-tls-verification"; 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 String JAV_CONFIG = "runelite.jav_config"; + private static final String JAV_CONFIG_BACKUP = "runelite.jav_config_backup"; + private static final String PLUGINHUB_BASE = "runelite.pluginhub.url"; + private static final String PLUGINHUB_VERSION = "runelite.pluginhub.version"; private static final String IMGUR_CLIENT_ID = "runelite.imgur.client.id"; private static final Properties properties = new Properties(); static { - try (InputStream in = RuneLiteProperties.class.getResourceAsStream("open.osrs.properties")) + try (InputStream in = RuneLiteProperties.class.getResourceAsStream("runelite.properties")) { properties.load(in); } @@ -66,13 +67,7 @@ public class RuneLiteProperties public static String getTitle() { - final StringBuilder sb = new StringBuilder(properties.getProperty(RUNELITE_TITLE)); - String proxy; - if ((proxy = System.getProperty("socksProxyHost")) != null) - { - sb.append(String.format(" (%s)", proxy)); - } - return sb.toString(); + return properties.getProperty(RUNELITE_TITLE); } public static String getVersion() @@ -80,16 +75,6 @@ public class RuneLiteProperties return properties.getProperty(RUNELITE_VERSION); } - public static String getPlusVersion() - { - return properties.getProperty(RUNELITE_PLUS_VERSION); - } - - public static String getPlusDate() - { - return properties.getProperty(RUNELITE_PLUS_DATE); - } - public static String getRunescapeVersion() { return properties.getProperty(RUNESCAPE_VERSION); @@ -120,6 +105,17 @@ public class RuneLiteProperties return properties.getProperty(PATREON_LINK); } + @Nullable + public static String getLauncherVersion() + { + return System.getProperty(LAUNCHER_VERSION_PROPERTY); + } + + public static boolean isInsecureSkipTlsVerification() + { + return Boolean.getBoolean(INSECURE_SKIP_TLS_VERIFICATION_PROPERTY); + } + public static String getTroubleshootingLink() { return properties.getProperty(TROUBLESHOOTING_LINK); @@ -135,31 +131,20 @@ public class RuneLiteProperties return properties.getProperty(DNS_CHANGE_LINK); } - @Nullable - public static String getLauncherVersion() + public static String getJavConfig() { - return System.getProperty(LAUNCHER_VERSION_PROPERTY); + return properties.getProperty(JAV_CONFIG); } - @Nullable - public static String getPluginPath() + public static String getJavConfigBackup() { - String pluginPath = properties.getProperty(PLUGIN_PATH); - return pluginPath.equals("") ? null : pluginPath; + return properties.getProperty(JAV_CONFIG_BACKUP); } - public static String[] getPluginDevelopmentPath() + public static HttpUrl getPluginHubBase() { - // First check if property supplied as environment variable PLUGIN_DEVELOPMENT_PATHS - String developmentPluginPaths = System.getenv(PLUGIN_DEVELOPMENT_PATH.replace('.', '_').toUpperCase()); - - if (Strings.isNullOrEmpty(developmentPluginPaths)) - { - // Otherwise check the property file - developmentPluginPaths = properties.getProperty(PLUGIN_DEVELOPMENT_PATH); - } - - return Strings.isNullOrEmpty(developmentPluginPaths) ? new String[0] : developmentPluginPaths.split(";"); + String version = System.getProperty(PLUGINHUB_VERSION, properties.getProperty(PLUGINHUB_VERSION)); + return HttpUrl.parse(properties.get(PLUGINHUB_BASE) + "/" + version); } public static String getImgurClientId() diff --git a/runelite-client/src/main/java/net/runelite/client/SessionClient.java b/runelite-client/src/main/java/net/runelite/client/SessionClient.java index dfd64f76a2..5ea3a116d3 100644 --- a/runelite-client/src/main/java/net/runelite/client/SessionClient.java +++ b/runelite-client/src/main/java/net/runelite/client/SessionClient.java @@ -24,8 +24,7 @@ */ package net.runelite.client; -import io.reactivex.rxjava3.core.Completable; -import io.reactivex.rxjava3.core.Observable; +import com.google.gson.JsonParseException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -36,6 +35,7 @@ import net.runelite.http.api.RuneLiteAPI; import okhttp3.HttpUrl; import okhttp3.OkHttpClient; import okhttp3.Request; +import okhttp3.RequestBody; import okhttp3.Response; import okhttp3.ResponseBody; @@ -44,73 +44,62 @@ class SessionClient { private final OkHttpClient okHttpClient; - Observable openSession() + UUID open() throws IOException { - final HttpUrl url = RuneLiteAPI.getSessionBase().newBuilder() - .addPathSegment("new") + HttpUrl url = RuneLiteAPI.getSessionBase().newBuilder() .build(); - return Observable.fromCallable(() -> + Request request = new Request.Builder() + .post(RequestBody.create(null, new byte[0])) + .url(url) + .build(); + + try (Response response = okHttpClient.newCall(request).execute()) { - Request request = new Request.Builder() - .url(url) - .build(); - - try (Response response = okHttpClient.newCall(request).execute()) - { - ResponseBody body = response.body(); - - InputStream in = body.byteStream(); - try - { - return RuneLiteAPI.GSON.fromJson(new InputStreamReader(in, StandardCharsets.UTF_8), UUID.class); - } - catch (IllegalArgumentException ex) - { - ex.printStackTrace(); - return null; - } - } - }); + ResponseBody body = response.body(); + + InputStream in = body.byteStream(); + return RuneLiteAPI.GSON.fromJson(new InputStreamReader(in, StandardCharsets.UTF_8), UUID.class); + } + catch (JsonParseException | IllegalArgumentException ex) // UUID.fromString can throw IllegalArgumentException + { + throw new IOException(ex); + } } - Completable pingSession(UUID uuid) + void ping(UUID uuid, boolean loggedIn) throws IOException { - final HttpUrl url = RuneLiteAPI.getSessionBase().newBuilder() + HttpUrl url = RuneLiteAPI.getSessionBase().newBuilder() .addPathSegment("ping") - .addQueryParameter("uuid", uuid.toString()) + .addQueryParameter("session", uuid.toString()) + .addQueryParameter("logged-in", String.valueOf(loggedIn)) .build(); - return Completable.fromAction(() -> - { - Request request = new Request.Builder() - .url(url) - .build(); + Request request = new Request.Builder() + .post(RequestBody.create(null, new byte[0])) + .url(url) + .build(); - try (Response response = okHttpClient.newCall(request).execute()) + try (Response response = okHttpClient.newCall(request).execute()) + { + if (!response.isSuccessful()) { - if (!response.isSuccessful()) - { - throw new IOException("Unsuccessful ping"); - } + throw new IOException("Unsuccessful ping"); } - }); + } } - Completable delete(UUID uuid) + void delete(UUID uuid) throws IOException { - final HttpUrl url = RuneLiteAPI.getSessionBase().newBuilder() + HttpUrl url = RuneLiteAPI.getSessionBase().newBuilder() .addQueryParameter("session", uuid.toString()) .build(); - return Completable.fromAction(() -> - { - Request request = new Request.Builder() - .delete() - .url(url) - .build(); + Request request = new Request.Builder() + .delete() + .url(url) + .build(); - okHttpClient.newCall(request).execute().close(); - }); + okHttpClient.newCall(request).execute().close(); } } diff --git a/runelite-client/src/main/java/net/runelite/client/account/SessionManager.java b/runelite-client/src/main/java/net/runelite/client/account/SessionManager.java index 693bc01a78..cadf0b4562 100644 --- a/runelite-client/src/main/java/net/runelite/client/account/SessionManager.java +++ b/runelite-client/src/main/java/net/runelite/client/account/SessionManager.java @@ -25,7 +25,6 @@ package net.runelite.client.account; import com.google.gson.Gson; -import io.reactivex.rxjava3.schedulers.Schedulers; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -37,11 +36,13 @@ import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.UUID; import javax.inject.Inject; +import javax.inject.Named; import javax.inject.Singleton; import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import net.runelite.client.RuneLite; +import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.EventBus; +import net.runelite.client.eventbus.Subscribe; import net.runelite.client.events.SessionClose; import net.runelite.client.events.SessionOpen; import net.runelite.client.util.LinkBrowser; @@ -55,31 +56,35 @@ import okhttp3.OkHttpClient; @Slf4j public class SessionManager { - private static final File SESSION_FILE = new File(RuneLite.RUNELITE_DIR, "session"); - @Getter private AccountSession accountSession; private final EventBus eventBus; + private final ConfigManager configManager; private final WSClient wsClient; + private final File sessionFile; private final AccountClient accountClient; @Inject private SessionManager( + @Named("sessionfile") File sessionfile, + ConfigManager configManager, EventBus eventBus, WSClient wsClient, OkHttpClient okHttpClient) { + this.configManager = configManager; this.eventBus = eventBus; this.wsClient = wsClient; + this.sessionFile = sessionfile; this.accountClient = new AccountClient(okHttpClient); - this.eventBus.subscribe(LoginResponse.class, this, this::onLoginResponse); + eventBus.register(this); } public void loadSession() { - if (!SESSION_FILE.exists()) + if (!sessionFile.exists()) { log.info("No session file exists"); return; @@ -87,7 +92,7 @@ public class SessionManager AccountSession session; - try (FileInputStream in = new FileInputStream(SESSION_FILE)) + try (FileInputStream in = new FileInputStream(sessionFile)) { session = new Gson().fromJson(new InputStreamReader(in, StandardCharsets.UTF_8), AccountSession.class); @@ -101,26 +106,13 @@ public class SessionManager // Check if session is still valid accountClient.setUuid(session.getUuid()); - accountClient.sessionCheck() - .subscribeOn(Schedulers.io()) - .subscribe(b -> - { - if (!b) - { - log.debug("Loaded session {} is invalid", session.getUuid()); - } - else - { - openSession(session, false); - } - }, ex -> - { - if (ex instanceof IOException) - { - log.debug("Unable to verify session", ex); - openSession(session, false); - } - }); + if (!accountClient.sessionCheck()) + { + log.debug("Loaded session {} is invalid", session.getUuid()); + return; + } + + openSession(session, false); } private void saveSession() @@ -130,11 +122,11 @@ public class SessionManager return; } - try (Writer fw = new OutputStreamWriter(new FileOutputStream(SESSION_FILE), StandardCharsets.UTF_8)) + try (Writer fw = new OutputStreamWriter(new FileOutputStream(sessionFile), StandardCharsets.UTF_8)) { new Gson().toJson(accountSession, fw); - log.debug("Saved session to {}", SESSION_FILE); + log.debug("Saved session to {}", sessionFile); } catch (IOException ex) { @@ -144,7 +136,7 @@ public class SessionManager private void deleteSession() { - SESSION_FILE.delete(); + sessionFile.delete(); } /** @@ -163,7 +155,14 @@ public class SessionManager accountSession = session; - eventBus.post(SessionOpen.class, new SessionOpen()); + if (session.getUsername() != null) + { + // Initialize config for new session + // If the session isn't logged in yet, don't switch to the new config + configManager.switchSession(session); + } + + eventBus.post(new SessionOpen()); } private void closeSession() @@ -189,7 +188,10 @@ public class SessionManager accountSession = null; // No more account - eventBus.post(SessionClose.class, new SessionClose()); + // Restore config + configManager.switchSession(null); + + eventBus.post(new SessionClose()); } public void login() @@ -217,7 +219,8 @@ public class SessionManager LinkBrowser.browse(login.getOauthUrl()); } - private void onLoginResponse(LoginResponse loginResponse) + @Subscribe + public void onLoginResponse(LoginResponse loginResponse) { log.debug("Now logged in as {}", loginResponse.getUsername()); diff --git a/runelite-client/src/main/java/net/runelite/client/callback/ClientThread.java b/runelite-client/src/main/java/net/runelite/client/callback/ClientThread.java index af2858ef49..2470b55056 100644 --- a/runelite-client/src/main/java/net/runelite/client/callback/ClientThread.java +++ b/runelite-client/src/main/java/net/runelite/client/callback/ClientThread.java @@ -25,34 +25,22 @@ package net.runelite.client.callback; import com.google.inject.Inject; -import io.reactivex.rxjava3.plugins.RxJavaPlugins; -import io.reactivex.rxjava3.schedulers.Schedulers; import java.util.Iterator; import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.Executor; import java.util.function.BooleanSupplier; -import javax.annotation.Nullable; import javax.inject.Singleton; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; -import org.jetbrains.annotations.NotNull; @Singleton @Slf4j -public class ClientThread implements Executor +public class ClientThread { private final ConcurrentLinkedQueue invokes = new ConcurrentLinkedQueue<>(); @Inject - @Nullable private Client client; - @Inject - private ClientThread() - { - RxJavaPlugins.setSingleSchedulerHandler(old -> Schedulers.from(this)); - } - public void invoke(Runnable r) { invoke(() -> @@ -124,14 +112,4 @@ public class ClientThread implements Executor } } } - - @Override - public void execute(@NotNull Runnable r) - { - invoke(() -> - { - r.run(); - return true; - }); - } } diff --git a/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java b/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java index 189d7f5a37..d7e353df4c 100644 --- a/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java +++ b/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java @@ -24,7 +24,6 @@ */ package net.runelite.client.callback; -import com.google.inject.Injector; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; @@ -40,31 +39,25 @@ import java.awt.image.VolatileImage; import javax.inject.Inject; import javax.inject.Singleton; import lombok.extern.slf4j.Slf4j; -import net.runelite.api.BufferProvider; import net.runelite.api.Client; -import net.runelite.api.Entity; import net.runelite.api.MainBufferProvider; import net.runelite.api.NullItemID; import net.runelite.api.RenderOverview; import net.runelite.api.Skill; import net.runelite.api.WorldMapManager; -import net.runelite.api.events.BeforeMenuRender; import net.runelite.api.events.BeforeRender; -import net.runelite.api.events.Event; import net.runelite.api.events.FakeXpDrop; import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.GameTick; import net.runelite.api.events.ScriptCallbackEvent; import net.runelite.api.hooks.Callbacks; -import net.runelite.api.hooks.DrawCallbacks; import net.runelite.api.widgets.Widget; import static net.runelite.api.widgets.WidgetInfo.WORLD_MAP_VIEW; import net.runelite.api.widgets.WidgetItem; import net.runelite.client.Notifier; -import net.runelite.client.RuneLite; import net.runelite.client.chat.ChatMessageManager; import net.runelite.client.eventbus.EventBus; -import net.runelite.client.events.DrawFinished; +import net.runelite.client.eventbus.Subscribe; import net.runelite.client.input.KeyManager; import net.runelite.client.input.MouseManager; import net.runelite.client.task.Scheduler; @@ -75,7 +68,6 @@ import net.runelite.client.ui.overlay.OverlayManager; import net.runelite.client.ui.overlay.OverlayRenderer; import net.runelite.client.ui.overlay.infobox.InfoBoxManager; import net.runelite.client.util.DeferredEventBus; -import net.runelite.client.util.ImageUtil; import net.runelite.client.util.RSTimeUnit; /** @@ -89,19 +81,17 @@ public class Hooks implements Callbacks { private static final long CHECK = RSTimeUnit.GAME_TICKS.getDuration().toNanos(); // ns - how often to run checks - private static final Injector injector = RuneLite.getInjector(); - private static final Client client = injector.getInstance(Client.class); - public static final OverlayRenderer renderer = injector.getInstance(OverlayRenderer.class); - private static final OverlayManager overlayManager = injector.getInstance(OverlayManager.class); + private static final GameTick GAME_TICK = new GameTick(); + private static final BeforeRender BEFORE_RENDER = new BeforeRender(); - private static final GameTick GAME_TICK = GameTick.INSTANCE; - private static final BeforeRender BEFORE_RENDER = BeforeRender.INSTANCE; - private static final DrawFinished drawFinishedEvent = new DrawFinished(); + @Inject + private Client client; - private int mouseX = 0; - private int mouseY = 0; - private final Image cursor = ImageUtil.getResourceStreamFromClass(Hooks.class, "cursor.png"); + @Inject + private OverlayRenderer renderer; + @Inject + private OverlayManager overlayManager; @Inject private EventBus eventBus; @@ -145,7 +135,7 @@ public class Hooks implements Callbacks private boolean shouldProcessGameTick; private static MainBufferProvider lastMainBufferProvider; - public static Graphics2D lastGraphics; + private static Graphics2D lastGraphics; /** * Get the Graphics2D for the MainBufferProvider image @@ -170,15 +160,15 @@ public class Hooks implements Callbacks } @Override - public void post(Class eventClass, E event) + public void post(Object event) { - eventBus.post(eventClass, event); + eventBus.post(event); } @Override - public void postDeferred(Class eventClass, E event) + public void postDeferred(Object event) { - deferredEventBus.post(eventClass, event); + deferredEventBus.post(event); } @Override @@ -190,13 +180,13 @@ public class Hooks implements Callbacks deferredEventBus.replay(); - eventBus.post(GameTick.class, GameTick.INSTANCE); + eventBus.post(GAME_TICK); int tick = client.getTickCount(); client.setTickCount(tick + 1); } - eventBus.post(BeforeRender.class, BeforeRender.INSTANCE); + eventBus.post(BEFORE_RENDER); clientThread.invoke(); @@ -293,16 +283,12 @@ public class Hooks implements Callbacks @Override public MouseEvent mouseDragged(MouseEvent mouseEvent) { - mouseX = mouseEvent.getX(); - mouseY = mouseEvent.getY(); return mouseManager.processMouseDragged(mouseEvent); } @Override public MouseEvent mouseMoved(MouseEvent mouseEvent) { - mouseX = mouseEvent.getX(); - mouseY = mouseEvent.getY(); return mouseManager.processMouseMoved(mouseEvent); } @@ -405,22 +391,6 @@ public class Hooks implements Callbacks finalImage = image; } - if (client.isMirrored()) - { - drawFinishedEvent.image = copy(finalImage); - drawFinishedEvent.image.getGraphics().drawImage(cursor, mouseX, mouseY, null); - eventBus.post(DrawFinished.class, drawFinishedEvent); - } - - try - { - renderer.render((Graphics2D)finalImage.getGraphics(), OverlayLayer.AFTER_MIRROR); - } - catch (Exception ex) - { - log.warn("Error during post-mirror rendering", ex); - } - // Draw the image onto the game canvas graphics.drawImage(finalImage, 0, 0, client.getCanvas()); @@ -431,6 +401,8 @@ public class Hooks implements Callbacks /** * Copy an image + * @param src + * @return */ private static Image copy(Image src) { @@ -475,7 +447,8 @@ public class Hooks implements Callbacks } } - public static void drawAfterWidgets() + @Override + public void drawAfterWidgets() { MainBufferProvider bufferProvider = (MainBufferProvider) client.getBufferProvider(); Graphics2D graphics2d = getGraphics(bufferProvider); @@ -495,6 +468,7 @@ public class Hooks implements Callbacks overlayManager.getItemWidgets().clear(); } + @Subscribe public void onGameStateChanged(GameStateChanged gameStateChanged) { switch (gameStateChanged.getGameState()) @@ -530,38 +504,6 @@ public class Hooks implements Callbacks deferredEventBus.replay(); } - public static void renderDraw(Entity entity, int orientation, int pitchSin, int pitchCos, int yawSin, int yawCos, int x, int y, int z, long hash) - { - DrawCallbacks drawCallbacks = client.getDrawCallbacks(); - if (drawCallbacks != null) - { - drawCallbacks.draw(entity, orientation, pitchSin, pitchCos, yawSin, yawCos, x, y, z, hash); - } - else - { - entity.draw(orientation, pitchSin, pitchCos, yawSin, yawCos, x, y, z, hash); - } - } - - public static void clearColorBuffer(int x, int y, int width, int height, int color) - { - BufferProvider bp = client.getBufferProvider(); - int canvasWidth = bp.getWidth(); - int[] pixels = bp.getPixels(); - - int pixelPos = y * canvasWidth + x; - int pixelJump = canvasWidth - width; - - for (int cy = y; cy < y + height; cy++) - { - for (int cx = x; cx < x + width; cx++) - { - pixels[pixelPos++] = 0; - } - pixelPos += pixelJump; - } - } - @Override public void drawItem(int itemId, WidgetItem widgetItem) { @@ -572,13 +514,7 @@ public class Hooks implements Callbacks } } - public static boolean drawMenu() - { - BeforeMenuRender event = new BeforeMenuRender(); - client.getCallbacks().post(BeforeMenuRender.class, event); - return event.isConsumed(); - } - + @Subscribe public void onScriptCallbackEvent(ScriptCallbackEvent scriptCallbackEvent) { if (!scriptCallbackEvent.getEventName().equals("fakeXpDrop")) @@ -597,6 +533,6 @@ public class Hooks implements Callbacks skill, xp ); - eventBus.post(FakeXpDrop.class, fakeXpDrop); + eventBus.post(fakeXpDrop); } -} \ No newline at end of file +} diff --git a/runelite-client/src/main/java/net/runelite/client/chat/ChatCommandManager.java b/runelite-client/src/main/java/net/runelite/client/chat/ChatCommandManager.java index a2d6a9737f..03bc5960b6 100644 --- a/runelite-client/src/main/java/net/runelite/client/chat/ChatCommandManager.java +++ b/runelite-client/src/main/java/net/runelite/client/chat/ChatCommandManager.java @@ -41,21 +41,20 @@ import net.runelite.client.events.ChatboxInput; import net.runelite.client.events.PrivateMessageInput; @Singleton -public class ChatCommandManager +public class ChatCommandManager implements ChatboxInputListener { private final Map commands = new ConcurrentHashMap<>(); + private final Client client; private final ScheduledExecutorService scheduledExecutorService; @Inject - private ChatCommandManager(EventBus eventBus, Client client, ScheduledExecutorService scheduledExecutorService) + private ChatCommandManager(EventBus eventBus, CommandManager commandManager, Client client, ScheduledExecutorService scheduledExecutorService) { this.client = client; this.scheduledExecutorService = scheduledExecutorService; - - eventBus.subscribe(ChatboxInput.class, this, this::onChatboxInput); - eventBus.subscribe(PrivateMessageInput.class, this, this::onPrivateMessageInput); - eventBus.subscribe(ChatMessage.class, this, this::onChatMessage); + eventBus.register(this); + commandManager.register(this); } public void registerCommand(String command, BiConsumer execute) @@ -83,7 +82,8 @@ public class ChatCommandManager commands.remove(command.toLowerCase()); } - private void onChatMessage(ChatMessage chatMessage) + @Subscribe + public void onChatMessage(ChatMessage chatMessage) { if (client.getGameState() != GameState.LOGGED_IN) { @@ -106,6 +106,11 @@ public class ChatCommandManager String message = chatMessage.getMessage(); String command = extractCommand(message); + if (command == null) + { + return; + } + ChatCommand chatCommand = commands.get(command.toLowerCase()); if (chatCommand == null) { @@ -122,8 +127,8 @@ public class ChatCommandManager } } - @Subscribe // just for show - private void onChatboxInput(ChatboxInput chatboxInput) + @Override + public boolean onChatboxInput(ChatboxInput chatboxInput) { String message = chatboxInput.getValue(); if (message.startsWith("/")) @@ -131,30 +136,51 @@ public class ChatCommandManager message = message.substring(1); // friends chat input } - onInput(chatboxInput, message); - } - - @Subscribe // just for show - private void onPrivateMessageInput(PrivateMessageInput input) - { - onInput(input, input.getMessage()); - } - - private void onInput(ChatInput chatInput, String message) - { String command = extractCommand(message); + if (command == null) + { + return false; + } ChatCommand chatCommand = commands.get(command.toLowerCase()); if (chatCommand == null) { - return; + return false; } BiPredicate input = chatCommand.getInput(); - if (input != null && input.test(chatInput, message)) + if (input == null) { - chatInput.setStop(); + return false; } + + return input.test(chatboxInput, message); + } + + @Override + public boolean onPrivateMessageInput(PrivateMessageInput privateMessageInput) + { + final String message = privateMessageInput.getMessage(); + + String command = extractCommand(message); + if (command == null) + { + return false; + } + + ChatCommand chatCommand = commands.get(command.toLowerCase()); + if (chatCommand == null) + { + return false; + } + + BiPredicate input = chatCommand.getInput(); + if (input == null) + { + return false; + } + + return input.test(privateMessageInput, message); } private static String extractCommand(String message) diff --git a/runelite-client/src/main/java/net/runelite/client/chat/ChatMessageBuilder.java b/runelite-client/src/main/java/net/runelite/client/chat/ChatMessageBuilder.java index 0b9b395146..1b8afd0924 100644 --- a/runelite-client/src/main/java/net/runelite/client/chat/ChatMessageBuilder.java +++ b/runelite-client/src/main/java/net/runelite/client/chat/ChatMessageBuilder.java @@ -25,8 +25,8 @@ package net.runelite.client.chat; import java.awt.Color; -import net.runelite.api.util.Text; import net.runelite.client.util.ColorUtil; +import net.runelite.client.util.Text; public class ChatMessageBuilder { diff --git a/runelite-client/src/main/java/net/runelite/client/chat/ChatMessageManager.java b/runelite-client/src/main/java/net/runelite/client/chat/ChatMessageManager.java index 655388e959..7faa7ac564 100644 --- a/runelite-client/src/main/java/net/runelite/client/chat/ChatMessageManager.java +++ b/runelite-client/src/main/java/net/runelite/client/chat/ChatMessageManager.java @@ -24,8 +24,10 @@ */ package net.runelite.client.chat; +import com.google.common.base.MoreObjects; import com.google.common.base.Strings; import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; import java.awt.Color; import java.util.Arrays; @@ -44,20 +46,20 @@ import net.runelite.api.MessageNode; import net.runelite.api.Player; import net.runelite.api.Varbits; import net.runelite.api.events.ChatMessage; +import net.runelite.client.events.ConfigChanged; import net.runelite.api.events.ResizeableChanged; import net.runelite.api.events.ScriptCallbackEvent; import net.runelite.api.events.VarbitChanged; import net.runelite.client.callback.ClientThread; import net.runelite.client.config.ChatColorConfig; -import net.runelite.client.eventbus.EventBus; -import net.runelite.client.events.ConfigChanged; +import net.runelite.client.eventbus.Subscribe; import net.runelite.client.ui.JagexColors; import net.runelite.client.util.ColorUtil; @Singleton public class ChatMessageManager { - private static final Set TUTORIAL_ISLAND_REGIONS = Set.of(12336, 12335, 12592, 12080, 12079, 12436); + private static final Set TUTORIAL_ISLAND_REGIONS = ImmutableSet.of(12336, 12335, 12592, 12080, 12079, 12436); private final Multimap colorCache = HashMultimap.create(); private final Client client; @@ -68,23 +70,17 @@ public class ChatMessageManager @Inject private ChatMessageManager( - final Client client, - final ChatColorConfig chatColorConfig, - final ClientThread clientThread, - final EventBus eventbus) + Client client, + ChatColorConfig chatColorConfig, + ClientThread clientThread) { this.client = client; this.chatColorConfig = chatColorConfig; this.clientThread = clientThread; - - eventbus.subscribe(VarbitChanged.class, this, this::onVarbitChanged); - eventbus.subscribe(ResizeableChanged.class, this, this::onResizeableChanged); - eventbus.subscribe(ConfigChanged.class, this, this::onConfigChanged); - eventbus.subscribe(ChatMessage.class, this, this::onChatMessage); - eventbus.subscribe(ScriptCallbackEvent.class, this, this::onScriptCallbackEvent); } - private void onVarbitChanged(VarbitChanged event) + @Subscribe + public void onVarbitChanged(VarbitChanged event) { int setting = client.getVar(Varbits.TRANSPARENT_CHATBOX); @@ -95,12 +91,14 @@ public class ChatMessageManager } } - private void onResizeableChanged(ResizeableChanged event) + @Subscribe + public void onResizeableChanged(ResizeableChanged event) { refreshAll(); } - private void onConfigChanged(ConfigChanged event) + @Subscribe + public void onConfigChanged(ConfigChanged event) { if (event.getGroup().equals("textrecolor")) { @@ -109,7 +107,8 @@ public class ChatMessageManager } } - void onChatMessage(ChatMessage chatMessage) + @Subscribe(priority = -1) // run after all plugins + public void onChatMessage(ChatMessage chatMessage) { MessageNode messageNode = chatMessage.getMessageNode(); ChatMessageType chatMessageType = chatMessage.getType(); @@ -174,7 +173,8 @@ public class ChatMessageManager } } - private void onScriptCallbackEvent(ScriptCallbackEvent scriptCallbackEvent) + @Subscribe + public void onScriptCallbackEvent(ScriptCallbackEvent scriptCallbackEvent) { final String eventName = scriptCallbackEvent.getEventName(); @@ -571,17 +571,11 @@ public class ChatMessageManager return; } - //guard case for google MoreObjects#firstNonNull - if (message.getValue() == null && message.getRuneLiteFormattedMessage() == null) - { - return; - } - // this updates chat cycle client.addChatMessage( message.getType(), - Objects.requireNonNullElse(message.getName(), ""), - Objects.requireNonNullElse(message.getValue(), message.getRuneLiteFormattedMessage()), + MoreObjects.firstNonNull(message.getName(), ""), + MoreObjects.firstNonNull(message.getValue(), message.getRuneLiteFormattedMessage()), message.getSender()); // Get last message from line buffer (the one we just added) @@ -591,7 +585,7 @@ public class ChatMessageManager // Update the message with RuneLite additions line.setRuneLiteFormatMessage(message.getRuneLiteFormattedMessage()); - + if (message.getTimestamp() != 0) { line.setTimestamp(message.getTimestamp()); diff --git a/runelite-client/src/main/java/net/runelite/client/discord/DiscordReplyType.java b/runelite-client/src/main/java/net/runelite/client/chat/ChatboxInputListener.java similarity index 79% rename from runelite-client/src/main/java/net/runelite/client/discord/DiscordReplyType.java rename to runelite-client/src/main/java/net/runelite/client/chat/ChatboxInputListener.java index 50f85154af..a6e04a21fb 100644 --- a/runelite-client/src/main/java/net/runelite/client/discord/DiscordReplyType.java +++ b/runelite-client/src/main/java/net/runelite/client/chat/ChatboxInputListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Tomas Slusny + * Copyright (c) 2018, Adam * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,25 +22,14 @@ * (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.discord; +package net.runelite.client.chat; -/** - * Discord reply type for request - */ -public enum DiscordReplyType +import net.runelite.client.events.ChatboxInput; +import net.runelite.client.events.PrivateMessageInput; + +public interface ChatboxInputListener { - /** - * Used to decline a request - */ - NO, + boolean onChatboxInput(ChatboxInput chatboxInput); - /** - * Used to accept a request - */ - YES, - - /** - * Currently unused response, treated like NO. - */ - IGNORE + boolean onPrivateMessageInput(PrivateMessageInput privateMessageInput); } diff --git a/runelite-client/src/main/java/net/runelite/client/chat/CommandManager.java b/runelite-client/src/main/java/net/runelite/client/chat/CommandManager.java index 0a065d91f7..6c725df06c 100644 --- a/runelite-client/src/main/java/net/runelite/client/chat/CommandManager.java +++ b/runelite-client/src/main/java/net/runelite/client/chat/CommandManager.java @@ -26,6 +26,8 @@ package net.runelite.client.chat; import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; import javax.inject.Inject; import javax.inject.Singleton; import lombok.extern.slf4j.Slf4j; @@ -36,6 +38,7 @@ import net.runelite.api.events.CommandExecuted; import net.runelite.api.events.ScriptCallbackEvent; import net.runelite.client.callback.ClientThread; import net.runelite.client.eventbus.EventBus; +import net.runelite.client.eventbus.Subscribe; import net.runelite.client.events.ChatboxInput; import net.runelite.client.events.PrivateMessageInput; @@ -52,20 +55,27 @@ public class CommandManager private final ClientThread clientThread; private boolean sending; + private final List chatboxInputListenerList = new CopyOnWriteArrayList<>(); + @Inject - private CommandManager( - final Client client, - final EventBus eventBus, - final ClientThread clientThread - ) + private CommandManager(Client client, EventBus eventBus, ClientThread clientThread) { this.client = client; this.eventBus = eventBus; this.clientThread = clientThread; - - eventBus.subscribe(ScriptCallbackEvent.class, this, this::onScriptCallbackEvent); } + public void register(ChatboxInputListener chatboxInputListener) + { + chatboxInputListenerList.add(chatboxInputListener); + } + + public void unregister(ChatboxInputListener chatboxInputListener) + { + chatboxInputListenerList.remove(chatboxInputListener); + } + + @Subscribe private void onScriptCallbackEvent(ScriptCallbackEvent event) { if (sending) @@ -105,7 +115,7 @@ public class CommandManager String[] args = Arrays.copyOfRange(split, 1, split.length); CommandExecuted commandExecuted = new CommandExecuted(command, args); - eventBus.post(CommandExecuted.class, commandExecuted); + eventBus.post(commandExecuted); } private void handleInput(ScriptCallbackEvent event) @@ -134,10 +144,13 @@ public class CommandManager clientThread.invoke(() -> sendChatboxInput(chatType, typedText)); } }; + boolean stop = false; + for (ChatboxInputListener chatboxInputListener : chatboxInputListenerList) + { + stop |= chatboxInputListener.onChatboxInput(chatboxInput); + } - eventBus.post(ChatboxInput.class, chatboxInput); - - if (chatboxInput.isStop()) + if (stop) { // input was blocked. stringStack[stringStackCount - 1] = ""; // prevent script from sending @@ -171,9 +184,13 @@ public class CommandManager } }; - eventBus.post(PrivateMessageInput.class, privateMessageInput); + boolean stop = false; + for (ChatboxInputListener chatboxInputListener : chatboxInputListenerList) + { + stop |= chatboxInputListener.onPrivateMessageInput(privateMessageInput); + } - if (privateMessageInput.isStop()) + if (stop) { intStack[intStackCount - 1] = 1; client.setStringStackSize(stringStackCount - 2); // remove both target and message diff --git a/runelite-client/src/main/java/net/runelite/client/config/Alpha.java b/runelite-client/src/main/java/net/runelite/client/config/Alpha.java index 51a0f4d50a..79815f5ca2 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/Alpha.java +++ b/runelite-client/src/main/java/net/runelite/client/config/Alpha.java @@ -37,4 +37,6 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented -public @interface Alpha {} +public @interface Alpha +{ +} diff --git a/runelite-client/src/main/java/net/runelite/client/config/Button.java b/runelite-client/src/main/java/net/runelite/client/config/Button.java deleted file mode 100644 index c6cc8d67ee..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/config/Button.java +++ /dev/null @@ -1,3 +0,0 @@ -package net.runelite.client.config; - -public class Button {} diff --git a/runelite-client/src/main/java/net/runelite/client/config/ChatColorConfig.java b/runelite-client/src/main/java/net/runelite/client/config/ChatColorConfig.java index 68b0ceabbf..a6757a382a 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/ChatColorConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/config/ChatColorConfig.java @@ -30,34 +30,28 @@ import net.runelite.client.ui.JagexColors; @ConfigGroup("textrecolor") public interface ChatColorConfig extends Config { - @ConfigTitleSection( - keyName = "opaqueTitle", + @ConfigSection( name = "Opaque", - description = "", - position = 1 + description = "The options that control the colours for the Opaque Chatbox", + position = 0, + closedByDefault = true ) - default Title opaqueTitle() - { - return new Title(); - } + String opaqueSection = "opaqueSection"; - @ConfigTitleSection( - keyName = "transparentTitle", + @ConfigSection( name = "Transparent", - description = "", - position = 1 + description = "The options that control the colours for the Transparent Chatbox", + position = 50, + closedByDefault = true ) - default Title transparentTitle() - { - return new Title(); - } + String transparentSection = "transparentSection"; @ConfigItem( position = 1, keyName = "opaquePublicChat", name = "Public chat", description = "Color of Public chat", - titleSection = "opaqueTitle" + section = opaqueSection ) Color opaquePublicChat(); @@ -66,7 +60,7 @@ public interface ChatColorConfig extends Config keyName = "opaquePublicChatHighlight", name = "Public chat highlight", description = "Color of highlights in Public chat", - titleSection = "opaqueTitle" + section = opaqueSection ) default Color opaquePublicChatHighlight() { @@ -78,7 +72,7 @@ public interface ChatColorConfig extends Config keyName = "opaquePrivateMessageSent", name = "Sent private messages", description = "Color of Private messages you've sent", - titleSection = "opaqueTitle" + section = opaqueSection ) Color opaquePrivateMessageSent(); @@ -87,7 +81,7 @@ public interface ChatColorConfig extends Config keyName = "opaquePrivateMessageSentHighlight", name = "Sent private messages highlight", description = "Color of highlights in Private messages you've sent", - titleSection = "opaqueTitle" + section = opaqueSection ) default Color opaquePrivateMessageSentHighlight() { @@ -99,7 +93,7 @@ public interface ChatColorConfig extends Config keyName = "opaquePrivateMessageReceived", name = "Received private messages", description = "Color of Private messages you've received", - titleSection = "opaqueTitle" + section = opaqueSection ) Color opaquePrivateMessageReceived(); @@ -108,7 +102,7 @@ public interface ChatColorConfig extends Config keyName = "opaquePrivateMessageReceivedHighlight", name = "Received private messages highlight", description = "Color of highlights in Private messages you've received", - titleSection = "opaqueTitle" + section = opaqueSection ) default Color opaquePrivateMessageReceivedHighlight() { @@ -120,7 +114,7 @@ public interface ChatColorConfig extends Config keyName = "opaqueClanChatInfo", name = "Friends chat info", description = "Friends Chat Information (eg. when joining a channel)", - titleSection = "opaqueTitle" + section = opaqueSection ) default Color opaqueFriendsChatInfo() { @@ -132,7 +126,7 @@ public interface ChatColorConfig extends Config keyName = "opaqueClanChatInfoHighlight", name = "Friends chat info highlight", description = "Friends Chat Information highlight (used for the Raids plugin)", - titleSection = "opaqueTitle" + section = opaqueSection ) default Color opaqueFriendsChatInfoHighlight() { @@ -144,7 +138,7 @@ public interface ChatColorConfig extends Config keyName = "opaqueClanChatMessage", name = "Friends chat message", description = "Color of Friends chat messages", - titleSection = "opaqueTitle" + section = opaqueSection ) Color opaqueFriendsChatMessage(); @@ -153,7 +147,7 @@ public interface ChatColorConfig extends Config keyName = "opaqueClanChatMessageHighlight", name = "Friends chat message highlight", description = "Color of highlights in Friends Chat messages", - titleSection = "opaqueTitle" + section = opaqueSection ) default Color opaqueFriendsChatMessageHighlight() { @@ -165,7 +159,7 @@ public interface ChatColorConfig extends Config keyName = "opaqueAutochatMessage", name = "Autochat", description = "Color of Autochat messages", - titleSection = "opaqueTitle" + section = opaqueSection ) Color opaqueAutochatMessage(); @@ -174,7 +168,7 @@ public interface ChatColorConfig extends Config keyName = "opaqueAutochatMessageHighlight", name = "Autochat highlight", description = "Color of highlights in Autochat messages", - titleSection = "opaqueTitle" + section = opaqueSection ) Color opaqueAutochatMessageHighlight(); @@ -183,7 +177,7 @@ public interface ChatColorConfig extends Config keyName = "opaqueTradeChatMessage", name = "Trade chat", description = "Color of Trade Chat Messages", - titleSection = "opaqueTitle" + section = opaqueSection ) Color opaqueTradeChatMessage(); @@ -192,7 +186,7 @@ public interface ChatColorConfig extends Config keyName = "opaqueTradeChatMessageHighlight", name = "Trade chat highlight", description = "Color of highlights in Trade Chat Messages", - titleSection = "opaqueTitle" + section = opaqueSection ) Color opaqueTradeChatMessageHighlight(); @@ -201,7 +195,7 @@ public interface ChatColorConfig extends Config keyName = "opaqueServerMessage", name = "Server message", description = "Color of Server Messages (eg. 'Welcome to RuneScape')", - titleSection = "opaqueTitle" + section = opaqueSection ) Color opaqueServerMessage(); @@ -210,7 +204,7 @@ public interface ChatColorConfig extends Config keyName = "opaqueServerMessageHighlight", name = "Server message highlight", description = "Color of highlights in Server Messages", - titleSection = "opaqueTitle" + section = opaqueSection ) Color opaqueServerMessageHighlight(); @@ -219,7 +213,7 @@ public interface ChatColorConfig extends Config keyName = "opaqueGameMessage", name = "Game message", description = "Color of Game Messages", - titleSection = "opaqueTitle" + section = opaqueSection ) Color opaqueGameMessage(); @@ -228,7 +222,7 @@ public interface ChatColorConfig extends Config keyName = "opaqueGameMessageHighlight", name = "Game message highlight", description = "Color of highlights in Game Messages", - titleSection = "opaqueTitle" + section = opaqueSection ) default Color opaqueGameMessageHighlight() { @@ -240,7 +234,7 @@ public interface ChatColorConfig extends Config keyName = "opaqueExamine", name = "Examine", description = "Color of Examine Text", - titleSection = "opaqueTitle" + section = opaqueSection ) Color opaqueExamine(); @@ -249,7 +243,7 @@ public interface ChatColorConfig extends Config keyName = "opaqueExamineHighlight", name = "Examine highlight", description = "Color of highlights in Examine Text", - titleSection = "opaqueTitle" + section = opaqueSection ) default Color opaqueExamineHighlight() { @@ -261,7 +255,7 @@ public interface ChatColorConfig extends Config keyName = "opaqueFiltered", name = "Filtered", description = "Color of Filtered Text (messages that aren't shown when Game messages are filtered)", - titleSection = "opaqueTitle" + section = opaqueSection ) Color opaqueFiltered(); @@ -270,7 +264,7 @@ public interface ChatColorConfig extends Config keyName = "opaqueFilteredHighlight", name = "Filtered highlight", description = "Color of highlights in Filtered Text", - titleSection = "opaqueTitle" + section = opaqueSection ) Color opaqueFilteredHighlight(); @@ -279,7 +273,7 @@ public interface ChatColorConfig extends Config keyName = "opaqueUsername", name = "Usernames", description = "Color of Usernames", - titleSection = "opaqueTitle" + section = opaqueSection ) Color opaqueUsername(); @@ -288,7 +282,7 @@ public interface ChatColorConfig extends Config keyName = "opaquePrivateUsernames", name = "Private chat usernames", description = "Color of Usernames in Private Chat", - titleSection = "opaqueTitle" + section = opaqueSection ) Color opaquePrivateUsernames(); @@ -297,7 +291,7 @@ public interface ChatColorConfig extends Config keyName = "opaqueClanChannelName", name = "Friends chat channel name", description = "Color of Friends chat channel name", - titleSection = "opaqueTitle" + section = opaqueSection ) Color opaqueFriendsChatChannelName(); @@ -306,7 +300,7 @@ public interface ChatColorConfig extends Config keyName = "opaqueClanUsernames", name = "Friends chat usernames", description = "Color of usernames in Friends chat", - titleSection = "opaqueTitle" + section = opaqueSection ) Color opaqueFriendsChatUsernames(); @@ -315,7 +309,7 @@ public interface ChatColorConfig extends Config keyName = "opaquePublicFriendUsernames", name = "Public friend usernames", description = "Color of Friend Usernames in Public Chat", - titleSection = "opaqueTitle" + section = opaqueSection ) Color opaquePublicFriendUsernames(); @@ -324,7 +318,7 @@ public interface ChatColorConfig extends Config keyName = "transparentPublicChat", name = "Public chat (transparent)", description = "Color of Public chat (transparent)", - titleSection = "transparentTitle" + section = transparentSection ) Color transparentPublicChat(); @@ -333,7 +327,7 @@ public interface ChatColorConfig extends Config keyName = "transparentPublicChatHighlight", name = "Public chat highlight (transparent)", description = "Color of highlights in Public chat (transparent)", - titleSection = "transparentTitle" + section = transparentSection ) default Color transparentPublicChatHighlight() { @@ -345,7 +339,7 @@ public interface ChatColorConfig extends Config keyName = "transparentPrivateMessageSent", name = "Sent private messages (transparent)", description = "Color of Private messages you've sent (transparent)", - titleSection = "transparentTitle" + section = transparentSection ) Color transparentPrivateMessageSent(); @@ -354,7 +348,7 @@ public interface ChatColorConfig extends Config keyName = "transparentPrivateMessageSentHighlight", name = "Sent private messages highlight (transparent)", description = "Color of highlights in Private messages you've sent (transparent)", - titleSection = "transparentTitle" + section = transparentSection ) default Color transparentPrivateMessageSentHighlight() { @@ -366,7 +360,7 @@ public interface ChatColorConfig extends Config keyName = "transparentPrivateMessageReceived", name = "Received private messages (transparent)", description = "Color of Private messages you've received (transparent)", - titleSection = "transparentTitle" + section = transparentSection ) Color transparentPrivateMessageReceived(); @@ -375,7 +369,7 @@ public interface ChatColorConfig extends Config keyName = "transparentPrivateMessageReceivedHighlight", name = "Received private messages highlight (transparent)", description = "Color of highlights in Private messages you've received (transparent)", - titleSection = "transparentTitle" + section = transparentSection ) default Color transparentPrivateMessageReceivedHighlight() { @@ -387,7 +381,7 @@ public interface ChatColorConfig extends Config keyName = "transparentClanChatInfo", name = "Friends chat info (transparent)", description = "Friends chat information (eg. when joining a channel) (transparent)", - titleSection = "transparentTitle" + section = transparentSection ) default Color transparentFriendsChatInfo() { @@ -399,7 +393,7 @@ public interface ChatColorConfig extends Config keyName = "transparentClanChatInfoHighlight", name = "Friends chat info highlight (transparent)", description = "Friends chat information highlight (used for the Raids plugin) (transparent)", - titleSection = "transparentTitle" + section = transparentSection ) default Color transparentFriendsChatInfoHighlight() { @@ -411,7 +405,7 @@ public interface ChatColorConfig extends Config keyName = "transparentClanChatMessage", name = "Friends chat message (transparent)", description = "Color of Friends chat messages (transparent)", - titleSection = "transparentTitle" + section = transparentSection ) Color transparentFriendsChatMessage(); @@ -420,7 +414,7 @@ public interface ChatColorConfig extends Config keyName = "transparentClanChatMessageHighlight", name = "Friends chat message highlight (transparent)", description = "Color of highlights in Friends chat messages (transparent)", - titleSection = "transparentTitle" + section = transparentSection ) default Color transparentFriendsChatMessageHighlight() { @@ -432,7 +426,7 @@ public interface ChatColorConfig extends Config keyName = "transparentAutochatMessage", name = "Autochat (transparent)", description = "Color of Autochat messages (transparent)", - titleSection = "transparentTitle" + section = transparentSection ) Color transparentAutochatMessage(); @@ -441,7 +435,7 @@ public interface ChatColorConfig extends Config keyName = "transparentAutochatMessageHighlight", name = "Autochat highlight (transparent)", description = "Color of highlights in Autochat messages (transparent)", - titleSection = "transparentTitle" + section = transparentSection ) Color transparentAutochatMessageHighlight(); @@ -450,7 +444,7 @@ public interface ChatColorConfig extends Config keyName = "transparentTradeChatMessage", name = "Trade chat (transparent)", description = "Color of Trade Chat Messages (transparent)", - titleSection = "transparentTitle" + section = transparentSection ) Color transparentTradeChatMessage(); @@ -459,7 +453,7 @@ public interface ChatColorConfig extends Config keyName = "transparentTradeChatMessageHighlight", name = "Trade chat highlight (transparent)", description = "Color of highlights in Trade Chat Messages (transparent)", - titleSection = "transparentTitle" + section = transparentSection ) Color transparentTradeChatMessageHighlight(); @@ -468,7 +462,7 @@ public interface ChatColorConfig extends Config keyName = "transparentServerMessage", name = "Server message (transparent)", description = "Color of Server Messages (eg. 'Welcome to RuneScape') (transparent)", - titleSection = "transparentTitle" + section = transparentSection ) Color transparentServerMessage(); @@ -477,7 +471,7 @@ public interface ChatColorConfig extends Config keyName = "transparentServerMessageHighlight", name = "Server message highlight (transparent)", description = "Color of highlights in Server Messages (transparent)", - titleSection = "transparentTitle" + section = transparentSection ) Color transparentServerMessageHighlight(); @@ -486,7 +480,7 @@ public interface ChatColorConfig extends Config keyName = "transparentGameMessage", name = "Game message (transparent)", description = "Color of Game Messages (transparent)", - titleSection = "transparentTitle" + section = transparentSection ) Color transparentGameMessage(); @@ -495,7 +489,7 @@ public interface ChatColorConfig extends Config keyName = "transparentGameMessageHighlight", name = "Game message highlight (transparent)", description = "Color of highlights in Game Messages (transparent)", - titleSection = "transparentTitle" + section = transparentSection ) default Color transparentGameMessageHighlight() { @@ -507,7 +501,7 @@ public interface ChatColorConfig extends Config keyName = "transparentExamine", name = "Examine (transparent)", description = "Color of Examine Text (transparent)", - titleSection = "transparentTitle" + section = transparentSection ) Color transparentExamine(); @@ -516,7 +510,7 @@ public interface ChatColorConfig extends Config keyName = "transparentExamineHighlight", name = "Examine highlight (transparent)", description = "Color of highlights in Examine Text (transparent)", - titleSection = "transparentTitle" + section = transparentSection ) default Color transparentExamineHighlight() { @@ -528,7 +522,7 @@ public interface ChatColorConfig extends Config keyName = "transparentFiltered", name = "Filtered (transparent)", description = "Color of Filtered Text (messages that aren't shown when Game messages are filtered) (transparent)", - titleSection = "transparentTitle" + section = transparentSection ) Color transparentFiltered(); @@ -537,7 +531,7 @@ public interface ChatColorConfig extends Config keyName = "transparentFilteredHighlight", name = "Filtered highlight (transparent)", description = "Color of highlights in Filtered Text (transparent)", - titleSection = "transparentTitle" + section = transparentSection ) Color transparentFilteredHighlight(); @@ -546,7 +540,7 @@ public interface ChatColorConfig extends Config keyName = "transparentUsername", name = "Usernames (transparent)", description = "Color of Usernames (transparent)", - titleSection = "transparentTitle" + section = transparentSection ) Color transparentUsername(); @@ -555,7 +549,7 @@ public interface ChatColorConfig extends Config keyName = "transparentPrivateUsernames", name = "Private chat usernames (transparent)", description = "Color of Usernames in Private Chat (transparent)", - titleSection = "transparentTitle" + section = transparentSection ) Color transparentPrivateUsernames(); @@ -564,7 +558,7 @@ public interface ChatColorConfig extends Config keyName = "transparentClanChannelName", name = "Friends chat channel name (transparent)", description = "Color of Friends chat channel name (transparent)", - titleSection = "transparentTitle" + section = transparentSection ) Color transparentFriendsChatChannelName(); @@ -573,7 +567,7 @@ public interface ChatColorConfig extends Config keyName = "transparentClanUsernames", name = "Friends chat usernames (transparent)", description = "Color of usernames in Friends chat (transparent)", - titleSection = "transparentTitle" + section = transparentSection ) Color transparentFriendsChatUsernames(); @@ -582,7 +576,7 @@ public interface ChatColorConfig extends Config keyName = "transparentPublicFriendUsernames", name = "Public friend usernames (transparent)", description = "Color of Friend Usernames in Public Chat (transparent)", - titleSection = "transparentTitle" + section = transparentSection ) Color transparentPublicFriendUsernames(); } diff --git a/runelite-client/src/main/java/net/runelite/client/config/Config.java b/runelite-client/src/main/java/net/runelite/client/config/Config.java index b8d7f71701..6c95e9941a 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/Config.java +++ b/runelite-client/src/main/java/net/runelite/client/config/Config.java @@ -24,4 +24,6 @@ */ package net.runelite.client.config; -public interface Config {} +public interface Config +{ +} diff --git a/runelite-client/src/main/java/net/runelite/client/config/ConfigDescriptor.java b/runelite-client/src/main/java/net/runelite/client/config/ConfigDescriptor.java index f8347c01b4..b234e756d6 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/ConfigDescriptor.java +++ b/runelite-client/src/main/java/net/runelite/client/config/ConfigDescriptor.java @@ -24,22 +24,20 @@ */ package net.runelite.client.config; -import java.util.Collection; import lombok.Getter; +import java.util.Collection; @Getter public class ConfigDescriptor { private final ConfigGroup group; - private final Collection sections; - private final Collection titleSections; + private final Collection sections; private final Collection items; - public ConfigDescriptor(ConfigGroup group, Collection sections, Collection titleSections, Collection items) + public ConfigDescriptor(ConfigGroup group, Collection sections, Collection items) { this.group = group; this.sections = sections; - this.titleSections = titleSections; this.items = items; } -} \ No newline at end of file +} diff --git a/runelite-client/src/main/java/net/runelite/client/config/ConfigInvocationHandler.java b/runelite-client/src/main/java/net/runelite/client/config/ConfigInvocationHandler.java index 6aefcefda6..30087bb33a 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/ConfigInvocationHandler.java +++ b/runelite-client/src/main/java/net/runelite/client/config/ConfigInvocationHandler.java @@ -26,11 +26,11 @@ package net.runelite.client.config; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; -import java.lang.invoke.MethodHandles; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.Objects; import lombok.extern.slf4j.Slf4j; +import net.runelite.client.util.ReflectUtil; @Slf4j class ConfigInvocationHandler implements InvocationHandler @@ -100,7 +100,7 @@ class ConfigInvocationHandler implements InvocationHandler // Convert value to return type Class returnType = method.getReturnType(); - + try { Object objectValue = ConfigManager.stringToObject(value, returnType); @@ -112,11 +112,7 @@ class ConfigInvocationHandler implements InvocationHandler log.warn("Unable to unmarshal {}.{} ", group.value(), item.keyName(), e); if (method.isDefault()) { - Object defaultValue = callDefaultMethod(proxy, method, null); - - manager.setConfiguration(group.value(), item.keyName(), defaultValue); - - return defaultValue; + return callDefaultMethod(proxy, method, null); } return null; } @@ -127,7 +123,7 @@ class ConfigInvocationHandler implements InvocationHandler if (args.length != 1) { - throw new RuntimeException("Invalid number of arguents to configuration method"); + throw new RuntimeException("Invalid number of arguments to configuration method"); } Object newValue = args[0]; @@ -169,7 +165,7 @@ class ConfigInvocationHandler implements InvocationHandler static Object callDefaultMethod(Object proxy, Method method, Object[] args) throws Throwable { Class declaringClass = method.getDeclaringClass(); - return MethodHandles.privateLookupIn(declaringClass, MethodHandles.lookup()) + return ReflectUtil.privateLookupIn(declaringClass) .unreflectSpecial(method, declaringClass) .bindTo(proxy) .invokeWithArguments(args); @@ -180,4 +176,4 @@ class ConfigInvocationHandler implements InvocationHandler log.trace("cache invalidate"); cache.invalidateAll(); } -} \ No newline at end of file +} diff --git a/runelite-client/src/main/java/net/runelite/client/config/ConfigItem.java b/runelite-client/src/main/java/net/runelite/client/config/ConfigItem.java index 2863e3f6a3..a9a511cc08 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/ConfigItem.java +++ b/runelite-client/src/main/java/net/runelite/client/config/ConfigItem.java @@ -33,143 +33,19 @@ import java.lang.annotation.Target; @Target(ElementType.METHOD) public @interface ConfigItem { - /** - * If there is a section set, it will display in order - * starting from lowest value and ending at highest value - * in that specific section. Else, it will display in order - * for the config panel in a whole. - * - * @return The index of the config item. - */ int position() default -1; - /** - * This is not visible to users - * - * @return name used for finding the config item - * from the properties map. Hence, KEY name. - */ String keyName(); - /** - * This is the name that is shown to users when looking - * at the config panel. - *

- * Choose a name carefully, as there is a maximum width - * that depends on the users DPI scaling. Short is best. - * - * @return display name for the config item. - */ String name(); - /** - * This will be shown to the user if they are hovering - * the config item in the config panel. - * - * @return the description of the config item. - */ String description(); - /** - * If this is set to true, the config field will be - * hidden. You are able to change this value at runtime - * by having another config field unhide it {@link #unhide()} - */ boolean hidden() default false; - /** - * This is only used for {@link Boolean} config fields. - *

- * If this is set, then a warning(y/n) will be displayed - * when the user enables said config field. If they accept - * then the value will be set updated. - * - * @return a warning for enabling the config field. - */ String warning() default ""; - /** - * This is only used for {@link String} config fields. - *

- * If this is set to true, any input from the user - * will be hidden, and not displayed. This should - * be used for any sensitive information that may - * be accidentally leaked. - *

- * For example; api keys. - */ boolean secret() default false; - /** - * If this is set, it will look for a section - * by that {@link ConfigSection#name()}, if it exists, - * it will insert the config item under that - * section in order. - * - * @return title of the section. - */ String section() default ""; - - /** - * If this is set, it will look for a title section - * by that {@link ConfigTitleSection#name()}, if it exists, - * it will insert the config item under that - * section in order. - * - * @return title of the section. - */ - String titleSection() default ""; - - /** - * If this is set, it will look for a config item - * by that name. If it is hidden, it will unhide the item. - * - * @return {@link #name()} to unhide. - */ - String unhide() default ""; - - String unhideValue() default ""; - - /** - * If this is set, it will look for a config item - * by that name. If it is not hidden, it will hide the item. - * - * @return {@link #name()} to hide. - */ - String hide() default ""; - - String hideValue() default ""; - - /** - * If this is set, it will look for a config item - * by the name provided, if that config item is enabled - * then this item will also be enabled. - */ - String enabledBy() default ""; - - String enabledByValue() default ""; - - /** - * If this is set, it will look for a config item - * by the name provided, if that config item is disabled - * then this item will also be disabled. - */ - String disabledBy() default ""; - - String disabledByValue() default ""; - - boolean parse() default false; - - Class clazz() default void.class; - - String method() default ""; - - /** - * Use this to indicate the enum class that is going to be used in the multiple select config. - * This implementation made debugging problems with multiple selects a lot easier - * - * @return The Enum that will be used for the multiple select - */ - Class enumClass() default Enum.class; - } diff --git a/runelite-client/src/main/java/net/runelite/client/config/ConfigItemDescriptor.java b/runelite-client/src/main/java/net/runelite/client/config/ConfigItemDescriptor.java index 852d9fa873..d6ced58e18 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/ConfigItemDescriptor.java +++ b/runelite-client/src/main/java/net/runelite/client/config/ConfigItemDescriptor.java @@ -27,11 +27,29 @@ package net.runelite.client.config; import lombok.Value; @Value -public class ConfigItemDescriptor +public class ConfigItemDescriptor implements ConfigObject { private final ConfigItem item; private final Class type; private final Range range; private final Alpha alpha; private final Units units; + + @Override + public String key() + { + return item.keyName(); + } + + @Override + public String name() + { + return item.name(); + } + + @Override + public int position() + { + return item.position(); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java b/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java index cccadf7bc1..01d8ebfe1d 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java +++ b/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java @@ -1,6 +1,5 @@ /* * Copyright (c) 2017, Adam - * Copyright (c) 2020, ThatGamerBlue * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,8 +24,13 @@ */ package net.runelite.client.config; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; import com.google.common.collect.ComparisonChain; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.hash.Hasher; +import com.google.common.hash.Hashing; import java.awt.Color; import java.awt.Dimension; import java.awt.Point; @@ -45,78 +49,709 @@ import java.nio.charset.StandardCharsets; import java.nio.file.AtomicMoveNotSupportedException; import java.nio.file.Files; import java.nio.file.StandardCopyOption; +import java.security.SecureRandom; +import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.time.Duration; import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumSet; +import java.util.Base64; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Properties; -import java.util.Stack; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Predicate; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; import javax.annotation.Nullable; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.Player; import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.PlayerChanged; +import net.runelite.api.events.UsernameChanged; +import net.runelite.api.events.WorldChanged; import net.runelite.client.RuneLite; +import net.runelite.client.account.AccountSession; import net.runelite.client.eventbus.EventBus; +import net.runelite.client.eventbus.Subscribe; import net.runelite.client.events.ClientShutdown; import net.runelite.client.events.ConfigChanged; -import net.runelite.client.plugins.ExternalPluginManager; -import net.runelite.client.plugins.Plugin; +import net.runelite.client.events.RuneScapeProfileChanged; import net.runelite.client.util.ColorUtil; -import net.runelite.client.util.Groups; -import org.apache.commons.lang3.StringUtils; -import org.jgroups.Message; -import org.jgroups.util.Util; -import static net.runelite.client.RuneLite.PROFILES_DIR; +import net.runelite.http.api.config.ConfigClient; +import net.runelite.http.api.config.ConfigEntry; +import net.runelite.http.api.config.Configuration; +import okhttp3.OkHttpClient; @Singleton @Slf4j public class ConfigManager { - private static final String STANDARD_SETTINGS_FILE_NAME = "settings.properties"; - private static final File STANDARD_SETTINGS_FILE = new File(RuneLite.RUNELITE_DIR, STANDARD_SETTINGS_FILE_NAME); + public static final String RSPROFILE_GROUP = "rsprofile"; + + private static final String RSPROFILE_DISPLAY_NAME = "displayName"; + private static final String RSPROFILE_TYPE = "type"; + private static final String RSPROFILE_LOGIN_HASH = "loginHash"; + private static final String RSPROFILE_LOGIN_SALT = "loginSalt"; + + private static final DateFormat TIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss"); + + private static final int KEY_SPLITTER_GROUP = 0; + private static final int KEY_SPLITTER_PROFILE = 1; + private static final int KEY_SPLITTER_KEY = 2; + + private final File settingsFileInput; + private final EventBus eventBus; + private final OkHttpClient okHttpClient; + + private AccountSession session; + private ConfigClient configClient; + private File propertiesFile; + + @Nullable + private final Client client; + private final ConfigInvocationHandler handler = new ConfigInvocationHandler(this); private final Properties properties = new Properties(); private final Map pendingChanges = new HashMap<>(); - private final Map> consumers = new HashMap<>(); - private final File settingsFileInput; - private final Groups groups; - private final EventBus eventBus; + + // null => we need to make a new profile + @Nullable + private String rsProfileKey; @Inject public ConfigManager( @Named("config") File config, ScheduledExecutorService scheduledExecutorService, - Groups groups, - EventBus eventBus) + EventBus eventBus, + OkHttpClient okHttpClient, + @Nullable Client client) { this.settingsFileInput = config; - this.groups = groups; this.eventBus = eventBus; + this.okHttpClient = okHttpClient; + this.client = client; + this.propertiesFile = getPropertiesFile(); scheduledExecutorService.scheduleWithFixedDelay(this::sendConfig, 30, 30, TimeUnit.SECONDS); - eventBus.subscribe(ClientShutdown.class, this, ev -> ev.waitFor(shutdown())); - - groups.getMessageObjectSubject() - .subscribe(this::receive); } - @SuppressWarnings("unchecked") + public final void switchSession(AccountSession session) + { + // Ensure existing config is saved + sendConfig(); + + if (session == null) + { + this.session = null; + this.configClient = null; + } + else + { + this.session = session; + this.configClient = new ConfigClient(okHttpClient, session.getUuid()); + } + + this.propertiesFile = getPropertiesFile(); + + load(); // load profile specific config + } + + private File getLocalPropertiesFile() + { + return settingsFileInput; + } + + private File getPropertiesFile() + { + // Sessions that aren't logged in have no username + if (session == null || session.getUsername() == null) + { + return getLocalPropertiesFile(); + } + else + { + File profileDir = new File(RuneLite.PROFILES_DIR, session.getUsername().toLowerCase()); + return new File(profileDir, RuneLite.DEFAULT_CONFIG_FILE.getName()); + } + } + + public void load() + { + if (configClient == null) + { + loadFromFile(); + return; + } + + Configuration configuration; + + try + { + configuration = configClient.get(); + } + catch (IOException ex) + { + log.debug("Unable to load configuration from client, using saved configuration from disk", ex); + loadFromFile(); + return; + } + + if (configuration.getConfig() == null || configuration.getConfig().isEmpty()) + { + log.debug("No configuration from client, using saved configuration on disk"); + loadFromFile(); + return; + } + + handler.invalidate(); + properties.clear(); + + for (ConfigEntry entry : configuration.getConfig()) + { + log.debug("Loading configuration value from client {}: {}", entry.getKey(), entry.getValue()); + + String[] split = splitKey(entry.getKey()); + if (split == null) + { + continue; + } + + final String groupName = split[KEY_SPLITTER_GROUP]; + final String profile = split[KEY_SPLITTER_PROFILE]; + final String key = split[KEY_SPLITTER_KEY]; + final String value = entry.getValue(); + final String oldValue = (String) properties.setProperty(entry.getKey(), value); + + ConfigChanged configChanged = new ConfigChanged(); + configChanged.setGroup(groupName); + configChanged.setProfile(profile); + configChanged.setKey(key); + configChanged.setOldValue(oldValue); + configChanged.setNewValue(value); + eventBus.post(configChanged); + } + + migrateConfig(); + + try + { + saveToFile(propertiesFile); + + log.debug("Updated configuration on disk with the latest version"); + } + catch (IOException ex) + { + log.warn("Unable to update configuration on disk", ex); + } + } + + private synchronized void syncPropertiesFromFile(File propertiesFile) + { + final Properties properties = new Properties(); + try (FileInputStream in = new FileInputStream(propertiesFile)) + { + properties.load(new InputStreamReader(in, StandardCharsets.UTF_8)); + } + catch (Exception e) + { + log.debug("Malformed properties, skipping update"); + return; + } + + final Map copy = (Map) ImmutableMap.copyOf(this.properties); + copy.forEach((wholeKey, value) -> + { + if (!properties.containsKey(wholeKey)) + { + String[] split = splitKey(wholeKey); + if (split == null) + { + return; + } + + String groupName = split[KEY_SPLITTER_GROUP]; + String profile = split[KEY_SPLITTER_PROFILE]; + String key = split[KEY_SPLITTER_KEY]; + unsetConfiguration(groupName, profile, key); + } + }); + + properties.forEach((wholeKey, objValue) -> + { + String[] split = splitKey((String) wholeKey); + if (split == null) + { + return; + } + + String groupName = split[KEY_SPLITTER_GROUP]; + String profile = split[KEY_SPLITTER_PROFILE]; + String key = split[KEY_SPLITTER_KEY]; + String value = String.valueOf(objValue); + setConfiguration(groupName, profile, key, value); + }); + + migrateConfig(); + } + + public Future importLocal() + { + if (session == null) + { + // No session, no import + return null; + } + + final File file = new File(propertiesFile.getParent(), propertiesFile.getName() + "." + TIME_FORMAT.format(new Date())); + + try + { + saveToFile(file); + } + catch (IOException e) + { + log.warn("Backup failed, skipping import", e); + return null; + } + + syncPropertiesFromFile(getLocalPropertiesFile()); + + return sendConfig(); + } + + private synchronized void loadFromFile() + { + handler.invalidate(); + properties.clear(); + + try (FileInputStream in = new FileInputStream(propertiesFile)) + { + properties.load(new InputStreamReader(in, StandardCharsets.UTF_8)); + } + catch (FileNotFoundException ex) + { + log.debug("Unable to load settings - no such file"); + } + catch (IllegalArgumentException | IOException ex) + { + log.warn("Unable to load settings", ex); + } + + try + { + Map copy = (Map) ImmutableMap.copyOf(properties); + copy.forEach((wholeKey, value) -> + { + String[] split = splitKey(wholeKey); + if (split == null) + { + log.debug("Properties key malformed!: {}", wholeKey); + properties.remove(wholeKey); + return; + } + + String groupName = split[KEY_SPLITTER_GROUP]; + String profile = split[KEY_SPLITTER_PROFILE]; + String key = split[KEY_SPLITTER_KEY]; + + ConfigChanged configChanged = new ConfigChanged(); + configChanged.setGroup(groupName); + configChanged.setProfile(profile); + configChanged.setKey(key); + configChanged.setOldValue(null); + configChanged.setNewValue(value); + eventBus.post(configChanged); + }); + } + catch (Exception ex) + { + log.warn("Error posting config events", ex); + } + + migrateConfig(); + } + + private void saveToFile(final File propertiesFile) throws IOException + { + File parent = propertiesFile.getParentFile(); + + parent.mkdirs(); + + File tempFile = new File(parent, RuneLite.DEFAULT_CONFIG_FILE.getName() + ".tmp"); + + try (FileOutputStream out = new FileOutputStream(tempFile)) + { + out.getChannel().lock(); + properties.store(new OutputStreamWriter(out, StandardCharsets.UTF_8), "RuneLite configuration"); + // FileOutputStream.close() closes the associated channel, which frees the lock + } + + try + { + Files.move(tempFile.toPath(), propertiesFile.toPath(), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE); + } + catch (AtomicMoveNotSupportedException ex) + { + log.debug("atomic move not supported", ex); + Files.move(tempFile.toPath(), propertiesFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + } + } + + public T getConfig(Class clazz) + { + if (!Modifier.isPublic(clazz.getModifiers())) + { + throw new RuntimeException("Non-public configuration classes can't have default methods invoked"); + } + + T t = (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] + { + clazz + }, handler); + + return t; + } + + public List getConfigurationKeys(String prefix) + { + return properties.keySet().stream().filter(v -> ((String) v).startsWith(prefix)).map(String.class::cast).collect(Collectors.toList()); + } + + public static String getWholeKey(String groupName, String profile, String key) + { + if (profile == null) + { + return groupName + "." + key; + } + else + { + return groupName + "." + profile + "." + key; + } + } + + public String getConfiguration(String groupName, String key) + { + return getConfiguration(groupName, null, key); + } + + public String getRSProfileConfiguration(String groupName, String key) + { + String rsProfileKey = this.rsProfileKey; + if (rsProfileKey == null) + { + return null; + } + + return getConfiguration(groupName, rsProfileKey, key); + } + + public String getConfiguration(String groupName, String profile, String key) + { + return properties.getProperty(getWholeKey(groupName, profile, key)); + } + + public T getConfiguration(String groupName, String key, Class clazz) + { + return getConfiguration(groupName, null, key, clazz); + } + + public T getRSProfileConfiguration(String groupName, String key, Class clazz) + { + String rsProfileKey = this.rsProfileKey; + if (rsProfileKey == null) + { + return null; + } + + return getConfiguration(groupName, rsProfileKey, key, clazz); + } + + public T getConfiguration(String groupName, String profile, String key, Class clazz) + { + String value = getConfiguration(groupName, profile, key); + if (!Strings.isNullOrEmpty(value)) + { + try + { + return (T) stringToObject(value, clazz); + } + catch (Exception e) + { + log.warn("Unable to unmarshal {} ", getWholeKey(groupName, profile, key), e); + } + } + return null; + } + + public void setConfiguration(String groupName, String key, String value) + { + setConfiguration(groupName, null, key, value); + } + + public void setConfiguration(String groupName, String profile, String key, String value) + { + assert !key.startsWith(RSPROFILE_GROUP + "."); + String wholeKey = getWholeKey(groupName, profile, key); + String oldValue = (String) properties.setProperty(wholeKey, value); + + if (Objects.equals(oldValue, value)) + { + return; + } + + log.debug("Setting configuration value for {} to {}", wholeKey, value); + handler.invalidate(); + + synchronized (pendingChanges) + { + pendingChanges.put(wholeKey, value); + } + + ConfigChanged configChanged = new ConfigChanged(); + configChanged.setGroup(groupName); + configChanged.setProfile(profile); + configChanged.setKey(key); + configChanged.setOldValue(oldValue); + configChanged.setNewValue(value); + + eventBus.post(configChanged); + } + + public void setConfiguration(String groupName, String profile, String key, Object value) + { + setConfiguration(groupName, profile, key, objectToString(value)); + } + + public void setConfiguration(String groupName, String key, Object value) + { + setConfiguration(groupName, null, key, value); + } + + public void setRSProfileConfiguration(String groupName, String key, Object value) + { + String rsProfileKey = this.rsProfileKey; + if (rsProfileKey == null) + { + if (client == null) + { + log.warn("trying to use profile without injected client"); + return; + } + + String displayName = null; + Player p = client.getLocalPlayer(); + if (p == null) + { + log.warn("trying to create profile without display name"); + } + else + { + displayName = p.getName(); + } + + String username = client.getUsername(); + if (Strings.isNullOrEmpty(username)) + { + log.warn("trying to create profile without a set username"); + return; + } + + RuneScapeProfile prof = findRSProfile(getRSProfiles(), username, RuneScapeProfileType.getCurrent(client), displayName, true); + rsProfileKey = prof.getKey(); + this.rsProfileKey = rsProfileKey; + } + setConfiguration(groupName, rsProfileKey, key, value); + } + + public void unsetConfiguration(String groupName, String key) + { + unsetConfiguration(groupName, null, key); + } + + public void unsetConfiguration(String groupName, String profile, String key) + { + assert !key.startsWith(RSPROFILE_GROUP + "."); + String wholeKey = getWholeKey(groupName, profile, key); + String oldValue = (String) properties.remove(wholeKey); + + if (oldValue == null) + { + return; + } + + log.debug("Unsetting configuration value for {}", wholeKey); + handler.invalidate(); + + synchronized (pendingChanges) + { + pendingChanges.put(wholeKey, null); + } + + ConfigChanged configChanged = new ConfigChanged(); + configChanged.setGroup(groupName); + configChanged.setKey(key); + configChanged.setOldValue(oldValue); + + eventBus.post(configChanged); + } + + public void unsetRSProfileConfiguration(String groupName, String key) + { + String rsProfileKey = this.rsProfileKey; + if (rsProfileKey == null) + { + return; + } + + unsetConfiguration(groupName, rsProfileKey, key); + } + + public ConfigDescriptor getConfigDescriptor(Config configurationProxy) + { + Class inter = configurationProxy.getClass().getInterfaces()[0]; + ConfigGroup group = inter.getAnnotation(ConfigGroup.class); + + if (group == null) + { + throw new IllegalArgumentException("Not a config group"); + } + + final List sections = Arrays.stream(inter.getDeclaredFields()) + .filter(m -> m.isAnnotationPresent(ConfigSection.class) && m.getType() == String.class) + .map(m -> + { + try + { + return new ConfigSectionDescriptor( + String.valueOf(m.get(inter)), + m.getDeclaredAnnotation(ConfigSection.class) + ); + } + catch (IllegalAccessException e) + { + log.warn("Unable to load section {}::{}", inter.getSimpleName(), m.getName()); + return null; + } + }) + .filter(Objects::nonNull) + .sorted((a, b) -> ComparisonChain.start() + .compare(a.getSection().position(), b.getSection().position()) + .compare(a.getSection().name(), b.getSection().name()) + .result()) + .collect(Collectors.toList()); + + final List items = Arrays.stream(inter.getMethods()) + .filter(m -> m.getParameterCount() == 0 && m.isAnnotationPresent(ConfigItem.class)) + .map(m -> new ConfigItemDescriptor( + m.getDeclaredAnnotation(ConfigItem.class), + m.getReturnType(), + m.getDeclaredAnnotation(Range.class), + m.getDeclaredAnnotation(Alpha.class), + m.getDeclaredAnnotation(Units.class) + )) + .sorted((a, b) -> ComparisonChain.start() + .compare(a.getItem().position(), b.getItem().position()) + .compare(a.getItem().name(), b.getItem().name()) + .result()) + .collect(Collectors.toList()); + + return new ConfigDescriptor(group, sections, items); + } + + /** + * Initialize the configuration from the default settings + * + * @param proxy + */ + public void setDefaultConfiguration(Object proxy, boolean override) + { + Class clazz = proxy.getClass().getInterfaces()[0]; + ConfigGroup group = clazz.getAnnotation(ConfigGroup.class); + + if (group == null) + { + return; + } + + for (Method method : clazz.getDeclaredMethods()) + { + ConfigItem item = method.getAnnotation(ConfigItem.class); + + // only apply default configuration for methods which read configuration (0 args) + if (item == null || method.getParameterCount() != 0) + { + continue; + } + + if (!method.isDefault()) + { + if (override) + { + String current = getConfiguration(group.value(), item.keyName()); + // only unset if already set + if (current != null) + { + unsetConfiguration(group.value(), item.keyName()); + } + } + continue; + } + + if (!override) + { + // This checks if it is set and is also unmarshallable to the correct type; so + // we will overwrite invalid config values with the default + Object current = getConfiguration(group.value(), item.keyName(), method.getReturnType()); + if (current != null) + { + continue; // something else is already set + } + } + + Object defaultValue; + try + { + defaultValue = ConfigInvocationHandler.callDefaultMethod(proxy, method, null); + } + catch (Throwable ex) + { + log.warn(null, ex); + continue; + } + + String current = getConfiguration(group.value(), item.keyName()); + String valueString = objectToString(defaultValue); + // null and the empty string are treated identically in sendConfig and treated as an unset + // If a config value defaults to "" and the current value is null, it will cause an extra + // unset to be sent, so treat them as equal + if (Objects.equals(current, valueString) || (Strings.isNullOrEmpty(current) && Strings.isNullOrEmpty(valueString))) + { + continue; // already set to the default value + } + + log.debug("Setting default configuration value for {}.{} to {}", group.value(), item.keyName(), defaultValue); + + setConfiguration(group.value(), item.keyName(), valueString); + } + } + static Object stringToObject(String str, Class type) { if (type == boolean.class || type == Boolean.class) @@ -127,10 +762,6 @@ public class ConfigManager { return Integer.parseInt(str); } - if (type == long.class) - { - return Long.parseLong(str); - } if (type == Color.class) { return ColorUtil.fromString(str); @@ -189,64 +820,9 @@ public class ConfigManager { return Duration.ofMillis(Long.parseLong(str)); } - if (type == int[].class) + if (type == byte[].class) { - if (str.contains(",")) - { - return Arrays.stream(str.split(",")).mapToInt(Integer::valueOf).toArray(); - } - return new int[]{Integer.parseInt(str)}; - } - if (type == EnumSet.class) - { - try - { - String substring = str.substring(str.indexOf("{") + 1, str.length() - 1); - String[] splitStr = substring.split(", "); - Class enumClass = null; - if (!str.contains("{")) - { - return null; - } - - enumClass = findEnumClass(str, ExternalPluginManager.pluginClassLoaders); - - EnumSet enumSet = EnumSet.noneOf(enumClass); - for (String s : splitStr) - { - try - { - enumSet.add(Enum.valueOf(enumClass, s.replace("[", "").replace("]", ""))); - } - catch (IllegalArgumentException ignore) - { - return EnumSet.noneOf(enumClass); - } - } - return enumSet; - } - catch (Exception e) - { - e.printStackTrace(); - return null; - } - - } - if (type == Map.class) - { - Map output = new HashMap<>(); - str = str.substring(1, str.length() - 1); - String[] splitStr = str.split(", "); - for (String s : splitStr) - { - String[] keyVal = s.split("="); - if (keyVal.length > 1) - { - output.put(keyVal[0], keyVal[1]); - } - } - - return output; + return Base64.getUrlDecoder().decode(str); } return str; } @@ -295,644 +871,329 @@ public class ConfigManager { return Long.toString(((Duration) object).toMillis()); } - if (object instanceof int[]) + if (object instanceof byte[]) { - if (((int[]) object).length == 0) - { - return String.valueOf(object); - } - return StringUtils.join(object, ","); + return Base64.getUrlEncoder().encodeToString((byte[]) object); } - if (object instanceof EnumSet) - { - if (((EnumSet) object).size() == 0) - { - return getElementType((EnumSet) object).getCanonicalName() + "{}"; - } - - return ((EnumSet) object).toArray()[0].getClass().getCanonicalName() + "{" + object.toString() + "}"; - } - if (object instanceof Number) - { - return String.valueOf(object); - } - return object == null ? null : object.toString(); } - public static > Class getElementType(EnumSet enumSet) + @Subscribe(priority = 100) + private void onClientShutdown(ClientShutdown e) { - if (enumSet.isEmpty()) + Future f = sendConfig(); + if (f != null) { - enumSet = EnumSet.complementOf(enumSet); + e.waitFor(f); } - return enumSet.iterator().next().getDeclaringClass(); } - public static Class findEnumClass(String clasz, ArrayList classLoaders) + @Nullable + private CompletableFuture sendConfig() { - StringBuilder transformedString = new StringBuilder(); - for (ClassLoader cl : classLoaders) + CompletableFuture future = null; + synchronized (pendingChanges) { - try + if (pendingChanges.isEmpty()) { - String[] strings = clasz.substring(0, clasz.indexOf("{")).split("\\."); - int i = 0; - while (i != strings.length) - { - if (i == 0) - { - transformedString.append(strings[i]); - } - else if (i == strings.length - 1) - { - transformedString.append("$").append(strings[i]); - } - else - { - transformedString.append(".").append(strings[i]); - } - i++; - } - return (Class) cl.loadClass(transformedString.toString()); - } - catch (Exception e2) - { - // Will likely fail a lot - } - try - { - return (Class) cl.loadClass(clasz.substring(0, clasz.indexOf("{"))); - } - catch (Exception e) - { - // Will likely fail a lot - } - transformedString = new StringBuilder(); - } - throw new RuntimeException("Failed to find Enum for " + clasz.substring(0, clasz.indexOf("{"))); - } - - public final void switchSession() - { - // Ensure existing config is saved - load(); - } - - public void load() - { - loadFromFile(); - } - - private synchronized void syncPropertiesFromFile(File propertiesFile) - { - final Properties properties = new Properties(); - try (FileInputStream in = new FileInputStream(propertiesFile)) - { - properties.load(new InputStreamReader(in, StandardCharsets.UTF_8)); - } - catch (Exception e) - { - log.debug("Malformed properties, skipping update"); - return; - } - - @SuppressWarnings("unchecked") final Map copy = (Map) Map.copyOf(this.properties); - copy.forEach((groupAndKey, value) -> - { - if (!properties.containsKey(groupAndKey)) - { - final String[] split = groupAndKey.split("\\.", 2); - if (split.length != 2) - { - return; - } - - final String groupName = split[0]; - final String key = split[1]; - unsetConfiguration(groupName, key); - } - }); - - properties.forEach((objGroupAndKey, objValue) -> - { - final String groupAndKey = String.valueOf(objGroupAndKey); - final String[] split = groupAndKey.split("\\.", 2); - if (split.length != 2) - { - return; + return null; } - final String groupName = split[0]; - final String key = split[1]; - final String value = String.valueOf(objValue); - setConfiguration(groupName, key, value); - }); - } + if (configClient != null) + { + Configuration patch = new Configuration(pendingChanges.entrySet().stream() + .map(e -> new ConfigEntry(e.getKey(), e.getValue())) + .collect(Collectors.toList())); - public void importLocal() - { - log.info("Nothing changed, don't worry!"); - } + future = configClient.patch(patch); + } - private synchronized void loadFromFile() - { - handler.invalidate(); - properties.clear(); - consumers.clear(); - - try (FileInputStream in = new FileInputStream(settingsFileInput)) - { - properties.load(new InputStreamReader(in, StandardCharsets.UTF_8)); - } - catch (FileNotFoundException ex) - { - log.debug("Unable to load settings - no such file, syncing from standard settings"); - syncLastModified(); - } - catch (IllegalArgumentException | IOException ex) - { - log.warn("Unable to load settings", ex); + pendingChanges.clear(); } try { - @SuppressWarnings("unchecked") Map copy = (Map) Map.copyOf(properties); - copy.forEach((groupAndKey, value) -> - { - final String[] split = groupAndKey.split("\\.", 2); - if (split.length != 2) - { - log.debug("Properties key malformed!: {}", groupAndKey); - properties.remove(groupAndKey); - return; - } - - final String groupName = split[0]; - final String key = split[1]; - - ConfigChanged configChanged = new ConfigChanged(); - configChanged.setGroup(groupName); - configChanged.setKey(key); - configChanged.setOldValue(null); - configChanged.setNewValue(value); - - eventBus.post(ConfigChanged.class, configChanged); - }); + saveToFile(propertiesFile); } - catch (Exception ex) + catch (IOException ex) { - log.warn("Error posting config events", ex); + log.warn("unable to save configuration file", ex); } + + return future; } - private void saveToFile() throws IOException + public List getRSProfiles() { - settingsFileInput.getParentFile().mkdirs(); - - File tempFile = new File(RuneLite.RUNELITE_DIR, RuneLite.DEFAULT_CONFIG_FILE.getName() + ".tmp"); - - try (FileOutputStream out = new FileOutputStream(tempFile)) + String prefix = RSPROFILE_GROUP + "." + RSPROFILE_GROUP + "."; + Set profileKeys = new HashSet<>(); + for (Object oKey : properties.keySet()) { - out.getChannel().lock(); - properties.store(new OutputStreamWriter(out, StandardCharsets.UTF_8), "RuneLite configuration"); - // FileOutputStream.close() closes the associated channel, which frees the lock - } - - try - { - Files.move(tempFile.toPath(), settingsFileInput.toPath(), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE); - } - catch (AtomicMoveNotSupportedException ex) - { - log.debug("atomic move not supported", ex); - Files.move(tempFile.toPath(), settingsFileInput.toPath(), StandardCopyOption.REPLACE_EXISTING); - } - } - - @SuppressWarnings("unchecked") - public T getConfig(Class clazz) - { - if (!Modifier.isPublic(clazz.getModifiers())) - { - throw new RuntimeException("Non-public configuration classes can't have default methods invoked"); - } - - return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] - { - clazz - }, handler); - } - - public List getConfigurationKeys(String prefix) - { - return properties.keySet().stream().filter(v -> ((String) v).startsWith(prefix)).map(String.class::cast).collect(Collectors.toList()); - } - - public String getConfiguration(String groupName, String key) - { - return properties.getProperty(groupName + "." + key); - } - - @SuppressWarnings("unchecked") - public T getConfiguration(String groupName, String key, Class clazz) - { - String value = getConfiguration(groupName, key); - if (!Strings.isNullOrEmpty(value)) - { - try - { - return (T) stringToObject(value, clazz); - } - catch (Exception e) - { - log.warn("Unable to unmarshal {}.{} ", groupName, key, e); - } - } - return null; - } - - public void setConfiguration(String groupName, String key, Object value) - { - // do not save consumers for buttons, they cannot be changed anyway - if (value instanceof Consumer) - { - return; - } - - setConfiguration(groupName, key, objectToString(value)); - } - - public void setConfiguration(String groupName, String key, String value) - { - setConfiguration(groupName, key, value, null); - } - - public void setConfiguration(String groupName, String key, String value, String origin) - { - String oldValue = (String) properties.setProperty(groupName + "." + key, value); - - if (Objects.equals(oldValue, value)) - { - return; - } - - log.debug("Setting configuration value for {}.{} to {}", groupName, key, value); - handler.invalidate(); - - synchronized (pendingChanges) - { - pendingChanges.put(groupName + "." + key, value); - } - - ConfigChanged configChanged = new ConfigChanged(); - configChanged.setGroup(groupName); - configChanged.setKey(key); - configChanged.setOldValue(oldValue); - configChanged.setNewValue(value); - configChanged.setOrigin(origin == null ? RuneLite.uuid : origin); - configChanged.setPath(settingsFileInput.getAbsolutePath()); - - eventBus.post(ConfigChanged.class, configChanged); - - if (origin == null) - { - broadcast(configChanged); - } - } - - public void unsetConfiguration(String groupName, String key) - { - unsetConfiguration(groupName, key, null); - } - - public void unsetConfiguration(String groupName, String key, String origin) - { - String oldValue = (String) properties.remove(groupName + "." + key); - - if (oldValue == null) - { - return; - } - - log.debug("Unsetting configuration value for {}.{}", groupName, key); - handler.invalidate(); - - synchronized (pendingChanges) - { - pendingChanges.put(groupName + "." + key, null); - } - - ConfigChanged configChanged = new ConfigChanged(); - configChanged.setGroup(groupName); - configChanged.setKey(key); - configChanged.setOldValue(oldValue); - configChanged.setOrigin(origin == null ? RuneLite.uuid : origin); - configChanged.setPath(settingsFileInput.getAbsolutePath()); - - eventBus.post(ConfigChanged.class, configChanged); - - if (origin == null) - { - broadcast(configChanged); - } - } - - public ConfigDescriptor getConfigDescriptor(Object configurationProxy) - { - Class inter = configurationProxy.getClass().getInterfaces()[0]; - ConfigGroup group = inter.getAnnotation(ConfigGroup.class); - - if (group == null) - { - throw new IllegalArgumentException("Not a config group"); - } - - final List sections = Arrays.stream(inter.getMethods()) - .filter(m -> m.getParameterCount() == 0 && m.isAnnotationPresent(ConfigSection.class) && m.getReturnType() == boolean.class) - .map(m -> m.getDeclaredAnnotation(ConfigSection.class)) - .sorted((a, b) -> ComparisonChain.start() - .compare(a.position(), b.position()) - .compare(a.name(), b.name()) - .result()) - .collect(Collectors.toList()); - - final List titleSections = Arrays.stream(inter.getMethods()) - .filter(m -> m.getParameterCount() == 0 && m.isAnnotationPresent(ConfigTitleSection.class)) - .map(m -> m.getDeclaredAnnotation(ConfigTitleSection.class)) - .sorted((a, b) -> ComparisonChain.start() - .compare(a.position(), b.position()) - .compare(a.name(), b.name()) - .result()) - .collect(Collectors.toList()); - - final List items = Arrays.stream(inter.getMethods()) - .filter(m -> m.getParameterCount() == 0 && m.isAnnotationPresent(ConfigItem.class)) - .map(m -> new ConfigItemDescriptor( - m.getDeclaredAnnotation(ConfigItem.class), - m.getReturnType(), - m.getDeclaredAnnotation(Range.class), - m.getDeclaredAnnotation(Alpha.class), - m.getDeclaredAnnotation(Units.class) - )) - .sorted((a, b) -> ComparisonChain.start() - .compare(a.getItem().position(), b.getItem().position()) - .compare(a.getItem().name(), b.getItem().name()) - .result()) - .collect(Collectors.toList()); - - return new ConfigDescriptor(group, sections, titleSections, items); - } - - /** - * Initialize the configuration from the default settings - * - * @param proxy - */ - public void setDefaultConfiguration(Object proxy, boolean override) - { - Class clazz = proxy.getClass().getInterfaces()[0]; - ConfigGroup group = clazz.getAnnotation(ConfigGroup.class); - - if (group == null) - { - return; - } - - for (Method method : getAllDeclaredInterfaceMethods(clazz)) - { - ConfigItem item = method.getAnnotation(ConfigItem.class); - - // only apply default configuration for methods which read configuration (0 args) - if (item == null || method.getParameterCount() != 0) + String key = (String) oKey; + if (!key.startsWith(prefix)) { continue; } - if (method.getReturnType().isAssignableFrom(Consumer.class)) + String[] split = splitKey(key); + if (split == null) { - Object defaultValue; - try - { - defaultValue = ConfigInvocationHandler.callDefaultMethod(proxy, method, null); - } - catch (Throwable ex) - { - log.warn(null, ex); - continue; - } - - log.debug("Registered consumer: {}.{}", group.value(), item.keyName()); - consumers.put(group.value() + "." + item.keyName(), (Consumer) defaultValue); + continue; } - else - { - if (!method.isDefault()) - { - if (override) - { - String current = getConfiguration(group.value(), item.keyName()); - // only unset if already set - if (current != null) - { - unsetConfiguration(group.value(), item.keyName()); - } - } - continue; - } - if (!override) - { - // This checks if it is set and is also unmarshallable to the correct type; so - // we will overwrite invalid config values with the default - Object current = getConfiguration(group.value(), item.keyName(), method.getReturnType()); - if (current != null) - { - continue; // something else is already set - } - } - - Object defaultValue; - try - { - defaultValue = ConfigInvocationHandler.callDefaultMethod(proxy, method, null); - } - catch (Throwable ex) - { - log.warn(null, ex); - continue; - } - - String current = getConfiguration(group.value(), item.keyName()); - if (defaultValue == null) - { - log.error("Plugin with bad default configuration, see config group {}, keyName {}", group.value(), item.keyName()); - continue; - } - String valueString = objectToString(defaultValue); - // null and the empty string are treated identically in sendConfig and treated as an unset - // If a config value defaults to "" and the current value is null, it will cause an extra - // unset to be sent, so treat them as equal - if (Objects.equals(current, valueString) || (Strings.isNullOrEmpty(current) && Strings.isNullOrEmpty(valueString))) - { - continue; // already set to the default value - } - - log.debug("Setting default configuration value for {}.{} to {}", group.value(), item.keyName(), defaultValue); - - setConfiguration(group.value(), item.keyName(), valueString); - } + profileKeys.add(split[KEY_SPLITTER_PROFILE]); } + + return profileKeys.stream() + .map(key -> + { + RuneScapeProfile prof = new RuneScapeProfile( + getConfiguration(RSPROFILE_GROUP, key, RSPROFILE_DISPLAY_NAME), + getConfiguration(RSPROFILE_GROUP, key, RSPROFILE_TYPE, RuneScapeProfileType.class), + getConfiguration(RSPROFILE_GROUP, key, RSPROFILE_LOGIN_HASH, byte[].class), + key + ); + + return prof; + }) + .collect(Collectors.toList()); } - /** - * Initialize the configuration from the default settings, for an external Plugin - * - * @param proxy - */ - public void setDefaultConfiguration(Object proxy, boolean override, Plugin plugin) + private synchronized RuneScapeProfile findRSProfile(List profiles, String username, RuneScapeProfileType type, String displayName, boolean create) { - setDefaultConfiguration(proxy, override); - } - - public void sendConfig() - { - boolean changed; - synchronized (pendingChanges) + byte[] salt = getConfiguration(RSPROFILE_GROUP, RSPROFILE_LOGIN_SALT, byte[].class); + if (salt == null) { - changed = !pendingChanges.isEmpty(); - pendingChanges.clear(); + salt = new byte[15]; + new SecureRandom() + .nextBytes(salt); + setConfiguration(RSPROFILE_GROUP, RSPROFILE_LOGIN_SALT, salt); } - if (changed) + Hasher h = Hashing.sha512().newHasher(); + h.putBytes(salt); + h.putString(username.toLowerCase(Locale.US), StandardCharsets.UTF_8); + byte[] loginHash = h.hash().asBytes(); + + Set matches = profiles.stream() + .filter(p -> Arrays.equals(p.getLoginHash(), loginHash) && p.getType() == type) + .collect(Collectors.toSet()); + + if (matches.size() > 1) { - try + log.warn("multiple matching profiles"); + } + + if (matches.size() >= 1) + { + return matches.iterator().next(); + } + + if (!create) + { + return null; + } + + // generate the new key deterministically so if you "create" the same profile on 2 different clients it doesn't duplicate + Set keys = profiles.stream().map(RuneScapeProfile::getKey).collect(Collectors.toSet()); + byte[] key = Arrays.copyOf(loginHash, 6); + key[0] += type.ordinal(); + for (int i = 0; i < 0xFF; i++, key[1]++) + { + String keyStr = RSPROFILE_GROUP + "." + Base64.getUrlEncoder().encodeToString(key); + if (!keys.contains(keyStr)) { - saveToFile(); - } - catch (IOException ex) - { - log.warn("unable to save configuration file", ex); + log.info("creating new profile {} for user {}", key, username); + + setConfiguration(RSPROFILE_GROUP, keyStr, RSPROFILE_LOGIN_HASH, loginHash); + setConfiguration(RSPROFILE_GROUP, keyStr, RSPROFILE_TYPE, type); + if (displayName != null) + { + setConfiguration(RSPROFILE_GROUP, keyStr, RSPROFILE_DISPLAY_NAME, displayName); + } + return new RuneScapeProfile(displayName, type, loginHash, keyStr); } } + throw new RuntimeException("too many rs profiles"); } - /** - * Does DFS on a class's interfaces to find all of its implemented methods. - */ - private Collection getAllDeclaredInterfaceMethods(Class clazz) + private void updateRSProfile() { - Collection methods = new HashSet<>(); - Stack> interfazes = new Stack<>(); - interfazes.push(clazz); - - while (!interfazes.isEmpty()) - { - Class interfaze = interfazes.pop(); - Collections.addAll(methods, interfaze.getDeclaredMethods()); - Collections.addAll(interfazes, interfaze.getInterfaces()); - } - - return methods; - } - - private void syncLastModified() - { - File newestFile; - - newestFile = STANDARD_SETTINGS_FILE; - - File[] profileDirFiles = PROFILES_DIR.listFiles(); - - if (profileDirFiles != null) - { - for (File profileDir : profileDirFiles) - { - if (!profileDir.isDirectory()) - { - continue; - } - - File[] settingsFiles = profileDir.listFiles(); - - if (settingsFiles == null) - { - continue; - } - - for (File settings : settingsFiles) - { - if (!settings.getName().equals(STANDARD_SETTINGS_FILE_NAME) || - settings.lastModified() < newestFile.lastModified()) - { - continue; - } - - newestFile = settings; - } - } - } - - syncPropertiesFromFile(newestFile); - } - - private void broadcast(ConfigChanged configChanged) - { - groups.sendConfig(null, configChanged); - } - - public void receive(Message message) - { - if (message.getObject() instanceof String) + if (client == null) { return; } - try + List profiles = getRSProfiles(); + RuneScapeProfile prof = findRSProfile(profiles, client.getUsername(), RuneScapeProfileType.getCurrent(client), null, false); + + String key = prof == null ? null : prof.getKey(); + if (Objects.equals(key, rsProfileKey)) { - ConfigChanged configChanged = Util.objectFromByteBuffer(message.getObject()); - - if (!configChanged.getPath().equals(settingsFileInput.getAbsolutePath())) - { - return; - } - - if (configChanged.getNewValue() == null) - { - unsetConfiguration(configChanged.getGroup(), configChanged.getKey(), configChanged.getOrigin()); - } - else - { - setConfiguration(configChanged.getGroup(), configChanged.getKey(), configChanged.getNewValue(), configChanged.getOrigin()); - } + return; } - catch (Exception e) + rsProfileKey = key; + + eventBus.post(new RuneScapeProfileChanged()); + } + + @Subscribe + private void onUsernameChanged(UsernameChanged ev) + { + updateRSProfile(); + } + + @Subscribe + private void onWorldChanged(WorldChanged ev) + { + updateRSProfile(); + } + + @Subscribe + private void onPlayerChanged(PlayerChanged ev) + { + if (ev.getPlayer() == client.getLocalPlayer()) { - e.printStackTrace(); + String name = ev.getPlayer().getName(); + setRSProfileConfiguration(RSPROFILE_GROUP, RSPROFILE_DISPLAY_NAME, name); } } /** - * Retrieves a consumer from config group and key name + * Split a config key into (group, profile, key) + * @param key in form group.(rsprofile.profile.)?key + * @return an array of {group, profile, key} */ - public Consumer getConsumer(final String configGroup, final String keyName) + @VisibleForTesting + @Nullable + static String[] splitKey(String key) { - return consumers.getOrDefault(configGroup + "." + keyName, (p) -> log.error("Failed to retrieve consumer with name {}.{}", configGroup, keyName)); + int i = key.indexOf('.'); + if (i == -1) + { + // all keys must have a group and key + return null; + } + + String group = key.substring(0, i); + String profile = null; + key = key.substring(i + 1); + if (key.startsWith(RSPROFILE_GROUP + ".")) + { + i = key.indexOf('.', RSPROFILE_GROUP.length() + 2); // skip . after RSPROFILE_GROUP + profile = key.substring(0, i); + key = key.substring(i + 1); + } + return new String[]{group, profile, key}; } - private Future shutdown() + private synchronized void migrateConfig() { - CompletableFuture fut = new CompletableFuture<>(); - try + String migrationKey = "profileMigrationDone"; + if (getConfiguration("runelite", migrationKey) != null) { - this.sendConfig(); - fut.complete(null); - } - catch (Exception ex) - { - fut.completeExceptionally(ex); + return; } - return fut; + Map profiles = new HashMap<>(); + + AtomicInteger changes = new AtomicInteger(); + List> migrators = new ArrayList<>(); + for (String[] tpl : new String[][] + { + {"(grandexchange)\\.buylimit_(%)\\.(#)", "$1.buylimit.$3"}, + {"(timetracking)\\.(%)\\.(autoweed|contract)", "$1.$3"}, + {"(timetracking)\\.(%)\\.(#\\.#)", "$1.$3"}, + {"(timetracking)\\.(%)\\.(birdhouse)\\.(#)", "$1.$3.$4"}, + {"(killcount|personalbest)\\.(%)\\.([^.]+)", "$1.$3"}, + {"(geoffer)\\.(%)\\.(#)", "$1.$3"}, + }) + { + String replace = tpl[1]; + String pat = ("^" + tpl[0] + "$") + .replace("#", "-?[0-9]+") + .replace("(%)", "(?.*)"); + Pattern p = Pattern.compile(pat); + + migrators.add(oldkey -> + { + Matcher m = p.matcher(oldkey); + if (!m.find()) + { + return false; + } + + String newKey = m.replaceFirst(replace); + String username = m.group("login").toLowerCase(Locale.US); + + if (username.startsWith(RSPROFILE_GROUP + ".")) + { + return false; + } + + String profKey = profiles.computeIfAbsent(username, u -> + findRSProfile(getRSProfiles(), u, RuneScapeProfileType.STANDARD, u, true).getKey()); + + String[] oldKeySplit = splitKey(oldkey); + if (oldKeySplit == null) + { + log.warn("skipping migration of invalid key \"{}\"", oldkey); + return false; + } + if (oldKeySplit[KEY_SPLITTER_PROFILE] != null) + { + log.debug("skipping migrated key \"{}\"", oldkey); + return false; + } + + String[] newKeySplit = splitKey(newKey); + if (newKeySplit == null || newKeySplit[KEY_SPLITTER_PROFILE] != null) + { + log.warn("migration produced a bad key: \"{}\" -> \"{}\"", oldkey, newKey); + return false; + } + + if (changes.getAndAdd(1) <= 0) + { + File file = new File(propertiesFile.getParent(), propertiesFile.getName() + "." + TIME_FORMAT.format(new Date())); + log.info("backing up pre-migration config to {}", file); + try + { + saveToFile(file); + } + catch (IOException e) + { + log.error("Backup failed", e); + throw new RuntimeException(e); + } + } + + String oldGroup = oldKeySplit[KEY_SPLITTER_GROUP]; + String oldKeyPart = oldKeySplit[KEY_SPLITTER_KEY]; + String value = getConfiguration(oldGroup, oldKeyPart); + setConfiguration(newKeySplit[KEY_SPLITTER_GROUP], profKey, newKeySplit[KEY_SPLITTER_KEY], value); + unsetConfiguration(oldGroup, oldKeyPart); + return true; + }); + } + + Set keys = (Set) ImmutableSet.copyOf((Set) properties.keySet()); + keys: + for (String key : keys) + { + for (Predicate mig : migrators) + { + if (mig.test(key)) + { + continue keys; + } + } + } + + if (changes.get() > 0) + { + log.info("migrated {} config keys", changes); + } + setConfiguration("runelite", migrationKey, 1); } } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableAlignment.java b/runelite-client/src/main/java/net/runelite/client/config/ConfigObject.java similarity index 87% rename from runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableAlignment.java rename to runelite-client/src/main/java/net/runelite/client/config/ConfigObject.java index 32acd39bca..0e8010e58e 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableAlignment.java +++ b/runelite-client/src/main/java/net/runelite/client/config/ConfigObject.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, TheStonedTurtle + * Copyright (c) 2020, Hydrox6 * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,11 +22,11 @@ * (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.overlay.components.table; +package net.runelite.client.config; -public enum TableAlignment +public interface ConfigObject { - LEFT, - CENTER, - RIGHT + String key(); + String name(); + int position(); } diff --git a/runelite-client/src/main/java/net/runelite/client/config/ConfigSection.java b/runelite-client/src/main/java/net/runelite/client/config/ConfigSection.java index cb243f6692..4a0f722d3f 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/ConfigSection.java +++ b/runelite-client/src/main/java/net/runelite/client/config/ConfigSection.java @@ -30,79 +30,14 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) +@Target(ElementType.FIELD) public @interface ConfigSection { - /** - * Displayed position of the section. - * - * @return The index of the section. - */ - int position(); - - /** - * This is not visible to users - * - * @return name used for finding the config section - * from the properties map. Hence, KEY name. - */ - String keyName(); - - /** - * This is the name that is shown to users when looking - * at the config panel. - *

- * Choose a name carefully, as there is a maximum width - * that depends on the users DPI scaling. Short is best. - * - * @return display name for the config section. - */ String name(); - /** - * This will be shown to the user if they are hovering - * the config item in the config panel. - * - * @return the description of the config item. - */ String description(); - /** - * Setting this will tell the panel - * that this section should be placed beneath - * said section. - * - * @return parent section. - */ - String section() default ""; + int position(); - /** - * NOT USED. - */ - String titleSection() default ""; - - /** - * NOT USED. - */ - boolean hidden() default false; - - /** - * NOT USED. - */ - String unhide() default ""; - - /** - * NOT USED. - */ - String unhideValue() default ""; - - /** - * NOT USED. - */ - String hide() default ""; - - /** - * NOT USED. - */ - String hideValue() default ""; + boolean closedByDefault() default false; } diff --git a/runelite-client/src/main/java/net/runelite/client/graphics/PixelDistanceAlpha.java b/runelite-client/src/main/java/net/runelite/client/config/ConfigSectionDescriptor.java similarity index 77% rename from runelite-client/src/main/java/net/runelite/client/graphics/PixelDistanceAlpha.java rename to runelite-client/src/main/java/net/runelite/client/config/ConfigSectionDescriptor.java index 4c3232399d..9a8447ee49 100644 --- a/runelite-client/src/main/java/net/runelite/client/graphics/PixelDistanceAlpha.java +++ b/runelite-client/src/main/java/net/runelite/client/config/ConfigSectionDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Woox + * Copyright (c) 2020, Hydrox6 * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,15 +22,31 @@ * (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.graphics; +package net.runelite.client.config; -import lombok.RequiredArgsConstructor; import lombok.Value; @Value -@RequiredArgsConstructor -class PixelDistanceAlpha +public class ConfigSectionDescriptor implements ConfigObject { - private final int outerAlpha; - private final int distArrayPos; + private final String key; + private final ConfigSection section; + + @Override + public String key() + { + return key; + } + + @Override + public String name() + { + return section.name(); + } + + @Override + public int position() + { + return section.position(); + } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/config/ConfigTitleSection.java b/runelite-client/src/main/java/net/runelite/client/config/ConfigTitleSection.java deleted file mode 100644 index 0b63aec49e..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/config/ConfigTitleSection.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2019, Hydrox6 - * 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.config; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface ConfigTitleSection -{ - /** - * Displayed position of the title section. - * - * @return The index of the title section. - */ - int position(); - - /** - * This is not visible to users - * - * @return name used for finding the config section - * from the properties map. Hence, KEY name. - */ - String keyName(); - - /** - * This is the name that is shown to users when looking - * at the config panel. - *

- * Choose a name carefully, as there is a maximum width - * that depends on the users DPI scaling. Short is best. - * - * @return display name for the config title section. - */ - String name(); - - /** - * This will be shown to the user if they are hovering - * the config item in the config panel. - * - * @return the description of the config item. - */ - String description(); - - /** - * Setting this will tell the panel - * that this title should be placed beneath - * said section. - * - * @return parent section. - */ - String section() default ""; - - /** - * Setting this will tell the panel - * that this title should be placed beneath - * said title. - * - * @return parent title section. - */ - String titleSection() default ""; - - /** - * NOT USED. - */ - boolean hidden() default false; - - /** - * NOT USED. - */ - String unhide() default ""; - - /** - * NOT USED. - */ - String unhideValue() default ""; - - /** - * NOT USED. - */ - String hide() default ""; - - /** - * NOT USED. - */ - String hideValue() default ""; -} diff --git a/runelite-client/src/main/java/net/runelite/client/config/FontType.java b/runelite-client/src/main/java/net/runelite/client/config/FontType.java index 8cf3dcbad8..8ed57bd302 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/FontType.java +++ b/runelite-client/src/main/java/net/runelite/client/config/FontType.java @@ -24,11 +24,12 @@ */ package net.runelite.client.config; -import java.awt.Font; -import lombok.Getter; import lombok.RequiredArgsConstructor; +import lombok.Getter; import net.runelite.client.ui.FontManager; +import java.awt.Font; + @Getter @RequiredArgsConstructor public enum FontType diff --git a/runelite-client/src/main/java/net/runelite/client/config/Keybind.java b/runelite-client/src/main/java/net/runelite/client/config/Keybind.java index 7867e6ff3e..e7e338a6b3 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/Keybind.java +++ b/runelite-client/src/main/java/net/runelite/client/config/Keybind.java @@ -60,7 +60,7 @@ public class Keybind private final int keyCode; private final int modifiers; - Keybind(int keyCode, int modifiers, boolean ignoreModifiers) + protected Keybind(int keyCode, int modifiers, boolean ignoreModifiers) { modifiers &= KEYBOARD_MODIFIER_MASK; @@ -108,7 +108,7 @@ public class Keybind return matches(e, false); } - boolean matches(KeyEvent e, boolean ignoreModifiers) + protected boolean matches(KeyEvent e, boolean ignoreModifiers) { if (NOT_SET.equals(this)) { @@ -177,7 +177,7 @@ public class Keybind return mod; } - private static String getModifiersExText(int modifiers) + public static String getModifiersExText(int modifiers) { StringBuilder buf = new StringBuilder(); if ((modifiers & InputEvent.META_DOWN_MASK) != 0) diff --git a/runelite-client/src/main/java/net/runelite/client/config/LauncherConfig.java b/runelite-client/src/main/java/net/runelite/client/config/LauncherConfig.java deleted file mode 100644 index ad8d912c4b..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/config/LauncherConfig.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (c) 2019 Owain van Brakel - * 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.config; - -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; - -@ConfigGroup("openosrs") -public interface LauncherConfig extends Config -{ - @Getter(AccessLevel.PRIVATE) - @AllArgsConstructor - enum BootstrapMode - { - STABLE("Stable"), - NIGHTLY("Nightly"); - - private String name; - - @Override - public String toString() - { - return getName(); - } - } - - @ConfigTitleSection( - keyName = "launcherTitle", - name = "Launcher", - description = "", - position = -1 - ) - default Title launcherTitle() - { - return new Title(); - } - - @ConfigTitleSection( - keyName = "updateChannelTitle", - name = "Update channel", - description = "", - position = 1, - titleSection = "launcherTitle" - ) - default Title updateChannelTitle() - { - return new Title(); - } - - @ConfigItem( - position = 2, - keyName = "askMode", - name = "Prompt for update channel", - description = "Ask for nightly or stable every startup", - titleSection = "updateChannelTitle" - ) - default boolean askMode() - { - return true; - } - - @ConfigItem( - keyName = "bootstrapMode", - name = "Update channel", - description = "Select the update channel", - titleSection = "updateChannelTitle", - position = 3, - hide = "askMode" - ) - default BootstrapMode bootstrapMode() - { - return BootstrapMode.STABLE; - } - - @ConfigTitleSection( - keyName = "miscLauncherTitle", - name = "Miscellaneous", - description = "", - position = 4, - titleSection = "launcherTitle" - ) - default Title miscLauncherTitle() - { - return new Title(); - } - - @ConfigItem( - position = 5, - keyName = "disableHw", - name = "Disable hardware acceleration", - description = "Enable this if you have graphical issues", - titleSection = "miscLauncherTitle", - warning = "Toggling this setting requires a restart of the client" - ) - default boolean disableHw() - { - return false; - } - - @ConfigTitleSection( - keyName = "advancedTitle", - name = "Advanced", - description = "", - position = 6, - titleSection = "launcherTitle" - ) - default Title advancedTitle() - { - return new Title(); - } - - @ConfigItem( - position = 7, - keyName = "noJvm", - name = "Use system java (caution!)", - description = "Enable this if you want to make use of the system java version instead of the launcher bundled version", - titleSection = "advancedTitle", - warning = "Toggling this setting requires a restart of the client" - ) - default boolean noJvm() - { - return false; - } - - @ConfigItem( - position = 8, - keyName = "useProxy", - name = "Use SOCKS5 proxy", - description = "Enable the client to use a proxy", - titleSection = "advancedTitle", - warning = "Toggling this setting requires a restart of the client" - ) - default boolean useProxy() - { - return false; - } - - @ConfigItem( - keyName = "proxyDetails", - name = "Proxy details", - description = "The format for this field is ip:port or ip:port:user:pass", - titleSection = "advancedTitle", - position = 9, - hidden = true, - unhide = "useProxy" - ) - default String proxyDetails() - { - return ""; - } -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/config/OpenOSRSConfig.java b/runelite-client/src/main/java/net/runelite/client/config/OpenOSRSConfig.java deleted file mode 100644 index 4ecdd1d6d2..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/config/OpenOSRSConfig.java +++ /dev/null @@ -1,373 +0,0 @@ -/* - * - * Copyright (c) 2019, Zeruth - * 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.config; - -import java.awt.Color; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; -import net.runelite.client.plugins.ExternalPluginManager; - -@ConfigGroup("openosrs") -public interface OpenOSRSConfig extends Config -{ - @Getter(AccessLevel.PUBLIC) - @AllArgsConstructor - enum SortStyle - { - CATEGORY("Category"), - ALPHABETICALLY("Alphabetically"), - REPOSITORY("Repository"); - - private String name; - - @Override - public String toString() - { - return getName(); - } - } - - @ConfigTitleSection( - keyName = "logTitle", - name = "Error data", - description = "", - position = 1 - ) - default Title logTitle() - { - return new Title(); - } - - @ConfigItem( - position = 3, - keyName = "shareLogs", - name = "Share anonymous error data", - description = "Share anonymous error data with the OpenOSRS developers", - titleSection = "logTitle" - ) - default boolean shareLogs() - { - return true; - } - - @ConfigTitleSection( - keyName = "pluginsTitle", - name = "Plugins", - description = "", - position = 1 - ) - default Title pluginsTitle() - { - return new Title(); - } - - @ConfigTitleSection( - keyName = "pluginSortingTitle", - name = "Sorting", - description = "", - position = 2, - titleSection = "pluginsTitle" - ) - default Title pluginSortingTitle() - { - return new Title(); - } - - @ConfigItem( - position = 3, - keyName = "pluginSortMode", - name = "Sorting mode", - description = "Sorts plugins ", - titleSection = "pluginSortingTitle" - ) - default SortStyle pluginSortMode() - { - return SortStyle.CATEGORY; - } - - @ConfigItem( - position = 4, - keyName = "enableCategories", - name = "Categorize plugins", - description = "Show sections in the plugin list for each plugin type", - titleSection = "pluginSortingTitle", - hidden = true, - unhide = "pluginSortMode", - unhideValue = "Category" - ) - default boolean enableCategories() - { - return true; - } - - @ConfigTitleSection( - keyName = "pluginsColorTitle", - name = "Colors", - description = "", - position = 5, - titleSection = "pluginsTitle" - ) - default Title pluginsColorTitle() - { - return new Title(); - } - - @ConfigItem( - position = 6, - keyName = "enabledColors", - name = "Enable plugin colors", - description = "Configure whether or not the plugins list should be colorcoded", - titleSection = "pluginsColorTitle" - ) - default boolean enabledColors() - { - return true; - } - - @Alpha - @ConfigItem( - position = 7, - keyName = "pvmColor", - name = "PvM color", - description = "Configure the color of PvM related plugins", - titleSection = "pluginsColorTitle", - hidden = true, - unhide = "enabledColors" - ) - default Color pvmColor() - { - return new Color(119, 221, 119, 255); - } - - @Alpha - @ConfigItem( - position = 8, - keyName = "skillingColor", - name = "Skilling color", - description = "Configure the color of skilling related plugins", - titleSection = "pluginsColorTitle", - hidden = true, - unhide = "enabledColors" - ) - default Color skillingColor() - { - return new Color(252, 252, 100, 255); - } - - @Alpha - @ConfigItem( - position = 9, - keyName = "pvpColor", - name = "PvP color", - description = "Configure the color of PvP related plugins", - titleSection = "pluginsColorTitle", - hidden = true, - unhide = "enabledColors" - ) - default Color pvpColor() - { - return new Color(255, 105, 97, 255); - } - - @Alpha - @ConfigItem( - position = 10, - keyName = "utilityColor", - name = "Utility color", - description = "Configure the color of utility related plugins", - titleSection = "pluginsColorTitle", - hidden = true, - unhide = "enabledColors" - ) - default Color utilityColor() - { - return new Color(144, 212, 237, 255); - } - - @Alpha - @ConfigItem( - position = 11, - keyName = "minigameColor", - name = "Minigame color", - description = "Configure the color of minigame related plugins", - titleSection = "pluginsColorTitle", - hidden = true, - unhide = "enabledColors" - ) - default Color minigameColor() - { - return new Color(235, 130, 66, 255); - } - - @Alpha - @ConfigItem( - position = 12, - keyName = "miscellaneousColor", - name = "Miscellaneous color", - description = "Configure the color of miscellaneous related plugins", - titleSection = "pluginsColorTitle", - hidden = true, - unhide = "enabledColors" - ) - default Color miscellaneousColor() - { - return new Color(243, 85, 136, 255); - } - - @Alpha - @ConfigItem( - position = 13, - keyName = "gamemodeColor", - name = "Gamemode color", - description = "Configure the color of gamemode plugins", - titleSection = "pluginsColorTitle", - hidden = true, - unhide = "enabledColors" - ) - default Color gamemodeColor() - { - return new Color(244, 239, 211, 255); - } - - @ConfigTitleSection( - keyName = "opacityTitle", - name = "Opacity", - description = "", - position = 17 - ) - default Title opacityTitle() - { - return new Title(); - } - - @ConfigItem( - keyName = "enableOpacity", - name = "Enable opacity", - description = "Enables opacity for the whole window.
NOTE: This only stays enabled if your pc supports this!", - position = 18, - titleSection = "opacityTitle" - ) - default boolean enableOpacity() - { - return false; - } - - @Range( - min = 15, - max = 100 - ) - @ConfigItem( - keyName = "opacityPercentage", - name = "Opacity percentage", - description = "Changes the opacity of the window if opacity is enabled", - position = 19, - titleSection = "opacityTitle" - ) - @Units(Units.PERCENT) - default int opacityPercentage() - { - return 100; - } - - @ConfigTitleSection( - keyName = "miscTitle", - name = "Miscellaneous", - description = "", - position = 20 - ) - default Title miscTitle() - { - return new Title(); - } - - @ConfigItem( - keyName = "localSync", - name = "Sync local instances", - description = "Enables multiple local instances of OpenOSRS to communicate (this enables syncing plugin state and config options)", - position = 21, - titleSection = "miscTitle" - ) - default boolean localSync() - { - return true; - } - - @ConfigItem( - keyName = "keyboardPin", - name = "Keyboard bank pin", - description = "Enables you to type your bank pin", - position = 22, - titleSection = "miscTitle" - ) - default boolean keyboardPin() - { - return false; - } - - @ConfigItem( - keyName = "detachHotkey", - name = "Detach Cam", - description = "Detach Camera hotkey, press this and it will activate detatched camera.", - position = 23, - titleSection = "miscTitle" - ) - default Keybind detachHotkey() - { - return Keybind.NOT_SET; - } - - @ConfigItem( - keyName = "externalRepositories", - name = "", - description = "", - hidden = true - ) - default String getExternalRepositories() - { - return ExternalPluginManager.DEFAULT_PLUGIN_REPOS; - } - - @ConfigItem( - keyName = "externalRepositories", - name = "", - description = "", - hidden = true - ) - void setExternalRepositories(String val); - - @ConfigItem( - keyName = "warning", - name = "", - description = "", - hidden = true - ) - default boolean warning() - { - return true; - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java b/runelite-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java index 0e7d781337..d68ae06f02 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java @@ -38,25 +38,34 @@ public interface RuneLiteConfig extends Config { String GROUP_NAME = "runelite"; - @ConfigTitleSection( - keyName = "uiTitle", - name = "User interface", - description = "", + @ConfigSection( + name = "Window Settings", + description = "Settings relating to the client's window and frame", + position = 0 + ) + String windowSettings = "windowSettings"; + + @ConfigSection( + name = "Notification Settings", + description = "Settings relating to notifications", position = 1 ) - default Title uiTitle() - { - return new Title(); - } + String notificationSettings = "notificationSettings"; + + @ConfigSection( + name = "Overlay Settings", + description = "Settings relating to fonts", + position = 2 + ) + String overlaySettings = "overlaySettings"; @ConfigItem( keyName = "gameSize", name = "Game size", description = "The game will resize to this resolution upon starting the client", - position = 2, - titleSection = "uiTitle" + position = 10, + section = windowSettings ) - @Units(Units.PIXELS) default Dimension gameSize() { return Constants.GAME_FIXED_SIZE; @@ -66,8 +75,8 @@ public interface RuneLiteConfig extends Config keyName = "automaticResizeType", name = "Resize type", description = "Choose how the window should resize when opening and closing panels", - position = 3, - titleSection = "uiTitle" + position = 11, + section = windowSettings ) default ExpandResizeType automaticResizeType() { @@ -78,8 +87,8 @@ public interface RuneLiteConfig extends Config keyName = "lockWindowSize", name = "Lock window size", description = "Determines if the window resizing is allowed or not", - position = 4, - titleSection = "uiTitle" + position = 12, + section = windowSettings ) default boolean lockWindowSize() { @@ -90,82 +99,61 @@ public interface RuneLiteConfig extends Config keyName = "containInScreen2", name = "Contain in screen", description = "Makes the client stay contained in the screen when attempted to move out of it.
Note: 'Always' only works if custom chrome is enabled.", - position = 5, - titleSection = "uiTitle" + position = 13, + section = windowSettings ) default ContainableFrame.Mode containInScreen() { return ContainableFrame.Mode.RESIZING; } - - @ConfigItem( - keyName = "uiEnableCustomChrome", - name = "Enable custom window chrome", - description = "Use Runelite's custom window title and borders.", - warning = "Please restart your client after changing this setting.", - position = 6, - titleSection = "uiTitle" - ) - default boolean enableCustomChrome() - { - return true; - } - - @ConfigItem( - keyName = "borderless", - name = "Windowed borderless", - description = "Use windowed borderless mode", - warning = "Please restart your client after changing this setting.", - position = 7, - titleSection = "uiTitle" - ) - default boolean borderless() - { - return false; - } - - @ConfigItem( - keyName = "usernameInTitle", - name = "Show display name in title", - description = "Toggles displaying of local player's display name in client title", - position = 8, - titleSection = "uiTitle" - ) - default boolean usernameInTitle() - { - return true; - } - - @ConfigTitleSection( - keyName = "miscTitle", - name = "Miscellaneous", - description = "", - position = 9 - ) - default Title miscTitle() - { - return new Title(); - } - @ConfigItem( keyName = "rememberScreenBounds", name = "Remember client position", description = "Save the position and size of the client after exiting", - position = 10, - titleSection = "miscTitle" + position = 14, + section = windowSettings ) default boolean rememberScreenBounds() { return true; } + @ConfigItem( + keyName = "uiEnableCustomChrome", + name = "Enable custom window chrome", + description = "Use Runelite's custom window title and borders.", + warning = "Please restart your client after changing this setting", + position = 15, + section = windowSettings + ) + default boolean enableCustomChrome() + { + return true; + } + + @Range( + min = 10, + max = 100 + ) + @ConfigItem( + keyName = "uiWindowOpacity", + name = "Window opacity", + description = "Set the windows opacity. Requires \"Enable custom window chrome\" to be enabled.", + position = 16, + section = windowSettings + ) + default int windowOpacity() + { + return 100; + } + @ConfigItem( keyName = "gameAlwaysOnTop", name = "Enable client always on top", description = "The game will always be on the top of the screen", - position = 11, - titleSection = "miscTitle" + position = 17, + section = windowSettings ) default boolean gameAlwaysOnTop() { @@ -176,57 +164,32 @@ public interface RuneLiteConfig extends Config keyName = "warningOnExit", name = "Display warning on exit", description = "Toggles a warning popup when trying to exit the client", - position = 12, - titleSection = "miscTitle" + position = 18, + section = windowSettings ) default WarningOnExit warningOnExit() { return WarningOnExit.LOGGED_IN; } - @Range(max = 100, min = 0) @ConfigItem( - keyName = "volume", - name = "Runelite Volume", - description = "Sets the volume of custom Runelite sounds (not the client sounds)", - position = 13, - titleSection = "miscTitle" + keyName = "usernameInTitle", + name = "Show display name in title", + description = "Toggles displaying of local player's display name in client title", + position = 19, + section = windowSettings ) - @Units(Units.PERCENT) - default int volume() + default boolean usernameInTitle() { - return 100; - } - - @ConfigItem( - keyName = "tooltipPosition", - name = "Tooltip Position", - description = "Configures whether to show the tooltip above or under the cursor", - position = 14, - titleSection = "miscTitle" - ) - default TooltipPositionType tooltipPosition() - { - return TooltipPositionType.UNDER_CURSOR; - } - - @ConfigTitleSection( - keyName = "notificationsTitle", - name = "Notifications", - description = "", - position = 15 - ) - default Title notificationsTitle() - { - return new Title(); + return true; } @ConfigItem( keyName = "notificationTray", name = "Enable tray notifications", description = "Enables tray notifications", - position = 16, - titleSection = "notificationsTitle" + position = 20, + section = notificationSettings ) default boolean enableTrayNotifications() { @@ -237,8 +200,8 @@ public interface RuneLiteConfig extends Config keyName = "notificationRequestFocus", name = "Request focus on notification", description = "Configures the window focus request type on notification", - position = 17, - titleSection = "notificationsTitle" + position = 21, + section = notificationSettings ) default RequestFocusType notificationRequestFocus() { @@ -249,8 +212,8 @@ public interface RuneLiteConfig extends Config keyName = "notificationSound", name = "Notification sound", description = "Enables the playing of a beep sound when notifications are displayed", - position = 18, - titleSection = "notificationsTitle" + position = 22, + section = notificationSettings ) default Notifier.NativeCustomOff notificationSound() { @@ -261,8 +224,8 @@ public interface RuneLiteConfig extends Config keyName = "notificationGameMessage", name = "Enable game message notifications", description = "Puts a notification message in the chatbox", - position = 19, - titleSection = "notificationsTitle" + position = 23, + section = notificationSettings ) default boolean enableGameMessageNotification() { @@ -270,11 +233,11 @@ public interface RuneLiteConfig extends Config } @ConfigItem( - keyName = "notificationFlash", - name = "Enable flash notification", + keyName = "flashNotification", + name = "Flash notification", description = "Flashes the game frame as a notification", - position = 20, - titleSection = "notificationsTitle" + position = 24, + section = notificationSettings ) default FlashNotification flashNotification() { @@ -285,8 +248,8 @@ public interface RuneLiteConfig extends Config keyName = "notificationFocused", name = "Send notifications when focused", description = "Toggles all notifications for when the client is focused", - position = 21, - titleSection = "notificationsTitle" + position = 25, + section = notificationSettings ) default boolean sendNotificationsWhenFocused() { @@ -298,31 +261,20 @@ public interface RuneLiteConfig extends Config keyName = "notificationFlashColor", name = "Notification Flash Color", description = "Sets the color of the notification flashes.", - position = 22, - titleSection = "notificationsTitle" + position = 26, + section = notificationSettings ) default Color notificationFlashColor() { return new Color(255, 0, 0, 70); } - @ConfigTitleSection( - keyName = "fontTitle", - name = "Font", - description = "", - position = 23 - ) - default Title fontTitle() - { - return new Title(); - } - @ConfigItem( keyName = "fontType", name = "Dynamic Overlay Font", description = "Configures what font type is used for in-game overlays such as player name, ground items, etc.", - position = 24, - titleSection = "fontTitle" + position = 30, + section = overlaySettings ) default FontType fontType() { @@ -333,8 +285,8 @@ public interface RuneLiteConfig extends Config keyName = "tooltipFontType", name = "Tooltip Font", description = "Configures what font type is used for in-game tooltips such as food stats, NPC names, etc.", - position = 25, - titleSection = "fontTitle" + position = 31, + section = overlaySettings ) default FontType tooltipFontType() { @@ -345,31 +297,20 @@ public interface RuneLiteConfig extends Config keyName = "interfaceFontType", name = "Interface Overlay Font", description = "Configures what font type is used for in-game interface overlays such as panels, opponent info, clue scrolls etc.", - position = 26, - titleSection = "fontTitle" + position = 32, + section = overlaySettings ) default FontType interfaceFontType() { return FontType.REGULAR; } - @ConfigTitleSection( - keyName = "overlayTitle", - name = "Overlays", - description = "", - position = 27 - ) - default Title overlayTitle() - { - return new Title(); - } - @ConfigItem( keyName = "menuEntryShift", name = "Require Shift for overlay menu", description = "Overlay right-click menu will require shift to be added", - position = 28, - titleSection = "overlayTitle" + position = 33, + section = overlaySettings ) default boolean menuEntryShift() { @@ -377,47 +318,23 @@ public interface RuneLiteConfig extends Config } @ConfigItem( - keyName = "overlayBackgroundColor", - name = "Overlay Color", - description = "Configures the background color of infoboxes and overlays", - position = 29, - titleSection = "overlayTitle" + keyName = "tooltipPosition", + name = "Tooltip Position", + description = "Configures whether to show the tooltip above or under the cursor", + position = 35, + section = overlaySettings ) - @Alpha - default Color overlayBackgroundColor() + default TooltipPositionType tooltipPosition() { - return ComponentConstants.STANDARD_BACKGROUND_COLOR; - } - - @ConfigTitleSection( - keyName = "infoboxTitle", - name = "Infoboxes", - description = "", - position = 30 - ) - default Title infoboxTitle() - { - return new Title(); - } - - @ConfigItem( - keyName = "infoBoxTextOutline", - name = "Outline infobox text", - description = "Draw a full outline instead of a simple shadow for infobox text", - position = 31, - titleSection = "infoboxTitle" - ) - default boolean infoBoxTextOutline() - { - return false; + return TooltipPositionType.UNDER_CURSOR; } @ConfigItem( keyName = "infoBoxVertical", name = "Display infoboxes vertically", description = "Toggles the infoboxes to display vertically", - position = 32, - titleSection = "infoboxTitle", + position = 40, + section = overlaySettings, hidden = true ) default boolean infoBoxVertical() @@ -429,8 +346,8 @@ public interface RuneLiteConfig extends Config keyName = "infoBoxSize", name = "Infobox size", description = "Configures the size of each infobox in pixels", - position = 33, - titleSection = "infoboxTitle" + position = 42, + section = overlaySettings ) @Units(Units.PIXELS) default int infoBoxSize() @@ -438,23 +355,48 @@ public interface RuneLiteConfig extends Config return 35; } - @ConfigTitleSection( - keyName = "keybindsTitle", - name = "Key binds", - description = "", - position = 34 + @ConfigItem( + keyName = "infoBoxTextOutline", + name = "Outline infobox text", + description = "Draw a full outline instead of a simple shadow for infobox text", + position = 43, + section = overlaySettings ) - default Title keybindsTitle() + default boolean infoBoxTextOutline() { - return new Title(); + return false; + } + + @ConfigItem( + keyName = "overlayBackgroundColor", + name = "Overlay Color", + description = "Configures the background color of infoboxes and overlays", + position = 44, + section = overlaySettings + ) + @Alpha + default Color overlayBackgroundColor() + { + return ComponentConstants.STANDARD_BACKGROUND_COLOR; + } + + @ConfigItem( + keyName = "blockExtraMouseButtons", + name = "Block Extra Mouse Buttons", + description = "Blocks extra mouse buttons (4 and above)", + position = 44 + ) + default boolean blockExtraMouseButtons() + { + return true; } @ConfigItem( keyName = "sidebarToggleKey", name = "Sidebar Toggle Key", description = "The key that will toggle the sidebar (accepts modifiers)", - position = 35, - titleSection = "keybindsTitle" + position = 45, + section = windowSettings ) default Keybind sidebarToggleKey() { @@ -465,23 +407,11 @@ public interface RuneLiteConfig extends Config keyName = "panelToggleKey", name = "Plugin Panel Toggle Key", description = "The key that will toggle the current or last opened plugin panel (accepts modifiers)", - position = 36, - titleSection = "keybindsTitle" + position = 46, + section = windowSettings ) default Keybind panelToggleKey() { return new Keybind(KeyEvent.VK_F12, InputEvent.CTRL_DOWN_MASK); } - - @ConfigItem( - keyName = "blockExtraMouseButtons", - name = "Block Extra Mouse Buttons", - description = "Blocks extra mouse buttons (4 and above)", - position = 37, - titleSection = "keybindsTitle" - ) - default boolean blockExtraMouseButtons() - { - return false; - } } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableRow.java b/runelite-client/src/main/java/net/runelite/client/config/RuneScapeProfile.java similarity index 72% rename from runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableRow.java rename to runelite-client/src/main/java/net/runelite/client/config/RuneScapeProfile.java index b513eb679c..f8f7d78310 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableRow.java +++ b/runelite-client/src/main/java/net/runelite/client/config/RuneScapeProfile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, TheStonedTurtle + * Copyright (c) 2020 Abex * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,20 +22,24 @@ * (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.overlay.components.table; +package net.runelite.client.config; -import java.awt.Color; -import java.util.Collections; -import java.util.List; -import lombok.Builder; import lombok.Data; +/** + * A profile/save of a OSRS account. Each account can 1 profile per {@link RuneScapeProfileType} + * (ie Standard/League/DMM}. + */ @Data -@Builder -public class TableRow +public class RuneScapeProfile { - Color rowColor; - TableAlignment rowAlignment; - @Builder.Default - List elements = Collections.emptyList(); + private final String displayName; + private final RuneScapeProfileType type; + private final byte[] loginHash; + + /** + * Profile key used to save configs for this profile to the config store. This will + * always start with {@link ConfigManager#RSPROFILE_GROUP} + */ + private final String key; } diff --git a/runelite-client/src/main/java/net/runelite/client/config/ConfigPanelItem.java b/runelite-client/src/main/java/net/runelite/client/config/RuneScapeProfileType.java similarity index 63% rename from runelite-client/src/main/java/net/runelite/client/config/ConfigPanelItem.java rename to runelite-client/src/main/java/net/runelite/client/config/RuneScapeProfileType.java index 259a52e8c2..f4cce1b590 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/ConfigPanelItem.java +++ b/runelite-client/src/main/java/net/runelite/client/config/RuneScapeProfileType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Craftiii4 + * Copyright (c) 2020 Abex * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,27 +24,36 @@ */ package net.runelite.client.config; -import java.util.ArrayList; -import java.util.List; -import lombok.AccessLevel; +import java.util.function.Predicate; import lombok.Getter; +import lombok.RequiredArgsConstructor; +import net.runelite.api.Client; +import net.runelite.api.WorldType; -public class ConfigPanelItem +@Getter +@RequiredArgsConstructor +public enum RuneScapeProfileType { + STANDARD(client -> true), + BETA(client -> client.getWorldType().contains(WorldType.TOURNAMENT)), + DEADMAN(client -> client.getWorldType().contains(WorldType.DEADMAN)), + TRAILBLAZER_LEAGUE(client -> client.getWorldType().contains(WorldType.LEAGUE)), + ; - @Getter(AccessLevel.PUBLIC) - private ConfigPanelItem parent; + private final Predicate test; - @Getter(AccessLevel.PUBLIC) - private List children; - - @Getter(AccessLevel.PUBLIC) - private ConfigItemDescriptor item; - - public ConfigPanelItem(ConfigPanelItem parent, ConfigItemDescriptor item) + public static RuneScapeProfileType getCurrent(Client client) { - this.parent = parent; - this.children = new ArrayList<>(); - this.item = item; + RuneScapeProfileType[] types = values(); + for (int i = types.length - 1; i >= 0; i--) + { + RuneScapeProfileType type = types[i]; + if (types[i].test.test(client)) + { + return type; + } + } + + return STANDARD; } -} \ No newline at end of file +} diff --git a/runelite-client/src/main/java/net/runelite/client/config/Title.java b/runelite-client/src/main/java/net/runelite/client/config/Title.java deleted file mode 100644 index 4605a9965c..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/config/Title.java +++ /dev/null @@ -1,3 +0,0 @@ -package net.runelite.client.config; - -public class Title {} diff --git a/runelite-client/src/main/java/net/runelite/client/config/Units.java b/runelite-client/src/main/java/net/runelite/client/config/Units.java index ca872bfa80..45d54df164 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/Units.java +++ b/runelite-client/src/main/java/net/runelite/client/config/Units.java @@ -42,12 +42,8 @@ public @interface Units String MINUTES = " mins"; String PERCENT = "%"; String PIXELS = "px"; - String POINTS = "pt"; String SECONDS = "s"; String TICKS = " ticks"; - String LEVELS = " lvls"; - String FPS = " fps"; - String GP = " GP"; String value(); } diff --git a/runelite-client/src/main/java/net/runelite/client/database/DatabaseManager.java b/runelite-client/src/main/java/net/runelite/client/database/DatabaseManager.java deleted file mode 100644 index 119c5d5f66..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/database/DatabaseManager.java +++ /dev/null @@ -1,91 +0,0 @@ -package net.runelite.client.database; - -import java.io.File; -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import javax.inject.Singleton; -import lombok.extern.slf4j.Slf4j; -import static net.runelite.client.RuneLite.RUNELITE_DIR; -import org.h2.jdbcx.JdbcDataSource; -import org.jooq.DSLContext; -import org.jooq.SQLDialect; -import org.jooq.conf.Settings; -import org.jooq.impl.DSL; - -@Singleton -@Slf4j -public class DatabaseManager -{ - private static final String DB_URL = "jdbc:h2:" + RUNELITE_DIR + File.separator + "RunelitePlus;AUTO_SERVER=TRUE"; - - // Database credentials - private static final String USER = "RLP"; - private static final String PASS = ""; - - private Connection connection; - - DatabaseManager() - { - System.getProperties().setProperty("org.jooq.no-logo", "true"); - } - - private void connect() - { - if (connection != null) - { - return; - } - - JdbcDataSource ds = new JdbcDataSource(); - ds.setURL(DatabaseManager.DB_URL); - ds.setUser(DatabaseManager.USER); - ds.setPassword(DatabaseManager.PASS); - - try - { - connection = ds.getConnection(); - } - catch (SQLException e) - { - e.printStackTrace(); - } - } - - Connection getConnection() - { - connect(); - - return connection; - } - - public DSLContext getDsl() - { - Settings settings = new Settings(); - settings.setExecuteLogging(false); - - return DSL.using(connection, SQLDialect.H2, settings); - } - - public boolean checkTableExists(String table) - { - boolean tableExists = false; - - connect(); - - try - { - ResultSet rset = connection.getMetaData().getTables(null, null, table.toUpperCase(), null); - if (rset.next()) - { - tableExists = true; - } - } - catch (SQLException e) - { - e.printStackTrace(); - } - - return tableExists; - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/database/GenerateClasses.java b/runelite-client/src/main/java/net/runelite/client/database/GenerateClasses.java deleted file mode 100644 index f6125b55e1..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/database/GenerateClasses.java +++ /dev/null @@ -1,42 +0,0 @@ -package net.runelite.client.database; - -import java.sql.Connection; -import org.jooq.codegen.GenerationTool; -import org.jooq.meta.h2.H2Database; -import org.jooq.meta.jaxb.Configuration; -import org.jooq.meta.jaxb.Database; -import org.jooq.meta.jaxb.Generator; -import org.jooq.meta.jaxb.Target; - -public class GenerateClasses -{ - public static void main(String... args) - { - DatabaseManager databaseManager = new DatabaseManager(); - - try (Connection c = databaseManager.getConnection()) - { - Configuration configuration = new Configuration() - .withGenerator(new Generator() - .withDatabase(new Database() - .withName(H2Database.class.getCanonicalName()) - .withIncludes(".*") - .withExcludes("") - .withInputSchema("PUBLIC") - ) - .withTarget(new Target() - .withPackageName("net.runelite.client.database.data") - .withDirectory("runelite-client/src/main/java/") - ) - ); - - GenerationTool tool = new GenerationTool(); - tool.setConnection(c); - tool.run(configuration); - } - catch (Exception e) - { - e.printStackTrace(); - } - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/database/data/DefaultCatalog.java b/runelite-client/src/main/java/net/runelite/client/database/data/DefaultCatalog.java deleted file mode 100644 index 139bcaf234..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/database/data/DefaultCatalog.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * This file is generated by jOOQ. - */ -package net.runelite.client.database.data; - - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import javax.annotation.processing.Generated; -import org.jooq.Schema; -import org.jooq.impl.CatalogImpl; - - -/** - * This class is generated by jOOQ. - */ -@Generated( - value = { - "http://www.jooq.org", - "jOOQ version:3.12.3" - }, - comments = "This class is generated by jOOQ" -) -@SuppressWarnings({"all", "unchecked", "rawtypes"}) -public class DefaultCatalog extends CatalogImpl -{ - - private static final long serialVersionUID = -102989253; - - /** - * The reference instance of - */ - public static final DefaultCatalog DEFAULT_CATALOG = new DefaultCatalog(); - - /** - * The schema PUBLIC. - */ - public final Public PUBLIC = net.runelite.client.database.data.Public.PUBLIC; - - /** - * No further instances allowed - */ - private DefaultCatalog() - { - super(""); - } - - @Override - public final List getSchemas() - { - List result = new ArrayList(); - result.addAll(getSchemas0()); - return result; - } - - private final List getSchemas0() - { - return Arrays.asList( - Public.PUBLIC); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/database/data/Indexes.java b/runelite-client/src/main/java/net/runelite/client/database/data/Indexes.java deleted file mode 100644 index 72626efa54..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/database/data/Indexes.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * This file is generated by jOOQ. - */ -package net.runelite.client.database.data; - - -import javax.annotation.processing.Generated; -import net.runelite.client.database.data.tables.Loottrackerevents; -import net.runelite.client.database.data.tables.Loottrackerlink; -import net.runelite.client.database.data.tables.Loottrackerloot; -import net.runelite.client.database.data.tables.TmorphSets; -import net.runelite.client.database.data.tables.User; -import org.jooq.Index; -import org.jooq.OrderField; -import org.jooq.impl.Internal; - - -/** - * A class modelling indexes of tables of the PUBLIC schema. - */ -@Generated( - value = { - "http://www.jooq.org", - "jOOQ version:3.12.3" - }, - comments = "This class is generated by jOOQ" -) -@SuppressWarnings({"all", "unchecked", "rawtypes"}) -public class Indexes -{ - - // ------------------------------------------------------------------------- - // INDEX definitions - // ------------------------------------------------------------------------- - - public static final Index PRIMARY_KEY_B = Indexes0.PRIMARY_KEY_B; - public static final Index FK_LOOTTRACKERDROP_INDEX_6 = Indexes0.FK_LOOTTRACKERDROP_INDEX_6; - public static final Index FK_LOOTTRACKEREVENT_INDEX_6 = Indexes0.FK_LOOTTRACKEREVENT_INDEX_6; - public static final Index FK_USER_INDEX_6 = Indexes0.FK_USER_INDEX_6; - public static final Index PRIMARY_KEY_6 = Indexes0.PRIMARY_KEY_6; - public static final Index TMORPH_SETS_SET_NAME_UINDEX = Indexes0.TMORPH_SETS_SET_NAME_UINDEX; - public static final Index PRIMARY_KEY_2 = Indexes0.PRIMARY_KEY_2; - public static final Index UN_USERNAME_INDEX_2 = Indexes0.UN_USERNAME_INDEX_2; - - // ------------------------------------------------------------------------- - // [#1459] distribute members to avoid static initialisers > 64kb - // ------------------------------------------------------------------------- - - private static class Indexes0 - { - public static Index PRIMARY_KEY_B = Internal.createIndex("PRIMARY_KEY_B", Loottrackerevents.LOOTTRACKEREVENTS, new OrderField[]{Loottrackerevents.LOOTTRACKEREVENTS.UNIQUEID}, true); - public static Index FK_LOOTTRACKERDROP_INDEX_6 = Internal.createIndex("FK_LOOTTRACKERDROP_INDEX_6", Loottrackerlink.LOOTTRACKERLINK, new OrderField[]{Loottrackerlink.LOOTTRACKERLINK.DROPUNIQUEID}, false); - public static Index FK_LOOTTRACKEREVENT_INDEX_6 = Internal.createIndex("FK_LOOTTRACKEREVENT_INDEX_6", Loottrackerlink.LOOTTRACKERLINK, new OrderField[]{Loottrackerlink.LOOTTRACKERLINK.EVENTUNIQUEID}, false); - public static Index FK_USER_INDEX_6 = Internal.createIndex("FK_USER_INDEX_6", Loottrackerlink.LOOTTRACKERLINK, new OrderField[]{Loottrackerlink.LOOTTRACKERLINK.USERUNIQUEID}, false); - public static Index PRIMARY_KEY_6 = Internal.createIndex("PRIMARY_KEY_6", Loottrackerloot.LOOTTRACKERLOOT, new OrderField[]{Loottrackerloot.LOOTTRACKERLOOT.UNIQUEID}, true); - public static Index TMORPH_SETS_SET_NAME_UINDEX = Internal.createIndex("TMORPH_SETS_SET_NAME_UINDEX", TmorphSets.TMORPH_SETS, new OrderField[]{TmorphSets.TMORPH_SETS.SET_NAME}, true); - public static Index PRIMARY_KEY_2 = Internal.createIndex("PRIMARY_KEY_2", User.USER, new OrderField[]{User.USER.UNIQUEID}, true); - public static Index UN_USERNAME_INDEX_2 = Internal.createIndex("UN_USERNAME_INDEX_2", User.USER, new OrderField[]{User.USER.USERNAME}, true); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/database/data/Keys.java b/runelite-client/src/main/java/net/runelite/client/database/data/Keys.java deleted file mode 100644 index 2f01544f08..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/database/data/Keys.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * This file is generated by jOOQ. - */ -package net.runelite.client.database.data; - - -import javax.annotation.processing.Generated; -import net.runelite.client.database.data.tables.Loottrackerevents; -import net.runelite.client.database.data.tables.Loottrackerlink; -import net.runelite.client.database.data.tables.Loottrackerloot; -import net.runelite.client.database.data.tables.User; -import net.runelite.client.database.data.tables.records.LoottrackereventsRecord; -import net.runelite.client.database.data.tables.records.LoottrackerlinkRecord; -import net.runelite.client.database.data.tables.records.LoottrackerlootRecord; -import net.runelite.client.database.data.tables.records.UserRecord; -import org.jooq.ForeignKey; -import org.jooq.UniqueKey; -import org.jooq.impl.Internal; - - -/** - * A class modelling foreign key relationships and constraints of tables of - * the PUBLIC schema. - */ -@Generated( - value = { - "http://www.jooq.org", - "jOOQ version:3.12.3" - }, - comments = "This class is generated by jOOQ" -) -@SuppressWarnings({"all", "unchecked", "rawtypes"}) -public class Keys -{ - - // ------------------------------------------------------------------------- - // IDENTITY definitions - // ------------------------------------------------------------------------- - - - // ------------------------------------------------------------------------- - // UNIQUE and PRIMARY KEY definitions - // ------------------------------------------------------------------------- - - public static final UniqueKey PK_LOOTTRACKEREVENTS = UniqueKeys0.PK_LOOTTRACKEREVENTS; - public static final UniqueKey PK_LOOTTRACKERDROPS = UniqueKeys0.PK_LOOTTRACKERDROPS; - public static final UniqueKey PK_USER = UniqueKeys0.PK_USER; - public static final UniqueKey UN_USERNAME = UniqueKeys0.UN_USERNAME; - - // ------------------------------------------------------------------------- - // FOREIGN KEY definitions - // ------------------------------------------------------------------------- - - public static final ForeignKey FK_LOOTTRACKEREVENT = ForeignKeys0.FK_LOOTTRACKEREVENT; - public static final ForeignKey FK_LOOTTRACKERDROP = ForeignKeys0.FK_LOOTTRACKERDROP; - public static final ForeignKey FK_USER = ForeignKeys0.FK_USER; - - // ------------------------------------------------------------------------- - // [#1459] distribute members to avoid static initialisers > 64kb - // ------------------------------------------------------------------------- - - private static class UniqueKeys0 - { - public static final UniqueKey PK_LOOTTRACKEREVENTS = Internal.createUniqueKey(Loottrackerevents.LOOTTRACKEREVENTS, "PK_LOOTTRACKEREVENTS", Loottrackerevents.LOOTTRACKEREVENTS.UNIQUEID); - public static final UniqueKey PK_LOOTTRACKERDROPS = Internal.createUniqueKey(Loottrackerloot.LOOTTRACKERLOOT, "PK_LOOTTRACKERDROPS", Loottrackerloot.LOOTTRACKERLOOT.UNIQUEID); - public static final UniqueKey PK_USER = Internal.createUniqueKey(User.USER, "PK_USER", User.USER.UNIQUEID); - public static final UniqueKey UN_USERNAME = Internal.createUniqueKey(User.USER, "UN_USERNAME", User.USER.USERNAME); - } - - private static class ForeignKeys0 - { - public static final ForeignKey FK_LOOTTRACKEREVENT = Internal.createForeignKey(net.runelite.client.database.data.Keys.PK_LOOTTRACKEREVENTS, Loottrackerlink.LOOTTRACKERLINK, "FK_LOOTTRACKEREVENT", Loottrackerlink.LOOTTRACKERLINK.EVENTUNIQUEID); - public static final ForeignKey FK_LOOTTRACKERDROP = Internal.createForeignKey(net.runelite.client.database.data.Keys.PK_LOOTTRACKERDROPS, Loottrackerlink.LOOTTRACKERLINK, "FK_LOOTTRACKERDROP", Loottrackerlink.LOOTTRACKERLINK.DROPUNIQUEID); - public static final ForeignKey FK_USER = Internal.createForeignKey(net.runelite.client.database.data.Keys.PK_USER, Loottrackerlink.LOOTTRACKERLINK, "FK_USER", Loottrackerlink.LOOTTRACKERLINK.USERUNIQUEID); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/database/data/Public.java b/runelite-client/src/main/java/net/runelite/client/database/data/Public.java deleted file mode 100644 index b560fe5538..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/database/data/Public.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * This file is generated by jOOQ. - */ -package net.runelite.client.database.data; - - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import javax.annotation.processing.Generated; -import net.runelite.client.database.data.tables.Loottrackerevents; -import net.runelite.client.database.data.tables.Loottrackerlink; -import net.runelite.client.database.data.tables.Loottrackerloot; -import net.runelite.client.database.data.tables.TmorphSets; -import net.runelite.client.database.data.tables.User; -import org.jooq.Catalog; -import org.jooq.Table; -import org.jooq.impl.SchemaImpl; - - -/** - * This class is generated by jOOQ. - */ -@Generated( - value = { - "http://www.jooq.org", - "jOOQ version:3.12.3" - }, - comments = "This class is generated by jOOQ" -) -@SuppressWarnings({"all", "unchecked", "rawtypes"}) -public class Public extends SchemaImpl -{ - - private static final long serialVersionUID = 1268129010; - - /** - * The reference instance of PUBLIC - */ - public static final Public PUBLIC = new Public(); - - /** - * The table PUBLIC.LOOTTRACKEREVENTS. - */ - public final Loottrackerevents LOOTTRACKEREVENTS = net.runelite.client.database.data.tables.Loottrackerevents.LOOTTRACKEREVENTS; - - /** - * The table PUBLIC.LOOTTRACKERLINK. - */ - public final Loottrackerlink LOOTTRACKERLINK = net.runelite.client.database.data.tables.Loottrackerlink.LOOTTRACKERLINK; - - /** - * The table PUBLIC.LOOTTRACKERLOOT. - */ - public final Loottrackerloot LOOTTRACKERLOOT = net.runelite.client.database.data.tables.Loottrackerloot.LOOTTRACKERLOOT; - - /** - * The table PUBLIC.TMORPH_SETS. - */ - public final TmorphSets TMORPH_SETS = net.runelite.client.database.data.tables.TmorphSets.TMORPH_SETS; - - /** - * The table PUBLIC.USER. - */ - public final User USER = net.runelite.client.database.data.tables.User.USER; - - /** - * No further instances allowed - */ - private Public() - { - super("PUBLIC", null); - } - - - @Override - public Catalog getCatalog() - { - return DefaultCatalog.DEFAULT_CATALOG; - } - - @Override - public final List> getTables() - { - List result = new ArrayList(); - result.addAll(getTables0()); - return result; - } - - private final List> getTables0() - { - return Arrays.>asList( - Loottrackerevents.LOOTTRACKEREVENTS, - Loottrackerlink.LOOTTRACKERLINK, - Loottrackerloot.LOOTTRACKERLOOT, - TmorphSets.TMORPH_SETS, - User.USER); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/database/data/Tables.java b/runelite-client/src/main/java/net/runelite/client/database/data/Tables.java deleted file mode 100644 index 0b8ca41d45..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/database/data/Tables.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * This file is generated by jOOQ. - */ -package net.runelite.client.database.data; - - -import javax.annotation.processing.Generated; -import net.runelite.client.database.data.tables.Loottrackerevents; -import net.runelite.client.database.data.tables.Loottrackerlink; -import net.runelite.client.database.data.tables.Loottrackerloot; -import net.runelite.client.database.data.tables.TmorphSets; -import net.runelite.client.database.data.tables.User; - - -/** - * Convenience access to all tables in PUBLIC - */ -@Generated( - value = { - "http://www.jooq.org", - "jOOQ version:3.12.3" - }, - comments = "This class is generated by jOOQ" -) -@SuppressWarnings({"all", "unchecked", "rawtypes"}) -public class Tables -{ - - /** - * The table PUBLIC.LOOTTRACKEREVENTS. - */ - public static final Loottrackerevents LOOTTRACKEREVENTS = Loottrackerevents.LOOTTRACKEREVENTS; - - /** - * The table PUBLIC.LOOTTRACKERLINK. - */ - public static final Loottrackerlink LOOTTRACKERLINK = Loottrackerlink.LOOTTRACKERLINK; - - /** - * The table PUBLIC.LOOTTRACKERLOOT. - */ - public static final Loottrackerloot LOOTTRACKERLOOT = Loottrackerloot.LOOTTRACKERLOOT; - - /** - * The table PUBLIC.TMORPH_SETS. - */ - public static final TmorphSets TMORPH_SETS = TmorphSets.TMORPH_SETS; - - /** - * The table PUBLIC.USER. - */ - public static final User USER = User.USER; -} diff --git a/runelite-client/src/main/java/net/runelite/client/database/data/tables/Loottrackerevents.java b/runelite-client/src/main/java/net/runelite/client/database/data/tables/Loottrackerevents.java deleted file mode 100644 index 5bceae62d6..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/database/data/tables/Loottrackerevents.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * This file is generated by jOOQ. - */ -package net.runelite.client.database.data.tables; - - -import java.sql.Timestamp; -import java.util.Arrays; -import java.util.List; -import java.util.UUID; -import javax.annotation.processing.Generated; -import net.runelite.client.database.data.Indexes; -import net.runelite.client.database.data.Keys; -import net.runelite.client.database.data.Public; -import net.runelite.client.database.data.tables.records.LoottrackereventsRecord; -import org.jooq.Field; -import org.jooq.ForeignKey; -import org.jooq.Index; -import org.jooq.Name; -import org.jooq.Record; -import org.jooq.Row4; -import org.jooq.Schema; -import org.jooq.Table; -import org.jooq.TableField; -import org.jooq.UniqueKey; -import org.jooq.impl.DSL; -import org.jooq.impl.TableImpl; - - -/** - * This class is generated by jOOQ. - */ -@Generated( - value = { - "http://www.jooq.org", - "jOOQ version:3.12.3" - }, - comments = "This class is generated by jOOQ" -) -@SuppressWarnings({"all", "unchecked", "rawtypes"}) -public class Loottrackerevents extends TableImpl -{ - - private static final long serialVersionUID = 1578403652; - - /** - * The reference instance of PUBLIC.LOOTTRACKEREVENTS - */ - public static final Loottrackerevents LOOTTRACKEREVENTS = new Loottrackerevents(); - - /** - * The class holding records for this type - */ - @Override - public Class getRecordType() - { - return LoottrackereventsRecord.class; - } - - /** - * The column PUBLIC.LOOTTRACKEREVENTS.UNIQUEID. - */ - public final TableField UNIQUEID = createField(DSL.name("UNIQUEID"), org.jooq.impl.SQLDataType.UUID.nullable(false), this, ""); - - /** - * The column PUBLIC.LOOTTRACKEREVENTS.EVENTID. - */ - public final TableField EVENTID = createField(DSL.name("EVENTID"), org.jooq.impl.SQLDataType.VARCHAR(255).nullable(false), this, ""); - - /** - * The column PUBLIC.LOOTTRACKEREVENTS.TYPE. - */ - public final TableField TYPE = createField(DSL.name("TYPE"), org.jooq.impl.SQLDataType.VARCHAR(255).nullable(false), this, ""); - - /** - * The column PUBLIC.LOOTTRACKEREVENTS.TIME. - */ - public final TableField TIME = createField(DSL.name("TIME"), org.jooq.impl.SQLDataType.TIMESTAMP.precision(6).nullable(false), this, ""); - - /** - * Create a PUBLIC.LOOTTRACKEREVENTS table reference - */ - public Loottrackerevents() - { - this(DSL.name("LOOTTRACKEREVENTS"), null); - } - - /** - * Create an aliased PUBLIC.LOOTTRACKEREVENTS table reference - */ - public Loottrackerevents(String alias) - { - this(DSL.name(alias), LOOTTRACKEREVENTS); - } - - /** - * Create an aliased PUBLIC.LOOTTRACKEREVENTS table reference - */ - public Loottrackerevents(Name alias) - { - this(alias, LOOTTRACKEREVENTS); - } - - private Loottrackerevents(Name alias, Table aliased) - { - this(alias, aliased, null); - } - - private Loottrackerevents(Name alias, Table aliased, Field[] parameters) - { - super(alias, null, aliased, parameters, DSL.comment("")); - } - - public Loottrackerevents(Table child, ForeignKey key) - { - super(child, key, LOOTTRACKEREVENTS); - } - - @Override - public Schema getSchema() - { - return Public.PUBLIC; - } - - @Override - public List getIndexes() - { - return Arrays.asList(Indexes.PRIMARY_KEY_B); - } - - @Override - public UniqueKey getPrimaryKey() - { - return Keys.PK_LOOTTRACKEREVENTS; - } - - @Override - public List> getKeys() - { - return Arrays.>asList(Keys.PK_LOOTTRACKEREVENTS); - } - - @Override - public Loottrackerevents as(String alias) - { - return new Loottrackerevents(DSL.name(alias), this); - } - - @Override - public Loottrackerevents as(Name alias) - { - return new Loottrackerevents(alias, this); - } - - /** - * Rename this table - */ - @Override - public Loottrackerevents rename(String name) - { - return new Loottrackerevents(DSL.name(name), null); - } - - /** - * Rename this table - */ - @Override - public Loottrackerevents rename(Name name) - { - return new Loottrackerevents(name, null); - } - - // ------------------------------------------------------------------------- - // Row4 type methods - // ------------------------------------------------------------------------- - - @Override - public Row4 fieldsRow() - { - return (Row4) super.fieldsRow(); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/database/data/tables/Loottrackerlink.java b/runelite-client/src/main/java/net/runelite/client/database/data/tables/Loottrackerlink.java deleted file mode 100644 index f800608007..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/database/data/tables/Loottrackerlink.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * This file is generated by jOOQ. - */ -package net.runelite.client.database.data.tables; - - -import java.util.Arrays; -import java.util.List; -import java.util.UUID; -import javax.annotation.processing.Generated; -import net.runelite.client.database.data.Indexes; -import net.runelite.client.database.data.Keys; -import net.runelite.client.database.data.Public; -import net.runelite.client.database.data.tables.records.LoottrackerlinkRecord; -import org.jooq.Field; -import org.jooq.ForeignKey; -import org.jooq.Index; -import org.jooq.Name; -import org.jooq.Record; -import org.jooq.Row4; -import org.jooq.Schema; -import org.jooq.Table; -import org.jooq.TableField; -import org.jooq.impl.DSL; -import org.jooq.impl.TableImpl; - - -/** - * This class is generated by jOOQ. - */ -@Generated( - value = { - "http://www.jooq.org", - "jOOQ version:3.12.3" - }, - comments = "This class is generated by jOOQ" -) -@SuppressWarnings({"all", "unchecked", "rawtypes"}) -public class Loottrackerlink extends TableImpl -{ - - private static final long serialVersionUID = -1694278583; - - /** - * The reference instance of PUBLIC.LOOTTRACKERLINK - */ - public static final Loottrackerlink LOOTTRACKERLINK = new Loottrackerlink(); - - /** - * The class holding records for this type - */ - @Override - public Class getRecordType() - { - return LoottrackerlinkRecord.class; - } - - /** - * The column PUBLIC.LOOTTRACKERLINK.LINKUNIQUEID. - */ - public final TableField LINKUNIQUEID = createField(DSL.name("LINKUNIQUEID"), org.jooq.impl.SQLDataType.UUID.nullable(false), this, ""); - - /** - * The column PUBLIC.LOOTTRACKERLINK.EVENTUNIQUEID. - */ - public final TableField EVENTUNIQUEID = createField(DSL.name("EVENTUNIQUEID"), org.jooq.impl.SQLDataType.UUID.nullable(false), this, ""); - - /** - * The column PUBLIC.LOOTTRACKERLINK.DROPUNIQUEID. - */ - public final TableField DROPUNIQUEID = createField(DSL.name("DROPUNIQUEID"), org.jooq.impl.SQLDataType.UUID.nullable(false), this, ""); - - /** - * The column PUBLIC.LOOTTRACKERLINK.USERUNIQUEID. - */ - public final TableField USERUNIQUEID = createField(DSL.name("USERUNIQUEID"), org.jooq.impl.SQLDataType.UUID.nullable(false), this, ""); - - /** - * Create a PUBLIC.LOOTTRACKERLINK table reference - */ - public Loottrackerlink() - { - this(DSL.name("LOOTTRACKERLINK"), null); - } - - /** - * Create an aliased PUBLIC.LOOTTRACKERLINK table reference - */ - public Loottrackerlink(String alias) - { - this(DSL.name(alias), LOOTTRACKERLINK); - } - - /** - * Create an aliased PUBLIC.LOOTTRACKERLINK table reference - */ - public Loottrackerlink(Name alias) - { - this(alias, LOOTTRACKERLINK); - } - - private Loottrackerlink(Name alias, Table aliased) - { - this(alias, aliased, null); - } - - private Loottrackerlink(Name alias, Table aliased, Field[] parameters) - { - super(alias, null, aliased, parameters, DSL.comment("")); - } - - public Loottrackerlink(Table child, ForeignKey key) - { - super(child, key, LOOTTRACKERLINK); - } - - @Override - public Schema getSchema() - { - return Public.PUBLIC; - } - - @Override - public List getIndexes() - { - return Arrays.asList(Indexes.FK_LOOTTRACKERDROP_INDEX_6, Indexes.FK_LOOTTRACKEREVENT_INDEX_6, Indexes.FK_USER_INDEX_6); - } - - @Override - public List> getReferences() - { - return Arrays.>asList(Keys.FK_LOOTTRACKEREVENT, Keys.FK_LOOTTRACKERDROP, Keys.FK_USER); - } - - public Loottrackerevents loottrackerevents() - { - return new Loottrackerevents(this, Keys.FK_LOOTTRACKEREVENT); - } - - public Loottrackerloot loottrackerloot() - { - return new Loottrackerloot(this, Keys.FK_LOOTTRACKERDROP); - } - - public User user() - { - return new User(this, Keys.FK_USER); - } - - @Override - public Loottrackerlink as(String alias) - { - return new Loottrackerlink(DSL.name(alias), this); - } - - @Override - public Loottrackerlink as(Name alias) - { - return new Loottrackerlink(alias, this); - } - - /** - * Rename this table - */ - @Override - public Loottrackerlink rename(String name) - { - return new Loottrackerlink(DSL.name(name), null); - } - - /** - * Rename this table - */ - @Override - public Loottrackerlink rename(Name name) - { - return new Loottrackerlink(name, null); - } - - // ------------------------------------------------------------------------- - // Row4 type methods - // ------------------------------------------------------------------------- - - @Override - public Row4 fieldsRow() - { - return (Row4) super.fieldsRow(); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/database/data/tables/Loottrackerloot.java b/runelite-client/src/main/java/net/runelite/client/database/data/tables/Loottrackerloot.java deleted file mode 100644 index a9d6b574a3..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/database/data/tables/Loottrackerloot.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * This file is generated by jOOQ. - */ -package net.runelite.client.database.data.tables; - - -import java.util.Arrays; -import java.util.List; -import java.util.UUID; -import javax.annotation.processing.Generated; -import net.runelite.client.database.data.Indexes; -import net.runelite.client.database.data.Keys; -import net.runelite.client.database.data.Public; -import net.runelite.client.database.data.tables.records.LoottrackerlootRecord; -import org.jooq.Field; -import org.jooq.ForeignKey; -import org.jooq.Index; -import org.jooq.Name; -import org.jooq.Record; -import org.jooq.Row3; -import org.jooq.Schema; -import org.jooq.Table; -import org.jooq.TableField; -import org.jooq.UniqueKey; -import org.jooq.impl.DSL; -import org.jooq.impl.TableImpl; - - -/** - * This class is generated by jOOQ. - */ -@Generated( - value = { - "http://www.jooq.org", - "jOOQ version:3.12.3" - }, - comments = "This class is generated by jOOQ" -) -@SuppressWarnings({"all", "unchecked", "rawtypes"}) -public class Loottrackerloot extends TableImpl -{ - - private static final long serialVersionUID = 1461948279; - - /** - * The reference instance of PUBLIC.LOOTTRACKERLOOT - */ - public static final Loottrackerloot LOOTTRACKERLOOT = new Loottrackerloot(); - - /** - * The class holding records for this type - */ - @Override - public Class getRecordType() - { - return LoottrackerlootRecord.class; - } - - /** - * The column PUBLIC.LOOTTRACKERLOOT.UNIQUEID. - */ - public final TableField UNIQUEID = createField(DSL.name("UNIQUEID"), org.jooq.impl.SQLDataType.UUID.nullable(false), this, ""); - - /** - * The column PUBLIC.LOOTTRACKERLOOT.ITEMID. - */ - public final TableField ITEMID = createField(DSL.name("ITEMID"), org.jooq.impl.SQLDataType.INTEGER.nullable(false), this, ""); - - /** - * The column PUBLIC.LOOTTRACKERLOOT.QUANTITY. - */ - public final TableField QUANTITY = createField(DSL.name("QUANTITY"), org.jooq.impl.SQLDataType.INTEGER.nullable(false), this, ""); - - /** - * Create a PUBLIC.LOOTTRACKERLOOT table reference - */ - public Loottrackerloot() - { - this(DSL.name("LOOTTRACKERLOOT"), null); - } - - /** - * Create an aliased PUBLIC.LOOTTRACKERLOOT table reference - */ - public Loottrackerloot(String alias) - { - this(DSL.name(alias), LOOTTRACKERLOOT); - } - - /** - * Create an aliased PUBLIC.LOOTTRACKERLOOT table reference - */ - public Loottrackerloot(Name alias) - { - this(alias, LOOTTRACKERLOOT); - } - - private Loottrackerloot(Name alias, Table aliased) - { - this(alias, aliased, null); - } - - private Loottrackerloot(Name alias, Table aliased, Field[] parameters) - { - super(alias, null, aliased, parameters, DSL.comment("")); - } - - public Loottrackerloot(Table child, ForeignKey key) - { - super(child, key, LOOTTRACKERLOOT); - } - - @Override - public Schema getSchema() - { - return Public.PUBLIC; - } - - @Override - public List getIndexes() - { - return Arrays.asList(Indexes.PRIMARY_KEY_6); - } - - @Override - public UniqueKey getPrimaryKey() - { - return Keys.PK_LOOTTRACKERDROPS; - } - - @Override - public List> getKeys() - { - return Arrays.>asList(Keys.PK_LOOTTRACKERDROPS); - } - - @Override - public Loottrackerloot as(String alias) - { - return new Loottrackerloot(DSL.name(alias), this); - } - - @Override - public Loottrackerloot as(Name alias) - { - return new Loottrackerloot(alias, this); - } - - /** - * Rename this table - */ - @Override - public Loottrackerloot rename(String name) - { - return new Loottrackerloot(DSL.name(name), null); - } - - /** - * Rename this table - */ - @Override - public Loottrackerloot rename(Name name) - { - return new Loottrackerloot(name, null); - } - - // ------------------------------------------------------------------------- - // Row3 type methods - // ------------------------------------------------------------------------- - - @Override - public Row3 fieldsRow() - { - return (Row3) super.fieldsRow(); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/database/data/tables/TmorphSets.java b/runelite-client/src/main/java/net/runelite/client/database/data/tables/TmorphSets.java deleted file mode 100644 index fcc76b8504..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/database/data/tables/TmorphSets.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * This file is generated by jOOQ. - */ -package net.runelite.client.database.data.tables; - - -import java.util.Arrays; -import java.util.List; -import javax.annotation.processing.Generated; -import net.runelite.client.database.data.Indexes; -import net.runelite.client.database.data.Public; -import net.runelite.client.database.data.tables.records.TmorphSetsRecord; -import org.jooq.Field; -import org.jooq.ForeignKey; -import org.jooq.Index; -import org.jooq.Name; -import org.jooq.Record; -import org.jooq.Row10; -import org.jooq.Schema; -import org.jooq.Table; -import org.jooq.TableField; -import org.jooq.impl.DSL; -import org.jooq.impl.TableImpl; - - -/** - * This class is generated by jOOQ. - */ -@Generated( - value = { - "http://www.jooq.org", - "jOOQ version:3.12.3" - }, - comments = "This class is generated by jOOQ" -) -@SuppressWarnings({"all", "unchecked", "rawtypes"}) -public class TmorphSets extends TableImpl -{ - - private static final long serialVersionUID = -2027086786; - - /** - * The reference instance of PUBLIC.TMORPH_SETS - */ - public static final TmorphSets TMORPH_SETS = new TmorphSets(); - - /** - * The class holding records for this type - */ - @Override - public Class getRecordType() - { - return TmorphSetsRecord.class; - } - - /** - * The column PUBLIC.TMORPH_SETS.SET_NAME. - */ - public final TableField SET_NAME = createField(DSL.name("SET_NAME"), org.jooq.impl.SQLDataType.VARCHAR(255).nullable(false), this, ""); - - /** - * The column PUBLIC.TMORPH_SETS.HELMET. - */ - public final TableField HELMET = createField(DSL.name("HELMET"), org.jooq.impl.SQLDataType.INTEGER.nullable(false), this, ""); - - /** - * The column PUBLIC.TMORPH_SETS.CAPE. - */ - public final TableField CAPE = createField(DSL.name("CAPE"), org.jooq.impl.SQLDataType.INTEGER.nullable(false), this, ""); - - /** - * The column PUBLIC.TMORPH_SETS.AMULET. - */ - public final TableField AMULET = createField(DSL.name("AMULET"), org.jooq.impl.SQLDataType.INTEGER.nullable(false), this, ""); - - /** - * The column PUBLIC.TMORPH_SETS.WEAPON. - */ - public final TableField WEAPON = createField(DSL.name("WEAPON"), org.jooq.impl.SQLDataType.INTEGER.nullable(false), this, ""); - - /** - * The column PUBLIC.TMORPH_SETS.TORSO. - */ - public final TableField TORSO = createField(DSL.name("TORSO"), org.jooq.impl.SQLDataType.INTEGER.nullable(false), this, ""); - - /** - * The column PUBLIC.TMORPH_SETS.SHIELD. - */ - public final TableField SHIELD = createField(DSL.name("SHIELD"), org.jooq.impl.SQLDataType.INTEGER.nullable(false), this, ""); - - /** - * The column PUBLIC.TMORPH_SETS.LEGS. - */ - public final TableField LEGS = createField(DSL.name("LEGS"), org.jooq.impl.SQLDataType.INTEGER.nullable(false), this, ""); - - /** - * The column PUBLIC.TMORPH_SETS.HANDS. - */ - public final TableField HANDS = createField(DSL.name("HANDS"), org.jooq.impl.SQLDataType.INTEGER.nullable(false), this, ""); - - /** - * The column PUBLIC.TMORPH_SETS.BOOTS. - */ - public final TableField BOOTS = createField(DSL.name("BOOTS"), org.jooq.impl.SQLDataType.INTEGER.nullable(false), this, ""); - - /** - * Create a PUBLIC.TMORPH_SETS table reference - */ - public TmorphSets() - { - this(DSL.name("TMORPH_SETS"), null); - } - - /** - * Create an aliased PUBLIC.TMORPH_SETS table reference - */ - public TmorphSets(String alias) - { - this(DSL.name(alias), TMORPH_SETS); - } - - /** - * Create an aliased PUBLIC.TMORPH_SETS table reference - */ - public TmorphSets(Name alias) - { - this(alias, TMORPH_SETS); - } - - private TmorphSets(Name alias, Table aliased) - { - this(alias, aliased, null); - } - - private TmorphSets(Name alias, Table aliased, Field[] parameters) - { - super(alias, null, aliased, parameters, DSL.comment("")); - } - - public TmorphSets(Table child, ForeignKey key) - { - super(child, key, TMORPH_SETS); - } - - @Override - public Schema getSchema() - { - return Public.PUBLIC; - } - - @Override - public List getIndexes() - { - return Arrays.asList(Indexes.TMORPH_SETS_SET_NAME_UINDEX); - } - - @Override - public TmorphSets as(String alias) - { - return new TmorphSets(DSL.name(alias), this); - } - - @Override - public TmorphSets as(Name alias) - { - return new TmorphSets(alias, this); - } - - /** - * Rename this table - */ - @Override - public TmorphSets rename(String name) - { - return new TmorphSets(DSL.name(name), null); - } - - /** - * Rename this table - */ - @Override - public TmorphSets rename(Name name) - { - return new TmorphSets(name, null); - } - - // ------------------------------------------------------------------------- - // Row10 type methods - // ------------------------------------------------------------------------- - - @Override - public Row10 fieldsRow() - { - return (Row10) super.fieldsRow(); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/database/data/tables/User.java b/runelite-client/src/main/java/net/runelite/client/database/data/tables/User.java deleted file mode 100644 index 15588c223d..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/database/data/tables/User.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * This file is generated by jOOQ. - */ -package net.runelite.client.database.data.tables; - - -import java.util.Arrays; -import java.util.List; -import java.util.UUID; -import javax.annotation.processing.Generated; -import net.runelite.client.database.data.Indexes; -import net.runelite.client.database.data.Keys; -import net.runelite.client.database.data.Public; -import net.runelite.client.database.data.tables.records.UserRecord; -import org.jooq.Field; -import org.jooq.ForeignKey; -import org.jooq.Index; -import org.jooq.Name; -import org.jooq.Record; -import org.jooq.Row2; -import org.jooq.Schema; -import org.jooq.Table; -import org.jooq.TableField; -import org.jooq.UniqueKey; -import org.jooq.impl.DSL; -import org.jooq.impl.TableImpl; - - -/** - * This class is generated by jOOQ. - */ -@Generated( - value = { - "http://www.jooq.org", - "jOOQ version:3.12.3" - }, - comments = "This class is generated by jOOQ" -) -@SuppressWarnings({"all", "unchecked", "rawtypes"}) -public class User extends TableImpl -{ - - private static final long serialVersionUID = -668009102; - - /** - * The reference instance of PUBLIC.USER - */ - public static final User USER = new User(); - - /** - * The class holding records for this type - */ - @Override - public Class getRecordType() - { - return UserRecord.class; - } - - /** - * The column PUBLIC.USER.UNIQUEID. - */ - public final TableField UNIQUEID = createField(DSL.name("UNIQUEID"), org.jooq.impl.SQLDataType.UUID.nullable(false), this, ""); - - /** - * The column PUBLIC.USER.USERNAME. - */ - public final TableField USERNAME = createField(DSL.name("USERNAME"), org.jooq.impl.SQLDataType.VARCHAR(12).nullable(false), this, ""); - - /** - * Create a PUBLIC.USER table reference - */ - public User() - { - this(DSL.name("USER"), null); - } - - /** - * Create an aliased PUBLIC.USER table reference - */ - public User(String alias) - { - this(DSL.name(alias), USER); - } - - /** - * Create an aliased PUBLIC.USER table reference - */ - public User(Name alias) - { - this(alias, USER); - } - - private User(Name alias, Table aliased) - { - this(alias, aliased, null); - } - - private User(Name alias, Table aliased, Field[] parameters) - { - super(alias, null, aliased, parameters, DSL.comment("")); - } - - public User(Table child, ForeignKey key) - { - super(child, key, USER); - } - - @Override - public Schema getSchema() - { - return Public.PUBLIC; - } - - @Override - public List getIndexes() - { - return Arrays.asList(Indexes.PRIMARY_KEY_2, Indexes.UN_USERNAME_INDEX_2); - } - - @Override - public UniqueKey getPrimaryKey() - { - return Keys.PK_USER; - } - - @Override - public List> getKeys() - { - return Arrays.>asList(Keys.PK_USER, Keys.UN_USERNAME); - } - - @Override - public User as(String alias) - { - return new User(DSL.name(alias), this); - } - - @Override - public User as(Name alias) - { - return new User(alias, this); - } - - /** - * Rename this table - */ - @Override - public User rename(String name) - { - return new User(DSL.name(name), null); - } - - /** - * Rename this table - */ - @Override - public User rename(Name name) - { - return new User(name, null); - } - - // ------------------------------------------------------------------------- - // Row2 type methods - // ------------------------------------------------------------------------- - - @Override - public Row2 fieldsRow() - { - return (Row2) super.fieldsRow(); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/database/data/tables/records/LoottrackereventsRecord.java b/runelite-client/src/main/java/net/runelite/client/database/data/tables/records/LoottrackereventsRecord.java deleted file mode 100644 index 00f0095252..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/database/data/tables/records/LoottrackereventsRecord.java +++ /dev/null @@ -1,258 +0,0 @@ -/* - * This file is generated by jOOQ. - */ -package net.runelite.client.database.data.tables.records; - - -import java.sql.Timestamp; -import java.util.UUID; -import javax.annotation.processing.Generated; -import net.runelite.client.database.data.tables.Loottrackerevents; -import org.jooq.Field; -import org.jooq.Record1; -import org.jooq.Record4; -import org.jooq.Row4; -import org.jooq.impl.UpdatableRecordImpl; - - -/** - * This class is generated by jOOQ. - */ -@Generated( - value = { - "http://www.jooq.org", - "jOOQ version:3.12.3" - }, - comments = "This class is generated by jOOQ" -) -@SuppressWarnings({"all", "unchecked", "rawtypes"}) -public class LoottrackereventsRecord extends UpdatableRecordImpl implements Record4 -{ - - private static final long serialVersionUID = -1418522415; - - /** - * Setter for PUBLIC.LOOTTRACKEREVENTS.UNIQUEID. - */ - public void setUniqueid(UUID value) - { - set(0, value); - } - - /** - * Getter for PUBLIC.LOOTTRACKEREVENTS.UNIQUEID. - */ - public UUID getUniqueid() - { - return (UUID) get(0); - } - - /** - * Setter for PUBLIC.LOOTTRACKEREVENTS.EVENTID. - */ - public void setEventid(String value) - { - set(1, value); - } - - /** - * Getter for PUBLIC.LOOTTRACKEREVENTS.EVENTID. - */ - public String getEventid() - { - return (String) get(1); - } - - /** - * Setter for PUBLIC.LOOTTRACKEREVENTS.TYPE. - */ - public void setType(String value) - { - set(2, value); - } - - /** - * Getter for PUBLIC.LOOTTRACKEREVENTS.TYPE. - */ - public String getType() - { - return (String) get(2); - } - - /** - * Setter for PUBLIC.LOOTTRACKEREVENTS.TIME. - */ - public void setTime(Timestamp value) - { - set(3, value); - } - - /** - * Getter for PUBLIC.LOOTTRACKEREVENTS.TIME. - */ - public Timestamp getTime() - { - return (Timestamp) get(3); - } - - // ------------------------------------------------------------------------- - // Primary key information - // ------------------------------------------------------------------------- - - @Override - public Record1 key() - { - return (Record1) super.key(); - } - - // ------------------------------------------------------------------------- - // Record4 type implementation - // ------------------------------------------------------------------------- - - @Override - public Row4 fieldsRow() - { - return (Row4) super.fieldsRow(); - } - - @Override - public Row4 valuesRow() - { - return (Row4) super.valuesRow(); - } - - @Override - public Field field1() - { - return Loottrackerevents.LOOTTRACKEREVENTS.UNIQUEID; - } - - @Override - public Field field2() - { - return Loottrackerevents.LOOTTRACKEREVENTS.EVENTID; - } - - @Override - public Field field3() - { - return Loottrackerevents.LOOTTRACKEREVENTS.TYPE; - } - - @Override - public Field field4() - { - return Loottrackerevents.LOOTTRACKEREVENTS.TIME; - } - - @Override - public UUID component1() - { - return getUniqueid(); - } - - @Override - public String component2() - { - return getEventid(); - } - - @Override - public String component3() - { - return getType(); - } - - @Override - public Timestamp component4() - { - return getTime(); - } - - @Override - public UUID value1() - { - return getUniqueid(); - } - - @Override - public String value2() - { - return getEventid(); - } - - @Override - public String value3() - { - return getType(); - } - - @Override - public Timestamp value4() - { - return getTime(); - } - - @Override - public LoottrackereventsRecord value1(UUID value) - { - setUniqueid(value); - return this; - } - - @Override - public LoottrackereventsRecord value2(String value) - { - setEventid(value); - return this; - } - - @Override - public LoottrackereventsRecord value3(String value) - { - setType(value); - return this; - } - - @Override - public LoottrackereventsRecord value4(Timestamp value) - { - setTime(value); - return this; - } - - @Override - public LoottrackereventsRecord values(UUID value1, String value2, String value3, Timestamp value4) - { - value1(value1); - value2(value2); - value3(value3); - value4(value4); - return this; - } - - // ------------------------------------------------------------------------- - // Constructors - // ------------------------------------------------------------------------- - - /** - * Create a detached LoottrackereventsRecord - */ - public LoottrackereventsRecord() - { - super(Loottrackerevents.LOOTTRACKEREVENTS); - } - - /** - * Create a detached, initialised LoottrackereventsRecord - */ - public LoottrackereventsRecord(UUID uniqueid, String eventid, String type, Timestamp time) - { - super(Loottrackerevents.LOOTTRACKEREVENTS); - - set(0, uniqueid); - set(1, eventid); - set(2, type); - set(3, time); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/database/data/tables/records/LoottrackerlinkRecord.java b/runelite-client/src/main/java/net/runelite/client/database/data/tables/records/LoottrackerlinkRecord.java deleted file mode 100644 index 44417761fe..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/database/data/tables/records/LoottrackerlinkRecord.java +++ /dev/null @@ -1,246 +0,0 @@ -/* - * This file is generated by jOOQ. - */ -package net.runelite.client.database.data.tables.records; - - -import java.util.UUID; -import javax.annotation.processing.Generated; -import net.runelite.client.database.data.tables.Loottrackerlink; -import org.jooq.Field; -import org.jooq.Record4; -import org.jooq.Row4; -import org.jooq.impl.TableRecordImpl; - - -/** - * This class is generated by jOOQ. - */ -@Generated( - value = { - "http://www.jooq.org", - "jOOQ version:3.12.3" - }, - comments = "This class is generated by jOOQ" -) -@SuppressWarnings({"all", "unchecked", "rawtypes"}) -public class LoottrackerlinkRecord extends TableRecordImpl implements Record4 -{ - - private static final long serialVersionUID = -1701074584; - - /** - * Setter for PUBLIC.LOOTTRACKERLINK.LINKUNIQUEID. - */ - public void setLinkuniqueid(UUID value) - { - set(0, value); - } - - /** - * Getter for PUBLIC.LOOTTRACKERLINK.LINKUNIQUEID. - */ - public UUID getLinkuniqueid() - { - return (UUID) get(0); - } - - /** - * Setter for PUBLIC.LOOTTRACKERLINK.EVENTUNIQUEID. - */ - public void setEventuniqueid(UUID value) - { - set(1, value); - } - - /** - * Getter for PUBLIC.LOOTTRACKERLINK.EVENTUNIQUEID. - */ - public UUID getEventuniqueid() - { - return (UUID) get(1); - } - - /** - * Setter for PUBLIC.LOOTTRACKERLINK.DROPUNIQUEID. - */ - public void setDropuniqueid(UUID value) - { - set(2, value); - } - - /** - * Getter for PUBLIC.LOOTTRACKERLINK.DROPUNIQUEID. - */ - public UUID getDropuniqueid() - { - return (UUID) get(2); - } - - /** - * Setter for PUBLIC.LOOTTRACKERLINK.USERUNIQUEID. - */ - public void setUseruniqueid(UUID value) - { - set(3, value); - } - - /** - * Getter for PUBLIC.LOOTTRACKERLINK.USERUNIQUEID. - */ - public UUID getUseruniqueid() - { - return (UUID) get(3); - } - - // ------------------------------------------------------------------------- - // Record4 type implementation - // ------------------------------------------------------------------------- - - @Override - public Row4 fieldsRow() - { - return (Row4) super.fieldsRow(); - } - - @Override - public Row4 valuesRow() - { - return (Row4) super.valuesRow(); - } - - @Override - public Field field1() - { - return Loottrackerlink.LOOTTRACKERLINK.LINKUNIQUEID; - } - - @Override - public Field field2() - { - return Loottrackerlink.LOOTTRACKERLINK.EVENTUNIQUEID; - } - - @Override - public Field field3() - { - return Loottrackerlink.LOOTTRACKERLINK.DROPUNIQUEID; - } - - @Override - public Field field4() - { - return Loottrackerlink.LOOTTRACKERLINK.USERUNIQUEID; - } - - @Override - public UUID component1() - { - return getLinkuniqueid(); - } - - @Override - public UUID component2() - { - return getEventuniqueid(); - } - - @Override - public UUID component3() - { - return getDropuniqueid(); - } - - @Override - public UUID component4() - { - return getUseruniqueid(); - } - - @Override - public UUID value1() - { - return getLinkuniqueid(); - } - - @Override - public UUID value2() - { - return getEventuniqueid(); - } - - @Override - public UUID value3() - { - return getDropuniqueid(); - } - - @Override - public UUID value4() - { - return getUseruniqueid(); - } - - @Override - public LoottrackerlinkRecord value1(UUID value) - { - setLinkuniqueid(value); - return this; - } - - @Override - public LoottrackerlinkRecord value2(UUID value) - { - setEventuniqueid(value); - return this; - } - - @Override - public LoottrackerlinkRecord value3(UUID value) - { - setDropuniqueid(value); - return this; - } - - @Override - public LoottrackerlinkRecord value4(UUID value) - { - setUseruniqueid(value); - return this; - } - - @Override - public LoottrackerlinkRecord values(UUID value1, UUID value2, UUID value3, UUID value4) - { - value1(value1); - value2(value2); - value3(value3); - value4(value4); - return this; - } - - // ------------------------------------------------------------------------- - // Constructors - // ------------------------------------------------------------------------- - - /** - * Create a detached LoottrackerlinkRecord - */ - public LoottrackerlinkRecord() - { - super(Loottrackerlink.LOOTTRACKERLINK); - } - - /** - * Create a detached, initialised LoottrackerlinkRecord - */ - public LoottrackerlinkRecord(UUID linkuniqueid, UUID eventuniqueid, UUID dropuniqueid, UUID useruniqueid) - { - super(Loottrackerlink.LOOTTRACKERLINK); - - set(0, linkuniqueid); - set(1, eventuniqueid); - set(2, dropuniqueid); - set(3, useruniqueid); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/database/data/tables/records/LoottrackerlootRecord.java b/runelite-client/src/main/java/net/runelite/client/database/data/tables/records/LoottrackerlootRecord.java deleted file mode 100644 index bb02cffd65..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/database/data/tables/records/LoottrackerlootRecord.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * This file is generated by jOOQ. - */ -package net.runelite.client.database.data.tables.records; - - -import java.util.UUID; -import javax.annotation.processing.Generated; -import net.runelite.client.database.data.tables.Loottrackerloot; -import org.jooq.Field; -import org.jooq.Record1; -import org.jooq.Record3; -import org.jooq.Row3; -import org.jooq.impl.UpdatableRecordImpl; - - -/** - * This class is generated by jOOQ. - */ -@Generated( - value = { - "http://www.jooq.org", - "jOOQ version:3.12.3" - }, - comments = "This class is generated by jOOQ" -) -@SuppressWarnings({"all", "unchecked", "rawtypes"}) -public class LoottrackerlootRecord extends UpdatableRecordImpl implements Record3 -{ - - private static final long serialVersionUID = 693470968; - - /** - * Setter for PUBLIC.LOOTTRACKERLOOT.UNIQUEID. - */ - public void setUniqueid(UUID value) - { - set(0, value); - } - - /** - * Getter for PUBLIC.LOOTTRACKERLOOT.UNIQUEID. - */ - public UUID getUniqueid() - { - return (UUID) get(0); - } - - /** - * Setter for PUBLIC.LOOTTRACKERLOOT.ITEMID. - */ - public void setItemid(Integer value) - { - set(1, value); - } - - /** - * Getter for PUBLIC.LOOTTRACKERLOOT.ITEMID. - */ - public Integer getItemid() - { - return (Integer) get(1); - } - - /** - * Setter for PUBLIC.LOOTTRACKERLOOT.QUANTITY. - */ - public void setQuantity(Integer value) - { - set(2, value); - } - - /** - * Getter for PUBLIC.LOOTTRACKERLOOT.QUANTITY. - */ - public Integer getQuantity() - { - return (Integer) get(2); - } - - // ------------------------------------------------------------------------- - // Primary key information - // ------------------------------------------------------------------------- - - @Override - public Record1 key() - { - return (Record1) super.key(); - } - - // ------------------------------------------------------------------------- - // Record3 type implementation - // ------------------------------------------------------------------------- - - @Override - public Row3 fieldsRow() - { - return (Row3) super.fieldsRow(); - } - - @Override - public Row3 valuesRow() - { - return (Row3) super.valuesRow(); - } - - @Override - public Field field1() - { - return Loottrackerloot.LOOTTRACKERLOOT.UNIQUEID; - } - - @Override - public Field field2() - { - return Loottrackerloot.LOOTTRACKERLOOT.ITEMID; - } - - @Override - public Field field3() - { - return Loottrackerloot.LOOTTRACKERLOOT.QUANTITY; - } - - @Override - public UUID component1() - { - return getUniqueid(); - } - - @Override - public Integer component2() - { - return getItemid(); - } - - @Override - public Integer component3() - { - return getQuantity(); - } - - @Override - public UUID value1() - { - return getUniqueid(); - } - - @Override - public Integer value2() - { - return getItemid(); - } - - @Override - public Integer value3() - { - return getQuantity(); - } - - @Override - public LoottrackerlootRecord value1(UUID value) - { - setUniqueid(value); - return this; - } - - @Override - public LoottrackerlootRecord value2(Integer value) - { - setItemid(value); - return this; - } - - @Override - public LoottrackerlootRecord value3(Integer value) - { - setQuantity(value); - return this; - } - - @Override - public LoottrackerlootRecord values(UUID value1, Integer value2, Integer value3) - { - value1(value1); - value2(value2); - value3(value3); - return this; - } - - // ------------------------------------------------------------------------- - // Constructors - // ------------------------------------------------------------------------- - - /** - * Create a detached LoottrackerlootRecord - */ - public LoottrackerlootRecord() - { - super(Loottrackerloot.LOOTTRACKERLOOT); - } - - /** - * Create a detached, initialised LoottrackerlootRecord - */ - public LoottrackerlootRecord(UUID uniqueid, Integer itemid, Integer quantity) - { - super(Loottrackerloot.LOOTTRACKERLOOT); - - set(0, uniqueid); - set(1, itemid); - set(2, quantity); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/database/data/tables/records/TmorphSetsRecord.java b/runelite-client/src/main/java/net/runelite/client/database/data/tables/records/TmorphSetsRecord.java deleted file mode 100644 index 73f341a46a..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/database/data/tables/records/TmorphSetsRecord.java +++ /dev/null @@ -1,503 +0,0 @@ -/* - * This file is generated by jOOQ. - */ -package net.runelite.client.database.data.tables.records; - - -import javax.annotation.processing.Generated; -import net.runelite.client.database.data.tables.TmorphSets; -import org.jooq.Field; -import org.jooq.Record10; -import org.jooq.Row10; -import org.jooq.impl.TableRecordImpl; - - -/** - * This class is generated by jOOQ. - */ -@Generated( - value = { - "http://www.jooq.org", - "jOOQ version:3.12.3" - }, - comments = "This class is generated by jOOQ" -) -@SuppressWarnings({"all", "unchecked", "rawtypes"}) -public class TmorphSetsRecord extends TableRecordImpl implements Record10 -{ - - private static final long serialVersionUID = 546214401; - - /** - * Setter for PUBLIC.TMORPH_SETS.SET_NAME. - */ - public void setSetName(String value) - { - set(0, value); - } - - /** - * Getter for PUBLIC.TMORPH_SETS.SET_NAME. - */ - public String getSetName() - { - return (String) get(0); - } - - /** - * Setter for PUBLIC.TMORPH_SETS.HELMET. - */ - public void setHelmet(Integer value) - { - set(1, value); - } - - /** - * Getter for PUBLIC.TMORPH_SETS.HELMET. - */ - public Integer getHelmet() - { - return (Integer) get(1); - } - - /** - * Setter for PUBLIC.TMORPH_SETS.CAPE. - */ - public void setCape(Integer value) - { - set(2, value); - } - - /** - * Getter for PUBLIC.TMORPH_SETS.CAPE. - */ - public Integer getCape() - { - return (Integer) get(2); - } - - /** - * Setter for PUBLIC.TMORPH_SETS.AMULET. - */ - public void setAmulet(Integer value) - { - set(3, value); - } - - /** - * Getter for PUBLIC.TMORPH_SETS.AMULET. - */ - public Integer getAmulet() - { - return (Integer) get(3); - } - - /** - * Setter for PUBLIC.TMORPH_SETS.WEAPON. - */ - public void setWeapon(Integer value) - { - set(4, value); - } - - /** - * Getter for PUBLIC.TMORPH_SETS.WEAPON. - */ - public Integer getWeapon() - { - return (Integer) get(4); - } - - /** - * Setter for PUBLIC.TMORPH_SETS.TORSO. - */ - public void setTorso(Integer value) - { - set(5, value); - } - - /** - * Getter for PUBLIC.TMORPH_SETS.TORSO. - */ - public Integer getTorso() - { - return (Integer) get(5); - } - - /** - * Setter for PUBLIC.TMORPH_SETS.SHIELD. - */ - public void setShield(Integer value) - { - set(6, value); - } - - /** - * Getter for PUBLIC.TMORPH_SETS.SHIELD. - */ - public Integer getShield() - { - return (Integer) get(6); - } - - /** - * Setter for PUBLIC.TMORPH_SETS.LEGS. - */ - public void setLegs(Integer value) - { - set(7, value); - } - - /** - * Getter for PUBLIC.TMORPH_SETS.LEGS. - */ - public Integer getLegs() - { - return (Integer) get(7); - } - - /** - * Setter for PUBLIC.TMORPH_SETS.HANDS. - */ - public void setHands(Integer value) - { - set(8, value); - } - - /** - * Getter for PUBLIC.TMORPH_SETS.HANDS. - */ - public Integer getHands() - { - return (Integer) get(8); - } - - /** - * Setter for PUBLIC.TMORPH_SETS.BOOTS. - */ - public void setBoots(Integer value) - { - set(9, value); - } - - /** - * Getter for PUBLIC.TMORPH_SETS.BOOTS. - */ - public Integer getBoots() - { - return (Integer) get(9); - } - - // ------------------------------------------------------------------------- - // Record10 type implementation - // ------------------------------------------------------------------------- - - @Override - public Row10 fieldsRow() - { - return (Row10) super.fieldsRow(); - } - - @Override - public Row10 valuesRow() - { - return (Row10) super.valuesRow(); - } - - @Override - public Field field1() - { - return TmorphSets.TMORPH_SETS.SET_NAME; - } - - @Override - public Field field2() - { - return TmorphSets.TMORPH_SETS.HELMET; - } - - @Override - public Field field3() - { - return TmorphSets.TMORPH_SETS.CAPE; - } - - @Override - public Field field4() - { - return TmorphSets.TMORPH_SETS.AMULET; - } - - @Override - public Field field5() - { - return TmorphSets.TMORPH_SETS.WEAPON; - } - - @Override - public Field field6() - { - return TmorphSets.TMORPH_SETS.TORSO; - } - - @Override - public Field field7() - { - return TmorphSets.TMORPH_SETS.SHIELD; - } - - @Override - public Field field8() - { - return TmorphSets.TMORPH_SETS.LEGS; - } - - @Override - public Field field9() - { - return TmorphSets.TMORPH_SETS.HANDS; - } - - @Override - public Field field10() - { - return TmorphSets.TMORPH_SETS.BOOTS; - } - - @Override - public String component1() - { - return getSetName(); - } - - @Override - public Integer component2() - { - return getHelmet(); - } - - @Override - public Integer component3() - { - return getCape(); - } - - @Override - public Integer component4() - { - return getAmulet(); - } - - @Override - public Integer component5() - { - return getWeapon(); - } - - @Override - public Integer component6() - { - return getTorso(); - } - - @Override - public Integer component7() - { - return getShield(); - } - - @Override - public Integer component8() - { - return getLegs(); - } - - @Override - public Integer component9() - { - return getHands(); - } - - @Override - public Integer component10() - { - return getBoots(); - } - - @Override - public String value1() - { - return getSetName(); - } - - @Override - public Integer value2() - { - return getHelmet(); - } - - @Override - public Integer value3() - { - return getCape(); - } - - @Override - public Integer value4() - { - return getAmulet(); - } - - @Override - public Integer value5() - { - return getWeapon(); - } - - @Override - public Integer value6() - { - return getTorso(); - } - - @Override - public Integer value7() - { - return getShield(); - } - - @Override - public Integer value8() - { - return getLegs(); - } - - @Override - public Integer value9() - { - return getHands(); - } - - @Override - public Integer value10() - { - return getBoots(); - } - - @Override - public TmorphSetsRecord value1(String value) - { - setSetName(value); - return this; - } - - @Override - public TmorphSetsRecord value2(Integer value) - { - setHelmet(value); - return this; - } - - @Override - public TmorphSetsRecord value3(Integer value) - { - setCape(value); - return this; - } - - @Override - public TmorphSetsRecord value4(Integer value) - { - setAmulet(value); - return this; - } - - @Override - public TmorphSetsRecord value5(Integer value) - { - setWeapon(value); - return this; - } - - @Override - public TmorphSetsRecord value6(Integer value) - { - setTorso(value); - return this; - } - - @Override - public TmorphSetsRecord value7(Integer value) - { - setShield(value); - return this; - } - - @Override - public TmorphSetsRecord value8(Integer value) - { - setLegs(value); - return this; - } - - @Override - public TmorphSetsRecord value9(Integer value) - { - setHands(value); - return this; - } - - @Override - public TmorphSetsRecord value10(Integer value) - { - setBoots(value); - return this; - } - - @Override - public TmorphSetsRecord values(String value1, Integer value2, Integer value3, Integer value4, Integer value5, Integer value6, Integer value7, Integer value8, Integer value9, Integer value10) - { - value1(value1); - value2(value2); - value3(value3); - value4(value4); - value5(value5); - value6(value6); - value7(value7); - value8(value8); - value9(value9); - value10(value10); - return this; - } - - // ------------------------------------------------------------------------- - // Constructors - // ------------------------------------------------------------------------- - - /** - * Create a detached TmorphSetsRecord - */ - public TmorphSetsRecord() - { - super(TmorphSets.TMORPH_SETS); - } - - /** - * Create a detached, initialised TmorphSetsRecord - */ - public TmorphSetsRecord(String setName, Integer helmet, Integer cape, Integer amulet, Integer weapon, Integer torso, Integer shield, Integer legs, Integer hands, Integer boots) - { - super(TmorphSets.TMORPH_SETS); - - set(0, setName); - set(1, helmet); - set(2, cape); - set(3, amulet); - set(4, weapon); - set(5, torso); - set(6, shield); - set(7, legs); - set(8, hands); - set(9, boots); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/database/data/tables/records/UserRecord.java b/runelite-client/src/main/java/net/runelite/client/database/data/tables/records/UserRecord.java deleted file mode 100644 index 2aeec56d34..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/database/data/tables/records/UserRecord.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * This file is generated by jOOQ. - */ -package net.runelite.client.database.data.tables.records; - - -import java.util.UUID; -import javax.annotation.processing.Generated; -import net.runelite.client.database.data.tables.User; -import org.jooq.Field; -import org.jooq.Record1; -import org.jooq.Record2; -import org.jooq.Row2; -import org.jooq.impl.UpdatableRecordImpl; - - -/** - * This class is generated by jOOQ. - */ -@Generated( - value = { - "http://www.jooq.org", - "jOOQ version:3.12.3" - }, - comments = "This class is generated by jOOQ" -) -@SuppressWarnings({"all", "unchecked", "rawtypes"}) -public class UserRecord extends UpdatableRecordImpl implements Record2 -{ - - private static final long serialVersionUID = 2077804101; - - /** - * Setter for PUBLIC.USER.UNIQUEID. - */ - public void setUniqueid(UUID value) - { - set(0, value); - } - - /** - * Getter for PUBLIC.USER.UNIQUEID. - */ - public UUID getUniqueid() - { - return (UUID) get(0); - } - - /** - * Setter for PUBLIC.USER.USERNAME. - */ - public void setUsername(String value) - { - set(1, value); - } - - /** - * Getter for PUBLIC.USER.USERNAME. - */ - public String getUsername() - { - return (String) get(1); - } - - // ------------------------------------------------------------------------- - // Primary key information - // ------------------------------------------------------------------------- - - @Override - public Record1 key() - { - return (Record1) super.key(); - } - - // ------------------------------------------------------------------------- - // Record2 type implementation - // ------------------------------------------------------------------------- - - @Override - public Row2 fieldsRow() - { - return (Row2) super.fieldsRow(); - } - - @Override - public Row2 valuesRow() - { - return (Row2) super.valuesRow(); - } - - @Override - public Field field1() - { - return User.USER.UNIQUEID; - } - - @Override - public Field field2() - { - return User.USER.USERNAME; - } - - @Override - public UUID component1() - { - return getUniqueid(); - } - - @Override - public String component2() - { - return getUsername(); - } - - @Override - public UUID value1() - { - return getUniqueid(); - } - - @Override - public String value2() - { - return getUsername(); - } - - @Override - public UserRecord value1(UUID value) - { - setUniqueid(value); - return this; - } - - @Override - public UserRecord value2(String value) - { - setUsername(value); - return this; - } - - @Override - public UserRecord values(UUID value1, String value2) - { - value1(value1); - value2(value2); - return this; - } - - // ------------------------------------------------------------------------- - // Constructors - // ------------------------------------------------------------------------- - - /** - * Create a detached UserRecord - */ - public UserRecord() - { - super(User.USER); - } - - /** - * Create a detached, initialised UserRecord - */ - public UserRecord(UUID uniqueid, String username) - { - super(User.USER); - - set(0, uniqueid); - set(1, username); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/discord/DiscordService.java b/runelite-client/src/main/java/net/runelite/client/discord/DiscordService.java index 05dff45492..cf2a16fd37 100644 --- a/runelite-client/src/main/java/net/runelite/client/discord/DiscordService.java +++ b/runelite-client/src/main/java/net/runelite/client/discord/DiscordService.java @@ -198,7 +198,7 @@ public class DiscordService implements AutoCloseable { log.info("Discord RPC service is ready with user {}.", user.username); currentUser = user; - eventBus.post(DiscordReady.class, new DiscordReady( + eventBus.post(new DiscordReady( user.userId, user.username, user.discriminator, @@ -208,31 +208,31 @@ public class DiscordService implements AutoCloseable private void disconnected(int errorCode, String message) { log.debug("Discord disconnected {}: {}", errorCode, message); - eventBus.post(DiscordDisconnected.class, new DiscordDisconnected(errorCode, message)); + eventBus.post(new DiscordDisconnected(errorCode, message)); } private void errored(int errorCode, String message) { log.warn("Discord error: {} - {}", errorCode, message); - eventBus.post(DiscordErrored.class, new DiscordErrored(errorCode, message)); + eventBus.post(new DiscordErrored(errorCode, message)); } private void joinGame(String joinSecret) { log.debug("Discord join game: {}", joinSecret); - eventBus.post(DiscordJoinGame.class, new DiscordJoinGame(joinSecret)); + eventBus.post(new DiscordJoinGame(joinSecret)); } private void spectateGame(String spectateSecret) { log.debug("Discord spectate game: {}", spectateSecret); - eventBus.post(DiscordSpectateGame.class, new DiscordSpectateGame(spectateSecret)); + eventBus.post(new DiscordSpectateGame(spectateSecret)); } private void joinRequest(DiscordUser user) { log.debug("Discord join request: {}", user); - eventBus.post(DiscordJoinRequest.class, new DiscordJoinRequest( + eventBus.post(new DiscordJoinRequest( user.userId, user.username, user.discriminator, diff --git a/runelite-client/src/main/java/net/runelite/client/discord/events/DiscordDisconnected.java b/runelite-client/src/main/java/net/runelite/client/discord/events/DiscordDisconnected.java index 5d1d85df1c..cc4d110a70 100644 --- a/runelite-client/src/main/java/net/runelite/client/discord/events/DiscordDisconnected.java +++ b/runelite-client/src/main/java/net/runelite/client/discord/events/DiscordDisconnected.java @@ -25,13 +25,12 @@ package net.runelite.client.discord.events; import lombok.Value; -import net.runelite.api.events.Event; /** * Called when the RPC connection has been severed */ @Value -public class DiscordDisconnected implements Event +public class DiscordDisconnected { /** * Discord error code diff --git a/runelite-client/src/main/java/net/runelite/client/discord/events/DiscordErrored.java b/runelite-client/src/main/java/net/runelite/client/discord/events/DiscordErrored.java index 82a80dd8e0..3c50464dbd 100644 --- a/runelite-client/src/main/java/net/runelite/client/discord/events/DiscordErrored.java +++ b/runelite-client/src/main/java/net/runelite/client/discord/events/DiscordErrored.java @@ -25,13 +25,12 @@ package net.runelite.client.discord.events; import lombok.Value; -import net.runelite.api.events.Event; /** * Called when an internal error is caught within the SDK */ @Value -public class DiscordErrored implements Event +public class DiscordErrored { /** * Discord error code. diff --git a/runelite-client/src/main/java/net/runelite/client/discord/events/DiscordJoinGame.java b/runelite-client/src/main/java/net/runelite/client/discord/events/DiscordJoinGame.java index 021e1d1b5e..a9d9421f8a 100644 --- a/runelite-client/src/main/java/net/runelite/client/discord/events/DiscordJoinGame.java +++ b/runelite-client/src/main/java/net/runelite/client/discord/events/DiscordJoinGame.java @@ -25,13 +25,12 @@ package net.runelite.client.discord.events; import lombok.Value; -import net.runelite.api.events.Event; /** * Called when the logged in user joined a game */ @Value -public class DiscordJoinGame implements Event +public class DiscordJoinGame { /** * Obfuscated data of your choosing used as join secret diff --git a/runelite-client/src/main/java/net/runelite/client/discord/events/DiscordJoinRequest.java b/runelite-client/src/main/java/net/runelite/client/discord/events/DiscordJoinRequest.java index 97b720894f..1b71368ac7 100644 --- a/runelite-client/src/main/java/net/runelite/client/discord/events/DiscordJoinRequest.java +++ b/runelite-client/src/main/java/net/runelite/client/discord/events/DiscordJoinRequest.java @@ -25,13 +25,12 @@ package net.runelite.client.discord.events; import lombok.Value; -import net.runelite.api.events.Event; /** * Called when another discord user wants to join the game of the logged in user */ @Value -public class DiscordJoinRequest implements Event +public class DiscordJoinRequest { /** * The userId for the user that requests to join diff --git a/runelite-client/src/main/java/net/runelite/client/discord/events/DiscordReady.java b/runelite-client/src/main/java/net/runelite/client/discord/events/DiscordReady.java index 62a40b38fa..2a6f9d622e 100644 --- a/runelite-client/src/main/java/net/runelite/client/discord/events/DiscordReady.java +++ b/runelite-client/src/main/java/net/runelite/client/discord/events/DiscordReady.java @@ -25,13 +25,12 @@ package net.runelite.client.discord.events; import lombok.Value; -import net.runelite.api.events.Event; /** * Called when the RPC connection has been established */ @Value -public class DiscordReady implements Event +public class DiscordReady { /** * The userId for the active user diff --git a/runelite-client/src/main/java/net/runelite/client/discord/events/DiscordSpectateGame.java b/runelite-client/src/main/java/net/runelite/client/discord/events/DiscordSpectateGame.java index 9f5204af1c..bcff4a74bd 100644 --- a/runelite-client/src/main/java/net/runelite/client/discord/events/DiscordSpectateGame.java +++ b/runelite-client/src/main/java/net/runelite/client/discord/events/DiscordSpectateGame.java @@ -25,13 +25,12 @@ package net.runelite.client.discord.events; import lombok.Value; -import net.runelite.api.events.Event; /** * Called when the logged in user joined to spectate a game */ @Value -public class DiscordSpectateGame implements Event +public class DiscordSpectateGame { /** * Obfuscated data of your choosing used as spectate secret diff --git a/runelite-client/src/main/java/net/runelite/client/eventbus/AccessorGenerator.java b/runelite-client/src/main/java/net/runelite/client/eventbus/AccessorGenerator.java deleted file mode 100644 index 2a1240179b..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/eventbus/AccessorGenerator.java +++ /dev/null @@ -1,85 +0,0 @@ -package net.runelite.client.eventbus; - -import com.google.common.collect.ImmutableSet; -import io.reactivex.rxjava3.functions.Consumer; -import java.lang.invoke.CallSite; -import java.lang.invoke.LambdaMetafactory; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodHandles.Lookup; -import java.lang.invoke.MethodType; -import static java.lang.invoke.MethodType.methodType; -import java.lang.reflect.Method; -import java.util.Set; -import lombok.extern.slf4j.Slf4j; -import net.runelite.api.events.Event; - -@Slf4j -public class AccessorGenerator -{ - public static Set scanSubscribes(Lookup caller, Object ref) - { - ImmutableSet.Builder builder = ImmutableSet.builder(); - - final Class refClass = ref.getClass(); - caller = getPrivateAccess(refClass, caller).in(refClass); - - for (Method method : refClass.getDeclaredMethods()) - { - Subscribe sub = method.getAnnotation(Subscribe.class); - if (sub != null) - { - final Consumer accessor; - final Class paramType = method.getParameterTypes()[0]; - - try - { - accessor = getConsumerFor(caller, ref, method); - } - catch (Throwable t) - { - log.warn("Creating consumer lambda for {} failed!", method, t); - continue; - } - - builder.add(new Subscription(paramType, accessor, sub.takeUntil(), sub.subscribe(), sub.observe())); - } - } - return builder.build(); - } - - @SuppressWarnings("unchecked") - private static Consumer getConsumerFor(Lookup caller, Object ref, Method method) throws Throwable - { - final MethodHandle methodHandle = caller.unreflect(method); - - final MethodType actualConsumer = methodHandle.type().dropParameterTypes(0, 1); - final MethodType eventsConsumer = actualConsumer.erase(); - final MethodType factoryType = methodType(Consumer.class, ref.getClass()); - - final CallSite callsite = LambdaMetafactory.metafactory( - caller, // To get past security checks - "accept", // The name of the method to implement inside the lambda type - factoryType, // Signature of the factory method - eventsConsumer, // Signature of function implementation - methodHandle, // Target method - actualConsumer // Target method signature - ); - - final MethodHandle factory = callsite.getTarget(); - return (Consumer) factory.invoke(ref); - } - - private static Lookup getPrivateAccess(Class into, Lookup from) - { - try - { - return MethodHandles.privateLookupIn(into, from); - } - catch (IllegalAccessException a) - { - log.warn("Failed to get private access in {} from {}", into, from, a); - return from; - } - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/eventbus/EventBus.java b/runelite-client/src/main/java/net/runelite/client/eventbus/EventBus.java index f16a701b84..2340e306dd 100644 --- a/runelite-client/src/main/java/net/runelite/client/eventbus/EventBus.java +++ b/runelite-client/src/main/java/net/runelite/client/eventbus/EventBus.java @@ -1,117 +1,230 @@ +/* + * Copyright (c) 2018, Tomas Slusny + * Copyright (c) 2018, 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.eventbus; -import com.jakewharton.rxrelay3.PublishRelay; -import com.jakewharton.rxrelay3.Relay; -import io.reactivex.rxjava3.annotations.NonNull; -import io.reactivex.rxjava3.core.ObservableTransformer; -import io.reactivex.rxjava3.core.Scheduler; -import io.reactivex.rxjava3.disposables.CompositeDisposable; -import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.functions.Consumer; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import javax.annotation.Nullable; -import javax.inject.Inject; -import javax.inject.Singleton; +import com.google.common.base.Preconditions; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.Multimap; +import java.lang.invoke.CallSite; +import java.lang.invoke.LambdaMetafactory; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Comparator; +import java.util.function.Consumer; +import javax.annotation.Nonnull; +import javax.annotation.concurrent.ThreadSafe; +import lombok.EqualsAndHashCode; +import lombok.RequiredArgsConstructor; +import lombok.Value; import lombok.extern.slf4j.Slf4j; -import net.runelite.api.events.Event; -import net.runelite.client.config.OpenOSRSConfig; +import net.runelite.client.util.ReflectUtil; @Slf4j -@Singleton -public class EventBus implements EventBusInterface +@RequiredArgsConstructor +@ThreadSafe +public class EventBus { - private final Map subscriptionList = new HashMap<>(); - private final Map, Relay> subjectList = new HashMap<>(); - private final Map subscriptionsMap = new HashMap<>(); - - @Inject - private OpenOSRSConfig openOSRSConfig; - - @NonNull - private Relay getSubject(Class eventClass) + @FunctionalInterface + public interface SubscriberMethod { - return subjectList.computeIfAbsent(eventClass, k -> PublishRelay.create().toSerialized()); + void invoke(Object event); } - @NonNull - private CompositeDisposable getCompositeDisposable(@NonNull Object object) + @Value + private static class Subscriber { - CompositeDisposable compositeDisposable = subscriptionsMap.get(object); - if (compositeDisposable == null) + private final Object object; + private final Method method; + private final float priority; + @EqualsAndHashCode.Exclude + private final SubscriberMethod lamda; + + void invoke(final Object arg) throws Exception { - compositeDisposable = new CompositeDisposable(); - subscriptionsMap.put(object, compositeDisposable); + if (lamda != null) + { + lamda.invoke(arg); + } + else + { + method.invoke(object, arg); + } + } + } + + private final Consumer exceptionHandler; + private ImmutableMultimap subscribers = ImmutableMultimap.of(); + + /** + * Instantiates EventBus with default exception handler + */ + public EventBus() + { + this((e) -> log.warn("Uncaught exception in event subscriber", e)); + } + + /** + * Registers subscriber to EventBus. All methods in subscriber and it's parent classes are checked for + * {@link Subscribe} annotation and then added to map of subscriptions. + * + * @param object subscriber to register + * @throws IllegalArgumentException in case subscriber method name is wrong (correct format is 'on' + EventName + */ + public synchronized void register(@Nonnull final Object object) + { + final ImmutableMultimap.Builder builder = ImmutableMultimap.builder(); + + if (subscribers != null) + { + builder.putAll(subscribers); } - return compositeDisposable; + builder.orderValuesBy(Comparator.comparing(Subscriber::getPriority).reversed() + .thenComparing(s -> s.object.getClass().getName())); + + for (Class clazz = object.getClass(); clazz != null; clazz = clazz.getSuperclass()) + { + for (final Method method : clazz.getDeclaredMethods()) + { + final Subscribe sub = method.getAnnotation(Subscribe.class); + + if (sub == null) + { + continue; + } + + Preconditions.checkArgument(method.getReturnType() == Void.TYPE, "@Subscribed method \"" + method + "\" cannot return a value"); + Preconditions.checkArgument(method.getParameterCount() == 1, "@Subscribed method \"" + method + "\" must take exactly 1 argument"); + Preconditions.checkArgument(!Modifier.isStatic(method.getModifiers()), "@Subscribed method \"" + method + "\" cannot be static"); + + final Class parameterClazz = method.getParameterTypes()[0]; + + Preconditions.checkArgument(!parameterClazz.isPrimitive(), "@Subscribed method \"" + method + "\" cannot subscribe to primitives"); + Preconditions.checkArgument((parameterClazz.getModifiers() & (Modifier.ABSTRACT | Modifier.INTERFACE)) == 0, "@Subscribed method \"" + method + "\" cannot subscribe to polymorphic classes"); + + for (Class psc = parameterClazz.getSuperclass(); psc != null; psc = psc.getSuperclass()) + { + if (subscribers.containsKey(psc)) + { + throw new IllegalArgumentException("@Subscribed method \"" + method + "\" cannot subscribe to class which inherits from subscribed class \"" + psc + "\""); + } + } + + final String preferredName = "on" + parameterClazz.getSimpleName(); + Preconditions.checkArgument(method.getName().equals(preferredName), "Subscribed method " + method + " should be named " + preferredName); + + method.setAccessible(true); + SubscriberMethod lambda = null; + + try + { + final MethodHandles.Lookup caller = ReflectUtil.privateLookupIn(clazz); + final MethodType subscription = MethodType.methodType(void.class, parameterClazz); + final MethodHandle target = caller.findVirtual(clazz, method.getName(), subscription); + final CallSite site = LambdaMetafactory.metafactory( + caller, + "invoke", + MethodType.methodType(SubscriberMethod.class, clazz), + subscription.changeParameterType(0, Object.class), + target, + subscription); + + final MethodHandle factory = site.getTarget(); + lambda = (SubscriberMethod) factory.bindTo(object).invokeExact(); + } + catch (Throwable e) + { + log.warn("Unable to create lambda for method {}", method, e); + } + + final Subscriber subscriber = new Subscriber(object, method, sub.priority(), lambda); + builder.put(parameterClazz, subscriber); + log.debug("Registering {} - {}", parameterClazz, subscriber); + } + } + + subscribers = builder.build(); } - private ObservableTransformer applyTake(int until) + /** + * Unregisters all subscribed methods from provided subscriber object. + * + * @param object object to unsubscribe from + */ + public synchronized void unregister(@Nonnull final Object object) { - return observable -> until > 0 ? observable.take(until) : observable; - } - - private ObservableTransformer applyScheduler(EventScheduler eventScheduler, boolean subscribe) - { - Scheduler scheduler = eventScheduler.get(); - - return observable -> scheduler == null ? observable : subscribe ? observable.subscribeOn(scheduler) : observable.observeOn(scheduler); - } - - @Override - public void subscribe(Class eventClass, @NonNull Object lifecycle, @NonNull Consumer action) - { - subscribe(eventClass, lifecycle, action, -1, EventScheduler.DEFAULT, EventScheduler.DEFAULT); - } - - @Override - public void subscribe(Class eventClass, @NonNull Object lifecycle, @NonNull Consumer action, int takeUtil) - { - subscribe(eventClass, lifecycle, action, takeUtil, EventScheduler.DEFAULT, EventScheduler.DEFAULT); - } - - @Override - // Subscribe on lifecycle (for example from plugin startUp -> shutdown) - public void subscribe(Class eventClass, @NonNull Object lifecycle, @NonNull Consumer action, int takeUntil, @Nullable EventScheduler subscribe, @Nullable EventScheduler observe) - { - assert Event.class.isAssignableFrom(eventClass) : "Parameters of methods annotated with @Subscribe should implement net.runelite.api.events.Event"; - - if (subscriptionList.containsKey(lifecycle) && eventClass.equals(subscriptionList.get(lifecycle))) + if (subscribers == null) { return; } - Disposable disposable = getSubject(eventClass) - .compose(applyTake(takeUntil)) - .filter(Objects::nonNull) // Filter out null objects, better safe than sorry - .cast(eventClass) // Cast it for easier usage - .doFinally(() -> unregister(lifecycle)) - .compose(applyScheduler(subscribe, true)) - .compose(applyScheduler(observe, false)) - .subscribe(action, error -> - log.error("Exception in eventbus", error)); + final Multimap map = HashMultimap.create(); + map.putAll(subscribers); - getCompositeDisposable(lifecycle).add(disposable); - subscriptionList.put(lifecycle, eventClass); + for (Class clazz = object.getClass(); clazz != null; clazz = clazz.getSuperclass()) + { + for (final Method method : clazz.getDeclaredMethods()) + { + final Subscribe sub = method.getAnnotation(Subscribe.class); + + if (sub == null) + { + continue; + } + + final Class parameterClazz = method.getParameterTypes()[0]; + map.remove(parameterClazz, new Subscriber(object, method, sub.priority(), null)); + } + } + + subscribers = ImmutableMultimap.copyOf(map); } - @Override - public void unregister(@NonNull Object lifecycle) + /** + * Posts provided event to all registered subscribers. Subscriber calls are invoked immediately, + * ordered by priority then their declaring class' name. + * + * @param event event to post + */ + public void post(@Nonnull final Object event) { - //We have to remove the composition from the map, because once you dispose it can't be used anymore - CompositeDisposable compositeDisposable = subscriptionsMap.remove(lifecycle); - subscriptionList.remove(lifecycle); - if (compositeDisposable != null) + for (final Subscriber subscriber : subscribers.get(event.getClass())) { - compositeDisposable.dispose(); + try + { + subscriber.invoke(event); + } + catch (Exception e) + { + exceptionHandler.accept(e); + } } } - - @Override - public void post(Class eventClass, @NonNull T event) - { - getSubject(eventClass).accept(event); - } } diff --git a/runelite-client/src/main/java/net/runelite/client/eventbus/EventBusInterface.java b/runelite-client/src/main/java/net/runelite/client/eventbus/EventBusInterface.java deleted file mode 100644 index 0e6cbfbd36..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/eventbus/EventBusInterface.java +++ /dev/null @@ -1,18 +0,0 @@ -package net.runelite.client.eventbus; - -import io.reactivex.rxjava3.annotations.NonNull; -import io.reactivex.rxjava3.functions.Consumer; -import net.runelite.api.events.Event; - -public interface EventBusInterface -{ - void subscribe(Class eventClass, @NonNull Object lifecycle, @NonNull Consumer action); - - void subscribe(Class eventClass, @NonNull Object lifecycle, @NonNull Consumer action, int takeUntil); - - void subscribe(Class eventClass, @NonNull Object lifecycle, @NonNull Consumer action, int takeUntil, EventScheduler subscribe, EventScheduler observe); - - void unregister(@NonNull Object lifecycle); - - void post(Class eventClass, @NonNull T event); -} diff --git a/runelite-client/src/main/java/net/runelite/client/eventbus/EventScheduler.java b/runelite-client/src/main/java/net/runelite/client/eventbus/EventScheduler.java deleted file mode 100644 index fb84f9ef75..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/eventbus/EventScheduler.java +++ /dev/null @@ -1,35 +0,0 @@ -package net.runelite.client.eventbus; - -import io.reactivex.rxjava3.core.Scheduler; -import io.reactivex.rxjava3.functions.Supplier; -import io.reactivex.rxjava3.schedulers.Schedulers; -import javax.annotation.Nullable; -import lombok.AllArgsConstructor; - -@AllArgsConstructor -public enum EventScheduler -{ - DEFAULT(() -> null), - COMPUTATION(Schedulers::computation), - IO(Schedulers::io), - NEWTHREAD(Schedulers::newThread), - SINGLE(Schedulers::single), - TRAMPOLINE(Schedulers::trampoline), - CLIENT(Schedulers::single); - - private Supplier scheduler; - - @Nullable - public Scheduler get() - { - try - { - return scheduler.get(); - } - catch (Throwable ignored) - { - } - - return null; - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/eventbus/Subscribe.java b/runelite-client/src/main/java/net/runelite/client/eventbus/Subscribe.java index cdecb670d8..d3a7c0514b 100644 --- a/runelite-client/src/main/java/net/runelite/client/eventbus/Subscribe.java +++ b/runelite-client/src/main/java/net/runelite/client/eventbus/Subscribe.java @@ -38,7 +38,5 @@ import java.lang.annotation.Target; @Documented public @interface Subscribe { - int takeUntil() default -1; - EventScheduler subscribe() default EventScheduler.DEFAULT; - EventScheduler observe() default EventScheduler.DEFAULT; + float priority() default 0; } diff --git a/runelite-client/src/main/java/net/runelite/client/eventbus/Subscription.java b/runelite-client/src/main/java/net/runelite/client/eventbus/Subscription.java deleted file mode 100644 index 1b9fbbaa77..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/eventbus/Subscription.java +++ /dev/null @@ -1,20 +0,0 @@ -package net.runelite.client.eventbus; - -import io.reactivex.rxjava3.functions.Consumer; -import lombok.Value; - -@Value -public class Subscription -{ - private final Class type; - private final Consumer method; - private final int takeUntil; - private final EventScheduler subscribe; - private final EventScheduler observe; - - @SuppressWarnings("unchecked") - public void subscribe(EventBus eventBus, Object lifecycle) - { - eventBus.subscribe(type, lifecycle, method, takeUntil, subscribe, observe); - } -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/events/AttackStyleChanged.java b/runelite-client/src/main/java/net/runelite/client/events/AttackStyleChanged.java deleted file mode 100644 index 98735a7cbf..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/events/AttackStyleChanged.java +++ /dev/null @@ -1,34 +0,0 @@ -package net.runelite.client.events; - -import lombok.Value; -import net.runelite.api.Player; -import net.runelite.api.events.Event; -import net.runelite.client.game.AttackStyle; - -/** - * This will fire when {@link net.runelite.client.game.PlayerManager} detects - * a change in the player appearance that resulted in the shifting of an attack style. - * For example, ranged str went to 0, but melee str went to 108. - */ -@Value -public class AttackStyleChanged implements Event -{ - /** - * The player that changed styles. - */ - Player player; - - /** - * Can be Unknown(nullable) - * - * @see net.runelite.client.game.AttackStyle - */ - AttackStyle oldStyle; - - /** - * Can be Unknown(nullable) - * - * @see net.runelite.client.game.AttackStyle - */ - AttackStyle newStyle; -} diff --git a/runelite-client/src/main/java/net/runelite/client/events/ChatInput.java b/runelite-client/src/main/java/net/runelite/client/events/ChatInput.java index b237e0fdd1..26373f7c03 100644 --- a/runelite-client/src/main/java/net/runelite/client/events/ChatInput.java +++ b/runelite-client/src/main/java/net/runelite/client/events/ChatInput.java @@ -24,18 +24,7 @@ */ package net.runelite.client.events; -import lombok.Getter; -import net.runelite.api.events.Event; - -public abstract class ChatInput implements Event +public abstract class ChatInput { - @Getter - private boolean stop = false; - - public void setStop() - { - this.stop = true; - } - public abstract void resume(); } diff --git a/runelite-client/src/main/java/net/runelite/client/events/ClientShutdown.java b/runelite-client/src/main/java/net/runelite/client/events/ClientShutdown.java index 1597833b32..a4817c39ae 100644 --- a/runelite-client/src/main/java/net/runelite/client/events/ClientShutdown.java +++ b/runelite-client/src/main/java/net/runelite/client/events/ClientShutdown.java @@ -31,13 +31,12 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import lombok.Value; import lombok.extern.slf4j.Slf4j; -import net.runelite.api.events.Event; @Value @Slf4j -public class ClientShutdown implements Event +public class ClientShutdown { - Queue> tasks = new ConcurrentLinkedQueue<>(); + private Queue> tasks = new ConcurrentLinkedQueue<>(); public void waitFor(Future future) { diff --git a/runelite-client/src/main/java/net/runelite/client/events/ConfigChanged.java b/runelite-client/src/main/java/net/runelite/client/events/ConfigChanged.java index d8f3bee83c..db79034c22 100644 --- a/runelite-client/src/main/java/net/runelite/client/events/ConfigChanged.java +++ b/runelite-client/src/main/java/net/runelite/client/events/ConfigChanged.java @@ -24,15 +24,15 @@ */ package net.runelite.client.events; -import java.io.Serializable; +import javax.annotation.Nullable; import lombok.Data; -import net.runelite.api.events.Event; +import net.runelite.client.config.RuneScapeProfile; /** * An event where a configuration entry has been modified. */ @Data -public class ConfigChanged implements Event, Serializable +public class ConfigChanged { /** * The parent group for the key. @@ -41,6 +41,14 @@ public class ConfigChanged implements Event, Serializable * between other key values that may have the same name. */ private String group; + + /** + * The profile that has changed, if any + * + * @see RuneScapeProfile#getKey() + */ + @Nullable + private String profile; /** * The configuration key that has been modified. */ @@ -53,12 +61,4 @@ public class ConfigChanged implements Event, Serializable * The new value of the entry, null if the entry has been unset. */ private String newValue; - /** - * The client where the config value was changed from - */ - private String origin; - /** - * Path of the current config file - */ - private String path; } diff --git a/runelite-client/src/main/java/net/runelite/client/events/ExternalPluginChanged.java b/runelite-client/src/main/java/net/runelite/client/events/ExternalPluginsChanged.java similarity index 81% rename from runelite-client/src/main/java/net/runelite/client/events/ExternalPluginChanged.java rename to runelite-client/src/main/java/net/runelite/client/events/ExternalPluginsChanged.java index 6961813bc4..f38490121e 100644 --- a/runelite-client/src/main/java/net/runelite/client/events/ExternalPluginChanged.java +++ b/runelite-client/src/main/java/net/runelite/client/events/ExternalPluginsChanged.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Owain van Brakel + * Copyright (c) 2019 Abex * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,14 +24,15 @@ */ package net.runelite.client.events; -import lombok.Data; -import net.runelite.api.events.Event; -import net.runelite.client.plugins.Plugin; +import java.util.List; +import lombok.Value; +import net.runelite.client.externalplugins.ExternalPluginManifest; -@Data -public class ExternalPluginChanged implements Event +/** + * Posted when an external plugin has been added, removed, or updated + */ +@Value +public class ExternalPluginsChanged { - private final String pluginId; - private final Plugin plugin; - private final boolean added; + private final List loadedManifest; } diff --git a/runelite-client/src/main/java/net/runelite/client/events/ExternalPluginsLoaded.java b/runelite-client/src/main/java/net/runelite/client/events/ExternalPluginsLoaded.java deleted file mode 100644 index a674165438..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/events/ExternalPluginsLoaded.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2019 Owain van Brakel - * 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.events; - -import lombok.Data; -import net.runelite.api.events.Event; - -@Data -public class ExternalPluginsLoaded implements Event -{} diff --git a/runelite-client/src/main/java/net/runelite/client/events/InfoBoxMenuClicked.java b/runelite-client/src/main/java/net/runelite/client/events/InfoBoxMenuClicked.java index 21cc9a6883..c23b26a75b 100644 --- a/runelite-client/src/main/java/net/runelite/client/events/InfoBoxMenuClicked.java +++ b/runelite-client/src/main/java/net/runelite/client/events/InfoBoxMenuClicked.java @@ -25,13 +25,12 @@ package net.runelite.client.events; import lombok.Value; -import net.runelite.api.events.Event; import net.runelite.client.ui.overlay.OverlayMenuEntry; import net.runelite.client.ui.overlay.infobox.InfoBox; @Value -public class InfoBoxMenuClicked implements Event +public class InfoBoxMenuClicked { - OverlayMenuEntry entry; - InfoBox infoBox; + private OverlayMenuEntry entry; + private InfoBox infoBox; } diff --git a/runelite-client/src/main/java/net/runelite/client/events/NavigationButtonAdded.java b/runelite-client/src/main/java/net/runelite/client/events/NavigationButtonAdded.java index a5a775cfc0..647cbe7016 100644 --- a/runelite-client/src/main/java/net/runelite/client/events/NavigationButtonAdded.java +++ b/runelite-client/src/main/java/net/runelite/client/events/NavigationButtonAdded.java @@ -25,11 +25,10 @@ package net.runelite.client.events; import lombok.Value; -import net.runelite.api.events.Event; import net.runelite.client.ui.NavigationButton; @Value -public class NavigationButtonAdded implements Event +public class NavigationButtonAdded { - NavigationButton button; + private NavigationButton button; } diff --git a/runelite-client/src/main/java/net/runelite/client/events/NavigationButtonRemoved.java b/runelite-client/src/main/java/net/runelite/client/events/NavigationButtonRemoved.java index 87dabb7363..988d18e72b 100644 --- a/runelite-client/src/main/java/net/runelite/client/events/NavigationButtonRemoved.java +++ b/runelite-client/src/main/java/net/runelite/client/events/NavigationButtonRemoved.java @@ -25,11 +25,10 @@ package net.runelite.client.events; import lombok.Value; -import net.runelite.api.events.Event; import net.runelite.client.ui.NavigationButton; @Value -public class NavigationButtonRemoved implements Event +public class NavigationButtonRemoved { - NavigationButton button; + private NavigationButton button; } diff --git a/runelite-client/src/main/java/net/runelite/client/events/NotificationFired.java b/runelite-client/src/main/java/net/runelite/client/events/NotificationFired.java index 77be29a54b..ecf1ffaf2d 100644 --- a/runelite-client/src/main/java/net/runelite/client/events/NotificationFired.java +++ b/runelite-client/src/main/java/net/runelite/client/events/NotificationFired.java @@ -26,11 +26,10 @@ package net.runelite.client.events; import java.awt.TrayIcon; import lombok.Value; -import net.runelite.api.events.Event; @Value -public class NotificationFired implements Event +public class NotificationFired { - String message; - TrayIcon.MessageType type; + final String message; + final TrayIcon.MessageType type; } diff --git a/runelite-client/src/main/java/net/runelite/client/events/NpcLootReceived.java b/runelite-client/src/main/java/net/runelite/client/events/NpcLootReceived.java index e2d77ac782..e3ef8a03da 100644 --- a/runelite-client/src/main/java/net/runelite/client/events/NpcLootReceived.java +++ b/runelite-client/src/main/java/net/runelite/client/events/NpcLootReceived.java @@ -27,12 +27,11 @@ package net.runelite.client.events; import java.util.Collection; import lombok.Value; import net.runelite.api.NPC; -import net.runelite.api.events.Event; import net.runelite.client.game.ItemStack; @Value -public class NpcLootReceived implements Event +public class NpcLootReceived { - NPC npc; - Collection items; + private final NPC npc; + private final Collection items; } diff --git a/runelite-client/src/main/java/net/runelite/client/events/OverlayMenuClicked.java b/runelite-client/src/main/java/net/runelite/client/events/OverlayMenuClicked.java index 9986fac8bd..8b02c808f1 100644 --- a/runelite-client/src/main/java/net/runelite/client/events/OverlayMenuClicked.java +++ b/runelite-client/src/main/java/net/runelite/client/events/OverlayMenuClicked.java @@ -24,19 +24,16 @@ */ package net.runelite.client.events; -import lombok.AllArgsConstructor; -import lombok.Data; -import net.runelite.api.events.Event; +import lombok.Value; import net.runelite.client.ui.overlay.Overlay; import net.runelite.client.ui.overlay.OverlayMenuEntry; /** * Event fired when an overlay menu entry is clicked. */ -@Data -@AllArgsConstructor -public class OverlayMenuClicked implements Event +@Value +public class OverlayMenuClicked { private OverlayMenuEntry entry; private Overlay overlay; -} \ No newline at end of file +} diff --git a/runelite-client/src/main/java/net/runelite/client/events/PartyChanged.java b/runelite-client/src/main/java/net/runelite/client/events/PartyChanged.java index 549337f6e1..0cb8a94c3e 100644 --- a/runelite-client/src/main/java/net/runelite/client/events/PartyChanged.java +++ b/runelite-client/src/main/java/net/runelite/client/events/PartyChanged.java @@ -26,10 +26,9 @@ package net.runelite.client.events; import java.util.UUID; import lombok.Value; -import net.runelite.api.events.Event; @Value -public class PartyChanged implements Event +public class PartyChanged { - UUID partyId; + private final UUID partyId; } diff --git a/runelite-client/src/main/java/net/runelite/client/events/PlayerLootReceived.java b/runelite-client/src/main/java/net/runelite/client/events/PlayerLootReceived.java index ec611d4be8..56eb722a83 100644 --- a/runelite-client/src/main/java/net/runelite/client/events/PlayerLootReceived.java +++ b/runelite-client/src/main/java/net/runelite/client/events/PlayerLootReceived.java @@ -27,12 +27,11 @@ package net.runelite.client.events; import java.util.Collection; import lombok.Value; import net.runelite.api.Player; -import net.runelite.api.events.Event; import net.runelite.client.game.ItemStack; @Value -public class PlayerLootReceived implements Event +public class PlayerLootReceived { - Player player; - Collection items; + private final Player player; + private final Collection items; } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/events/PluginChanged.java b/runelite-client/src/main/java/net/runelite/client/events/PluginChanged.java index 723cd8e0b3..c237782732 100644 --- a/runelite-client/src/main/java/net/runelite/client/events/PluginChanged.java +++ b/runelite-client/src/main/java/net/runelite/client/events/PluginChanged.java @@ -25,11 +25,10 @@ package net.runelite.client.events; import lombok.Data; -import net.runelite.api.events.Event; import net.runelite.client.plugins.Plugin; @Data -public class PluginChanged implements Event +public class PluginChanged { private final Plugin plugin; private final boolean loaded; diff --git a/runelite-client/src/main/java/net/runelite/client/events/ExternalRepositoryChanged.java b/runelite-client/src/main/java/net/runelite/client/events/RuneScapeProfileChanged.java similarity index 83% rename from runelite-client/src/main/java/net/runelite/client/events/ExternalRepositoryChanged.java rename to runelite-client/src/main/java/net/runelite/client/events/RuneScapeProfileChanged.java index 7ae9e3624e..9205fe0958 100644 --- a/runelite-client/src/main/java/net/runelite/client/events/ExternalRepositoryChanged.java +++ b/runelite-client/src/main/java/net/runelite/client/events/RuneScapeProfileChanged.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Owain van Brakel + * Copyright (c) 2020 Abex * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,12 +24,11 @@ */ package net.runelite.client.events; -import lombok.Data; -import net.runelite.api.events.Event; - -@Data -public class ExternalRepositoryChanged implements Event +/** + * Posted when the user switches to a different RuneScape save profile + * This might be because they logged into a different account, or hopped + * to/from a Beta/Tournament/DMM/Leagues world + */ +public class RuneScapeProfileChanged { - private final String owner; - private final boolean added; } diff --git a/runelite-client/src/main/java/net/runelite/client/events/SessionClose.java b/runelite-client/src/main/java/net/runelite/client/events/SessionClose.java index 150b66f412..e8a2f227cb 100644 --- a/runelite-client/src/main/java/net/runelite/client/events/SessionClose.java +++ b/runelite-client/src/main/java/net/runelite/client/events/SessionClose.java @@ -25,7 +25,6 @@ package net.runelite.client.events; import lombok.Data; -import net.runelite.api.events.Event; /** * An event where a new RuneLite account session has been closed, @@ -35,4 +34,7 @@ import net.runelite.api.events.Event; * it has nothing to do with whether an account is being logged out. */ @Data -public class SessionClose implements Event {} +public class SessionClose +{ + +} diff --git a/runelite-client/src/main/java/net/runelite/client/events/SessionOpen.java b/runelite-client/src/main/java/net/runelite/client/events/SessionOpen.java index 130305798e..971ba7cb2e 100644 --- a/runelite-client/src/main/java/net/runelite/client/events/SessionOpen.java +++ b/runelite-client/src/main/java/net/runelite/client/events/SessionOpen.java @@ -25,7 +25,6 @@ package net.runelite.client.events; import lombok.Data; -import net.runelite.api.events.Event; /** * An event where a new RuneLite account session has been opened @@ -35,4 +34,7 @@ import net.runelite.api.events.Event; * it has nothing to do with whether an account is being logged in. */ @Data -public class SessionOpen implements Event {} +public class SessionOpen +{ + +} diff --git a/runelite-client/src/main/java/net/runelite/client/events/WorldsFetch.java b/runelite-client/src/main/java/net/runelite/client/events/WorldsFetch.java index aa74ab9ee6..cc07aa5a23 100644 --- a/runelite-client/src/main/java/net/runelite/client/events/WorldsFetch.java +++ b/runelite-client/src/main/java/net/runelite/client/events/WorldsFetch.java @@ -25,14 +25,13 @@ package net.runelite.client.events; import lombok.Value; -import net.runelite.api.events.Event; import net.runelite.http.api.worlds.WorldResult; /** * Fired when the @{link net.runelite.client.game.WorldService} refreshes the world list */ @Value -public class WorldsFetch implements Event +public class WorldsFetch { - WorldResult worldResult; -} \ No newline at end of file + private final WorldResult worldResult; +} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/FixedWidthPanel.java b/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginClassLoader.java similarity index 75% rename from runelite-client/src/main/java/net/runelite/client/ui/components/FixedWidthPanel.java rename to runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginClassLoader.java index fd951aac30..054a7779b5 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/components/FixedWidthPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginClassLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Adam + * Copyright (c) 2019 Abex * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,17 +22,20 @@ * (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.components; +package net.runelite.client.externalplugins; -import java.awt.Dimension; -import javax.swing.JPanel; -import net.runelite.client.ui.PluginPanel; +import java.net.URL; +import java.net.URLClassLoader; +import lombok.Getter; -class FixedWidthPanel extends JPanel +class ExternalPluginClassLoader extends URLClassLoader { - @Override - public Dimension getPreferredSize() + @Getter + private final ExternalPluginManifest manifest; + + ExternalPluginClassLoader(ExternalPluginManifest manifest, URL[] urls) { - return new Dimension(PluginPanel.PANEL_WIDTH, super.getPreferredSize().height); + super(urls, ExternalPluginClassLoader.class.getClassLoader()); + this.manifest = manifest; } -} \ No newline at end of file +} diff --git a/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginClient.java b/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginClient.java new file mode 100644 index 0000000000..0df12bf1ed --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginClient.java @@ -0,0 +1,175 @@ +/* + * 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.externalplugins; + +import com.google.common.reflect.TypeToken; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.Signature; +import java.security.SignatureException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.util.List; +import javax.imageio.ImageIO; +import javax.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import net.runelite.client.RuneLiteProperties; +import net.runelite.client.util.VerificationException; +import net.runelite.http.api.RuneLiteAPI; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okio.BufferedSource; + +@Slf4j +public class ExternalPluginClient +{ + private final OkHttpClient okHttpClient; + + @Inject + private ExternalPluginClient(OkHttpClient okHttpClient) + { + this.okHttpClient = okHttpClient; + } + + public List downloadManifest() throws IOException, VerificationException + { + HttpUrl manifest = RuneLiteProperties.getPluginHubBase() + .newBuilder() + .addPathSegments("manifest.js") + .build(); + try (Response res = okHttpClient.newCall(new Request.Builder().url(manifest).build()).execute()) + { + if (res.code() != 200) + { + throw new IOException("Non-OK response code: " + res.code()); + } + + BufferedSource src = res.body().source(); + + byte[] signature = new byte[src.readInt()]; + src.readFully(signature); + + byte[] data = src.readByteArray(); + Signature s = Signature.getInstance("SHA256withRSA"); + s.initVerify(loadCertificate()); + s.update(data); + + if (!s.verify(signature)) + { + throw new VerificationException("Unable to verify external plugin manifest"); + } + + return RuneLiteAPI.GSON.fromJson(new String(data, StandardCharsets.UTF_8), + new TypeToken>() + { + }.getType()); + } + catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) + { + throw new RuntimeException(e); + } + } + + public BufferedImage downloadIcon(ExternalPluginManifest plugin) throws IOException + { + if (!plugin.hasIcon()) + { + return null; + } + + HttpUrl url = RuneLiteProperties.getPluginHubBase() + .newBuilder() + .addPathSegment(plugin.getInternalName()) + .addPathSegment(plugin.getCommit() + ".png") + .build(); + + try (Response res = okHttpClient.newCall(new Request.Builder().url(url).build()).execute()) + { + byte[] bytes = res.body().bytes(); + // We don't stream so the lock doesn't block the edt trying to load something at the same time + synchronized (ImageIO.class) + { + return ImageIO.read(new ByteArrayInputStream(bytes)); + } + } + } + + private static Certificate loadCertificate() + { + try + { + CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); + Certificate certificate = certFactory.generateCertificate(ExternalPluginClient.class.getResourceAsStream("externalplugins.crt")); + return certificate; + } + catch (CertificateException e) + { + throw new RuntimeException(e); + } + } + + void submitPlugins(List plugins) + { + if (plugins.isEmpty()) + { + return; + } + + HttpUrl url = RuneLiteAPI.getApiBase().newBuilder() + .addPathSegment("pluginhub") + .build(); + + Request request = new Request.Builder() + .url(url) + .post(RequestBody.create(RuneLiteAPI.JSON, RuneLiteAPI.GSON.toJson(plugins))) + .build(); + + okHttpClient.newCall(request).enqueue(new Callback() + { + @Override + public void onFailure(Call call, IOException e) + { + log.debug("Error submitting plugins", e); + } + + @Override + public void onResponse(Call call, Response response) + { + log.debug("Submitted plugin list"); + response.close(); + } + }); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginManager.java b/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginManager.java new file mode 100644 index 0000000000..504331f166 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginManager.java @@ -0,0 +1,429 @@ +/* + * 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.externalplugins; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import com.google.common.hash.Hashing; +import com.google.common.hash.HashingInputStream; +import com.google.common.io.Files; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import javax.swing.SwingUtilities; +import lombok.extern.slf4j.Slf4j; +import net.runelite.client.RuneLite; +import net.runelite.client.RuneLiteProperties; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.config.RuneLiteConfig; +import net.runelite.client.eventbus.EventBus; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.ExternalPluginsChanged; +import net.runelite.client.events.SessionClose; +import net.runelite.client.events.SessionOpen; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginInstantiationException; +import net.runelite.client.plugins.PluginManager; +import net.runelite.client.ui.SplashScreen; +import net.runelite.client.util.CountingInputStream; +import net.runelite.client.util.Text; +import net.runelite.client.util.VerificationException; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +@Singleton +@Slf4j +public class ExternalPluginManager +{ + private static final String PLUGIN_LIST_KEY = "externalPlugins"; + private static Class[] builtinExternals = null; + + @Inject + @Named("safeMode") + private boolean safeMode; + + private final ConfigManager configManager; + private final ExternalPluginClient externalPluginClient; + private final ScheduledExecutorService executor; + private final PluginManager pluginManager; + private final EventBus eventBus; + private final OkHttpClient okHttpClient; + + @Inject + private ExternalPluginManager( + ConfigManager configManager, + ExternalPluginClient externalPluginClient, + ScheduledExecutorService executor, + PluginManager pluginManager, + EventBus eventBus, + OkHttpClient okHttpClient + ) + { + this.configManager = configManager; + this.externalPluginClient = externalPluginClient; + this.executor = executor; + this.pluginManager = pluginManager; + this.eventBus = eventBus; + this.okHttpClient = okHttpClient; + + executor.scheduleWithFixedDelay(() -> externalPluginClient.submitPlugins(getInstalledExternalPlugins()), + new Random().nextInt(60), 180, TimeUnit.MINUTES); + } + + public void loadExternalPlugins() throws PluginInstantiationException + { + refreshPlugins(); + + if (builtinExternals != null) + { + // builtin external's don't actually have a manifest or a separate classloader... + pluginManager.loadPlugins(Lists.newArrayList(builtinExternals), null); + } + } + + @Subscribe + public void onSessionOpen(SessionOpen event) + { + executor.submit(this::refreshPlugins); + } + + @Subscribe + public void onSessionClose(SessionClose event) + { + executor.submit(this::refreshPlugins); + } + + private void refreshPlugins() + { + if (safeMode) + { + log.debug("External plugins are disabled in safe mode!"); + return; + } + + Multimap loadedExternalPlugins = HashMultimap.create(); + for (Plugin p : pluginManager.getPlugins()) + { + ExternalPluginManifest m = getExternalPluginManifest(p.getClass()); + if (m != null) + { + loadedExternalPlugins.put(m, p); + } + } + + List installedIDs = getInstalledExternalPlugins(); + if (installedIDs.isEmpty() && loadedExternalPlugins.isEmpty()) + { + return; + } + + boolean startup = SplashScreen.isOpen(); + try + { + double splashStart = startup ? .60 : 0; + double splashLength = startup ? .10 : 1; + if (!startup) + { + SplashScreen.init(); + } + + SplashScreen.stage(splashStart, null, "Downloading external plugins"); + Set externalPlugins = new HashSet<>(); + + RuneLite.PLUGINS_DIR.mkdirs(); + + List manifestList; + try + { + manifestList = externalPluginClient.downloadManifest(); + Map manifests = manifestList + .stream().collect(ImmutableMap.toImmutableMap(ExternalPluginManifest::getInternalName, Function.identity())); + + Set needsDownload = new HashSet<>(); + Set keep = new HashSet<>(); + + for (String name : installedIDs) + { + ExternalPluginManifest manifest = manifests.get(name); + if (manifest != null) + { + externalPlugins.add(manifest); + + if (!manifest.isValid()) + { + needsDownload.add(manifest); + } + else + { + keep.add(manifest.getJarFile()); + } + } + } + + // delete old plugins + File[] files = RuneLite.PLUGINS_DIR.listFiles(); + if (files != null) + { + for (File fi : files) + { + if (!keep.contains(fi)) + { + fi.delete(); + } + } + } + + int toDownload = needsDownload.stream().mapToInt(ExternalPluginManifest::getSize).sum(); + int downloaded = 0; + + for (ExternalPluginManifest manifest : needsDownload) + { + HttpUrl url = RuneLiteProperties.getPluginHubBase().newBuilder() + .addPathSegment(manifest.getInternalName()) + .addPathSegment(manifest.getCommit() + ".jar") + .build(); + + try (Response res = okHttpClient.newCall(new Request.Builder().url(url).build()).execute()) + { + int fdownloaded = downloaded; + downloaded += manifest.getSize(); + HashingInputStream his = new HashingInputStream(Hashing.sha256(), + new CountingInputStream(res.body().byteStream(), i -> + SplashScreen.stage(splashStart + (splashLength * .2), splashStart + (splashLength * .8), + null, "Downloading " + manifest.getDisplayName(), + i + fdownloaded, toDownload, true))); + Files.asByteSink(manifest.getJarFile()).writeFrom(his); + if (!his.hash().toString().equals(manifest.getHash())) + { + throw new VerificationException("Plugin " + manifest.getInternalName() + " didn't match its hash"); + } + } + catch (IOException | VerificationException e) + { + externalPlugins.remove(manifest); + log.error("Unable to download external plugin \"{}\"", manifest.getInternalName(), e); + } + } + } + catch (IOException | VerificationException e) + { + log.error("Unable to download external plugins", e); + return; + } + + SplashScreen.stage(splashStart + (splashLength * .8), null, "Starting external plugins"); + + // TODO(abex): make sure the plugins get fully removed from the scheduler/eventbus/other managers (iterate and check classloader) + Set add = new HashSet<>(); + for (ExternalPluginManifest ex : externalPlugins) + { + if (loadedExternalPlugins.removeAll(ex).size() <= 0) + { + add.add(ex); + } + } + // list of loaded external plugins that aren't in the manifest + Collection remove = loadedExternalPlugins.values(); + + for (Plugin p : remove) + { + log.info("Stopping external plugin \"{}\"", p.getClass()); + try + { + SwingUtilities.invokeAndWait(() -> + { + try + { + pluginManager.stopPlugin(p); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + }); + } + catch (InterruptedException | InvocationTargetException e) + { + log.warn("Unable to stop external plugin \"{}\"", p.getClass().getName(), e); + } + pluginManager.remove(p); + } + + for (ExternalPluginManifest manifest : add) + { + // I think this can't happen, but just in case + if (!manifest.isValid()) + { + log.warn("Invalid plugin for validated manifest: {}", manifest); + continue; + } + + log.info("Loading external plugin \"{}\" version \"{}\" commit \"{}\"", manifest.getInternalName(), manifest.getVersion(), manifest.getCommit()); + + List newPlugins = null; + try + { + ClassLoader cl = new ExternalPluginClassLoader(manifest, new URL[]{manifest.getJarFile().toURI().toURL()}); + List> clazzes = new ArrayList<>(); + for (String className : manifest.getPlugins()) + { + clazzes.add(cl.loadClass(className)); + } + + List newPlugins2 = newPlugins = pluginManager.loadPlugins(clazzes, null); + if (!startup) + { + pluginManager.loadDefaultPluginConfiguration(newPlugins); + + SwingUtilities.invokeAndWait(() -> + { + try + { + for (Plugin p : newPlugins2) + { + pluginManager.startPlugin(p); + } + } + catch (PluginInstantiationException e) + { + throw new RuntimeException(e); + } + }); + } + } + catch (ThreadDeath e) + { + throw e; + } + catch (Throwable e) + { + log.warn("Unable to start or load external plugin \"{}\"", manifest.getInternalName(), e); + if (newPlugins != null) + { + for (Plugin p : newPlugins) + { + try + { + SwingUtilities.invokeAndWait(() -> + { + try + { + pluginManager.stopPlugin(p); + } + catch (Exception e2) + { + throw new RuntimeException(e2); + } + }); + } + catch (InterruptedException | InvocationTargetException e2) + { + log.info("Unable to fully stop plugin \"{}\"", manifest.getInternalName(), e2); + } + pluginManager.remove(p); + } + } + } + } + + if (!startup) + { + eventBus.post(new ExternalPluginsChanged(manifestList)); + } + } + finally + { + if (!startup) + { + SplashScreen.stop(); + } + } + } + + public List getInstalledExternalPlugins() + { + String externalPluginsStr = configManager.getConfiguration(RuneLiteConfig.GROUP_NAME, PLUGIN_LIST_KEY); + return Text.fromCSV(externalPluginsStr == null ? "" : externalPluginsStr); + } + + public void install(String key) + { + Set plugins = new HashSet<>(getInstalledExternalPlugins()); + if (plugins.add(key)) + { + configManager.setConfiguration(RuneLiteConfig.GROUP_NAME, PLUGIN_LIST_KEY, Text.toCSV(plugins)); + executor.submit(this::refreshPlugins); + } + } + + public void remove(String key) + { + Set plugins = new HashSet<>(getInstalledExternalPlugins()); + if (plugins.remove(key)) + { + configManager.setConfiguration(RuneLiteConfig.GROUP_NAME, PLUGIN_LIST_KEY, Text.toCSV(plugins)); + executor.submit(this::refreshPlugins); + } + } + + public void update() + { + executor.submit(this::refreshPlugins); + } + + public static ExternalPluginManifest getExternalPluginManifest(Class plugin) + { + ClassLoader cl = plugin.getClassLoader(); + if (cl instanceof ExternalPluginClassLoader) + { + ExternalPluginClassLoader ecl = (ExternalPluginClassLoader) cl; + return ecl.getManifest(); + } + return null; + } + + public static void loadBuiltin(Class... plugins) + { + builtinExternals = plugins; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/ComboBoxIconEntry.java b/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginManifest.java similarity index 54% rename from runelite-client/src/main/java/net/runelite/client/ui/components/ComboBoxIconEntry.java rename to runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginManifest.java index 7c94851bc9..31ec502ed2 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/components/ComboBoxIconEntry.java +++ b/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginManifest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, TheStonedTurtle + * Copyright (c) 2019 Abex * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,23 +22,68 @@ * (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.components; +package net.runelite.client.externalplugins; +import com.google.common.hash.Hashing; +import com.google.common.io.Files; +import java.io.File; +import java.io.IOException; +import java.net.URL; import javax.annotation.Nullable; -import javax.swing.Icon; -import lombok.AllArgsConstructor; -import lombok.Getter; +import lombok.Data; +import lombok.EqualsAndHashCode; +import net.runelite.client.RuneLite; -/** - * Used with ComboBoxListRenderer to render an icon next to the text of the list entry. - * Also supports adding a data object to be used for more complex selection logic - */ -@AllArgsConstructor -@Getter -public class ComboBoxIconEntry +@Data +public class ExternalPluginManifest { - private Icon icon; - private String text; + private String internalName; + private String commit; + private String hash; + private int size; + private String[] plugins; + + private String displayName; + private String version; + private String author; @Nullable - private Object data; -} \ No newline at end of file + private String description; + @Nullable + private String warning; + @Nullable + private String[] tags; + @EqualsAndHashCode.Exclude + private URL support; + private boolean hasIcon; + + public boolean hasIcon() + { + return hasIcon; + } + + File getJarFile() + { + return new File(RuneLite.PLUGINS_DIR, internalName + commit + ".jar"); + } + + boolean isValid() + { + File file = getJarFile(); + + try + { + if (file.exists()) + { + String hash = Files.asByteSource(file).hash(Hashing.sha256()).toString(); + if (this.hash.equals(hash)) + { + return true; + } + } + } + catch (IOException e) + { + } + return false; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/game/AgilityShortcut.java b/runelite-client/src/main/java/net/runelite/client/game/AgilityShortcut.java index 91b0ef4f7c..d7a50592f6 100644 --- a/runelite-client/src/main/java/net/runelite/client/game/AgilityShortcut.java +++ b/runelite-client/src/main/java/net/runelite/client/game/AgilityShortcut.java @@ -28,56 +28,57 @@ package net.runelite.client.game; import lombok.Getter; import static net.runelite.api.NullObjectID.*; import static net.runelite.api.ObjectID.*; +import net.runelite.api.TileObject; import net.runelite.api.coords.WorldPoint; @Getter public enum AgilityShortcut { GENERIC_SHORTCUT(1, "Shortcut", null, - // Trollheim - ROCKS_3790, ROCKS_3791, - // Fremennik Slayer Cave - STEPS_29993, - // Fossil Island - LADDER_30938, LADDER_30939, LADDER_30940, LADDER_30941, RUBBER_CAP_MUSHROOM, - // Brimhaven dungeon - CREVICE_30198, - // Lumbridge - STILE_12982, - // Gu'Tanoth Bridge - GAP, GAP_2831, - // Lumbridge Swamp Caves - STEPPING_STONE_5948, STEPPING_STONE_5949, ROCKS_6673, - // Morytania Pirate Ship - ROCK_16115, - // Lumber Yard - BROKEN_FENCE_2618, - // McGrubor's Wood - LOOSE_RAILING, - // Underwater Area Fossil Island - TUNNEL_30959, HOLE_30966, OBSTACLE, OBSTACLE_30767, OBSTACLE_30964, OBSTACLE_30962, PLANT_DOOR_30961, - // Tree Gnome Village - LOOSE_RAILING_2186, - // Burgh de Rott - LOW_FENCE, - // Taverley - STILE, - // Asgarnian Ice Dungeon - STEPS, - // Fossil Island Wyvern Cave - STAIRS_31485, - // Trollweiss Mountain Cave - ROCKY_HANDHOLDS, ROCKY_HANDHOLDS_19847, - // Witchaven Dungeon - SHORTCUT), + // Trollheim + ROCKS_3790, ROCKS_3791, + // Fremennik Slayer Cave + STEPS_29993, + // Fossil Island + LADDER_30938, LADDER_30939, LADDER_30940, LADDER_30941, RUBBER_CAP_MUSHROOM, + // Brimhaven dungeon + CREVICE_30198, + // Lumbridge + STILE_12982, + // Gu'Tanoth Bridge + GAP, GAP_2831, + // Lumbridge Swamp Caves + STEPPING_STONE_5948, STEPPING_STONE_5949, ROCKS_6673, + // Morytania Pirate Ship + ROCK_16115, + // Lumber Yard + BROKEN_FENCE_2618, + // McGrubor's Wood + LOOSE_RAILING, + // Underwater Area Fossil Island + TUNNEL_30959, HOLE_30966, OBSTACLE, OBSTACLE_30767, OBSTACLE_30964, OBSTACLE_30962, PLANT_DOOR_30961, + // Tree Gnome Village + LOOSE_RAILING_2186, + // Burgh de Rott + LOW_FENCE, + // Taverley + STILE, + // Asgarnian Ice Dungeon + STEPS, + // Fossil Island Wyvern Cave + STAIRS_31485, + // Trollweiss Mountain Cave + ROCKY_HANDHOLDS, ROCKY_HANDHOLDS_19847, + // Witchaven Dungeon + SHORTCUT), BRIMHAVEN_DUNGEON_MEDIUM_PIPE_RETURN(1, "Pipe Squeeze", null, new WorldPoint(2698, 9491, 0), PIPE_21727), BRIMHAVEN_DUNGEON_PIPE_RETURN(1, "Pipe Squeeze", null, new WorldPoint(2655, 9573, 0), PIPE_21728), BRIMHAVEN_DUNGEON_STEPPING_STONES_RETURN(1, "Pipe Squeeze", null, STEPPING_STONE_21739), BRIMHAVEN_DUNGEON_LOG_BALANCE_RETURN(1, "Log Balance", null, LOG_BALANCE_20884), AGILITY_PYRAMID_ROCKS_WEST(1, "Rocks", null, CLIMBING_ROCKS_11948), CAIRN_ISLE_CLIMBING_ROCKS(1, "Rocks", null, CLIMBING_ROCKS), - KARAMJA_GLIDER_LOG(1, "Log Balance", new WorldPoint(2906, 3050, 0), A_WOODEN_LOG), - FALADOR_CRUMBLING_WALL(5, "Crumbling Wall", new WorldPoint(2936, 3357, 0), CRUMBLING_WALL_24222), + KARAMJA_GLIDER_LOG(1, "Log Balance", new WorldPoint(2906, 3050, 0), A_WOODEN_LOG ), + FALADOR_CRUMBLING_WALL(5, "Crumbling Wall", new WorldPoint(2936, 3357, 0), CRUMBLING_WALL_24222 ), RIVER_LUM_GRAPPLE_WEST(8, "Grapple Broken Raft", new WorldPoint(3245, 3179, 0), BROKEN_RAFT), RIVER_LUM_GRAPPLE_EAST(8, "Grapple Broken Raft", new WorldPoint(3258, 3179, 0), BROKEN_RAFT), CORSAIR_COVE_ROCKS(10, "Rocks", new WorldPoint(2545, 2871, 0), ROCKS_31757), @@ -88,7 +89,7 @@ public enum AgilityShortcut GOBLIN_VILLAGE_WALL(14, "Wall", new WorldPoint(2925, 3523, 0), TIGHTGAP), CORSAIR_COVE_DUNGEON_PILLAR(15, "Pillar Jump", new WorldPoint(1980, 8996, 0), PILLAR_31809), EDGEVILLE_DUNGEON_MONKEYBARS(15, "Monkey Bars", null, MONKEYBARS_23566), - TROLLHEIM_ROCKS(15, "Rocks", null, new WorldPoint(2838, 3614, 0), ROCKS_3748), // No fixed world map location, but rocks near death plateau have a requirement of 15 + TROLLHEIM_ROCKS(15, "Rocks", null, new WorldPoint(2838, 3614, 0), ROCKS_3748), // No fixed world map location, but rocks near death plateau have a requirement of 15 YANILLE_UNDERWALL_TUNNEL(16, "Underwall Tunnel", new WorldPoint(2574, 3109, 0), HOLE_16520, CASTLE_WALL), KOUREND_CATACOMBS_SOUTH_WEST_CREVICE_NORTH(17, "Crevice", new WorldPoint(1647, 10008, 0), CRACK_28892), KOUREND_CATACOMBS_SOUTH_WEST_CREVICE_SOUTH(17, "Crevice", new WorldPoint(1645, 10001, 0), CRACK_28892), @@ -151,7 +152,7 @@ public enum AgilityShortcut ISAFDAR_FOREST_OBSTACLES(56, "Trap", null, DENSE_FOREST_3938, DENSE_FOREST_3939, DENSE_FOREST_3998, DENSE_FOREST_3999, DENSE_FOREST, LEAVES, LEAVES_3924, LEAVES_3925, STICKS, TRIPWIRE, TRIPWIRE_3921), RELEKKA_EAST_FENCE(57, "Fence", new WorldPoint(2688, 3697, 0), BROKEN_FENCE), YANILLE_DUNGEON_MONKEY_BARS(57, "Monkey Bars", null, MONKEYBARS_23567), - PHASMATYS_ECTOPOOL_SHORTCUT(58, "Weathered Wall", null, WEATHERED_WALL, WEATHERED_WALL_16526), + PHASMATYS_ECTOPOOL_SHORTCUT(58, "Weathered Wall", null , WEATHERED_WALL, WEATHERED_WALL_16526), ELVEN_OVERPASS_CLIFF_SCRAMBLE(59, "Rocks", new WorldPoint(2345, 3300, 0), ROCKS_16514, ROCKS_16515), ELVEN_OVERPASS_CLIFF_SCRAMBLE_PRIFDDINAS(59, "Rocks", new WorldPoint(3369, 6052, 0), ROCKS_16514, ROCKS_16515), WILDERNESS_GWD_CLIMB_EAST(60, "Rocks", new WorldPoint(2943, 3770, 0), ROCKY_HANDHOLDS_26400, ROCKY_HANDHOLDS_26401, ROCKY_HANDHOLDS_26402, ROCKY_HANDHOLDS_26404, ROCKY_HANDHOLDS_26405, ROCKY_HANDHOLDS_26406), @@ -182,9 +183,17 @@ public enum AgilityShortcut TAVERLEY_DUNGEON_PIPE_BLUE_DRAGON(70, "Pipe Squeeze", new WorldPoint(2886, 9798, 0), OBSTACLE_PIPE_16509), TAVERLEY_DUNGEON_ROCKS_NORTH(70, "Rocks", new WorldPoint(2887, 9823, 0), ROCKS, ROCKS_14106), TAVERLEY_DUNGEON_ROCKS_SOUTH(70, "Rocks", new WorldPoint(2887, 9631, 0), ROCKS, ROCKS_14106), - FOSSIL_ISLAND_HARDWOOD_NORTH(70, "Hole", new WorldPoint(3712, 3828, 0), HOLE_31481, HOLE_31482), - FOSSIL_ISLAND_HARDWOOD_SOUTH(70, "Hole", new WorldPoint(3714, 3816, 0), HOLE_31481, HOLE_31482), - AL_KHARID_WINDOW(70, "Window", new WorldPoint(3293, 3158, 0), BROKEN_WALL_33344, BIG_WINDOW), + FOSSIL_ISLAND_HARDWOOD_NORTH(70, "Hole" , new WorldPoint(3712, 3828, 0), HOLE_31481, HOLE_31482), + FOSSIL_ISLAND_HARDWOOD_SOUTH(70, "Hole" , new WorldPoint(3714, 3816, 0), HOLE_31481, HOLE_31482), + AL_KHARID_WINDOW(70, "Window", new WorldPoint(3295, 3158, 0), BROKEN_WALL_33344, BIG_WINDOW) + { + @Override + public boolean matches(TileObject object) + { + // there are two BIG_WINDOW objects right next to each other here, but only this one is valid + return object.getId() != BIG_WINDOW || object.getWorldLocation().equals(getWorldLocation()); + } + }, GWD_SARADOMIN_ROPE_NORTH(70, "Rope Descent", new WorldPoint(2912, 5300, 0), NULL_26371, NULL_26561), GWD_SARADOMIN_ROPE_SOUTH(70, "Rope Descent", new WorldPoint(2951, 5267, 0), NULL_26375, NULL_26562), GU_TANOTH_CRUMBLING_WALL(71, "Rocks", new WorldPoint(2545, 3032, 0), CRUMBLING_WALL_40355, ROCKS_40356), @@ -192,14 +201,14 @@ public enum AgilityShortcut SLAYER_TOWER_ADVANCED_CHAIN_SECOND(71, "Spiked Chain (Floor 3)", new WorldPoint(3446, 3576, 0), SPIKEY_CHAIN_16538), STRONGHOLD_SLAYER_CAVE_TUNNEL(72, "Tunnel", new WorldPoint(2431, 9806, 0), TUNNEL_30174, TUNNEL_30175), TROLL_STRONGHOLD_WALL_CLIMB(73, "Rocks", new WorldPoint(2841, 3694, 0), ROCKS_16464), - ARCEUUS_ESSENSE_MINE_WEST(73, "Rock Climb", new WorldPoint(1742, 3853, 0), ROCKS_27984, ROCKS_27985), + ARCEUUS_ESSENSE_MINE_WEST(73, "Rock Climb", new WorldPoint(1742, 3853, 0), ROCKS_27984, ROCKS_27985 ), LAVA_DRAGON_ISLE_JUMP(74, "Stepping Stone", new WorldPoint(3200, 3807, 0), STEPPING_STONE_14918), FORTHOS_DUNGEON_SPIKED_BLADES(75, "Spiked Blades", new WorldPoint(1819, 9946, 0), STRANGE_FLOOR_34834), REVENANT_CAVES_DEMONS_JUMP(75, "Jump", new WorldPoint(3199, 10135, 0), PILLAR_31561), REVENANT_CAVES_ANKOU_EAST(75, "Jump", new WorldPoint(3201, 10195, 0), PILLAR_31561), REVENANT_CAVES_ANKOU_NORTH(75, "Jump", new WorldPoint(3180, 10209, 0), PILLAR_31561), ZUL_ANDRA_ISLAND_CROSSING(76, "Stepping Stone", new WorldPoint(2156, 3073, 0), STEPPING_STONE_10663), - SHILO_VILLAGE_STEPPING_STONES(77, "Stepping Stones", new WorldPoint(2863, 2974, 0), STEPPING_STONE_16466), + SHILO_VILLAGE_STEPPING_STONES( 77, "Stepping Stones", new WorldPoint(2863, 2974, 0), STEPPING_STONE_16466), IORWERTHS_DUNGEON_NORTHERN_SHORTCUT_EAST(78, "Tight Gap", new WorldPoint(3221, 12441, 0), TIGHT_GAP), IORWERTHS_DUNGEON_NORTHERN_SHORTCUT_WEST(78, "Tight Gap", new WorldPoint(3215, 12441, 0), TIGHT_GAP_36693), KHARAZI_JUNGLE_VINE_CLIMB(79, "Vine", new WorldPoint(2897, 2939, 0), NULL_26884, NULL_26886), @@ -265,4 +274,9 @@ public enum AgilityShortcut { return description + " - Level " + level; } + + public boolean matches(TileObject object) + { + return true; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/game/AttackStyle.java b/runelite-client/src/main/java/net/runelite/client/game/AttackStyle.java deleted file mode 100644 index 65e110e59b..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/game/AttackStyle.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2019, ganom - * 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.game; - -import java.awt.Color; -import lombok.AllArgsConstructor; -import lombok.Getter; -import net.runelite.api.Prayer; - -@AllArgsConstructor -@Getter -public enum AttackStyle -{ - MAGE("Mage", Color.CYAN, Prayer.PROTECT_FROM_MAGIC), - RANGE("Range", Color.GREEN, Prayer.PROTECT_FROM_MISSILES), - MELEE("Melee", Color.RED, Prayer.PROTECT_FROM_MELEE), - UNKNOWN("Unknown", Color.WHITE, null); - - private String name; - private Color color; - private Prayer prayer; -} diff --git a/runelite-client/src/main/java/net/runelite/client/game/CombatStats.java b/runelite-client/src/main/java/net/runelite/client/game/CombatStats.java deleted file mode 100644 index 3f940b5e41..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/game/CombatStats.java +++ /dev/null @@ -1,24 +0,0 @@ -package net.runelite.client.game; - -import lombok.Value; - -@Value -public class CombatStats -{ - private int magicAttack; - private int magicDefence; - private int magicStr; - private int meleeAtkCrush; - private int meleeAtkSlash; - private int meleeAtkStab; - private int meleeAttack; - private int meleeDefCrush; - private int meleeDefence; - private int meleeDefSlash; - private int meleeDefStab; - private int meleeStr; - private int rangeAttack; - private int rangeDefence; - private int rangeStr; - private int speed; -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/game/FriendChatManager.java b/runelite-client/src/main/java/net/runelite/client/game/FriendChatManager.java index 53e59422ec..5c24a6ce33 100644 --- a/runelite-client/src/main/java/net/runelite/client/game/FriendChatManager.java +++ b/runelite-client/src/main/java/net/runelite/client/game/FriendChatManager.java @@ -36,18 +36,18 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.inject.Inject; import javax.inject.Singleton; -import net.runelite.api.Client; -import net.runelite.api.FriendsChatManager; import net.runelite.api.FriendsChatMember; +import net.runelite.api.FriendsChatManager; import net.runelite.api.FriendsChatRank; +import net.runelite.api.Client; import net.runelite.api.GameState; import net.runelite.api.IndexedSprite; import net.runelite.api.SpriteID; import net.runelite.api.events.FriendsChatChanged; import net.runelite.api.events.GameStateChanged; -import net.runelite.api.util.Text; -import net.runelite.client.eventbus.EventBus; +import net.runelite.client.eventbus.Subscribe; import net.runelite.client.util.ImageUtil; +import net.runelite.client.util.Text; @Singleton public class FriendChatManager @@ -74,7 +74,7 @@ public class FriendChatManager private final LoadingCache ranksCache = CacheBuilder.newBuilder() .maximumSize(100) .expireAfterWrite(1, TimeUnit.MINUTES) - .build(new CacheLoader<>() + .build(new CacheLoader() { @Override public FriendsChatRank load(@Nonnull String key) @@ -93,15 +93,10 @@ public class FriendChatManager private int offset; @Inject - private FriendChatManager(final Client client, - final SpriteManager spriteManager, - final EventBus eventbus) + private FriendChatManager(Client client, SpriteManager spriteManager) { this.client = client; this.spriteManager = spriteManager; - - eventbus.subscribe(GameStateChanged.class, this, this::onGameStateChanged); - eventbus.subscribe(FriendsChatChanged.class, this, this::onFriendsChatChanged); } public boolean isMember(String name) @@ -131,14 +126,16 @@ public class FriendChatManager return offset + friendsChatRank.ordinal() - 1; } - private void onGameStateChanged(GameStateChanged gameStateChanged) + @Subscribe + public void onGameStateChanged(GameStateChanged gameStateChanged) { - if (gameStateChanged.getGameState() == GameState.LOGGED_IN && offset == 0) + if (gameStateChanged.getGameState() == GameState.LOGIN_SCREEN && offset == 0) { loadRankIcons(); } } + @Subscribe public void onFriendsChatChanged(FriendsChatChanged friendsChatChanged) { ranksCache.invalidateAll(); diff --git a/runelite-client/src/main/java/net/runelite/client/game/HiscoreLoader.java b/runelite-client/src/main/java/net/runelite/client/game/HiscoreLoader.java index 13f8141cc3..36b6024e35 100644 --- a/runelite-client/src/main/java/net/runelite/client/game/HiscoreLoader.java +++ b/runelite-client/src/main/java/net/runelite/client/game/HiscoreLoader.java @@ -36,7 +36,6 @@ import static net.runelite.client.game.HiscoreManager.NONE; import net.runelite.http.api.hiscore.HiscoreClient; import net.runelite.http.api.hiscore.HiscoreEndpoint; import net.runelite.http.api.hiscore.HiscoreResult; -import org.jetbrains.annotations.NotNull; @Slf4j class HiscoreLoader extends CacheLoader @@ -51,7 +50,7 @@ class HiscoreLoader extends CacheLoader itemPrices = Collections.emptyMap(); private Map itemStats = Collections.emptyMap(); private final LoadingCache itemImages; - private final LoadingCache itemDefinitions; + private final LoadingCache itemCompositions; private final LoadingCache itemOutlines; // Worn items with weight reducing property have a different worn and inventory ItemID @@ -164,19 +162,15 @@ public class ItemManager build(); @Inject - public ItemManager( - Client client, - ScheduledExecutorService executor, - ClientThread clientThread, - EventBus eventbus, + public ItemManager(Client client, ScheduledExecutorService scheduledExecutorService, ClientThread clientThread, OkHttpClient okHttpClient) { this.client = client; this.clientThread = clientThread; this.itemClient = new ItemClient(okHttpClient); - executor.scheduleWithFixedDelay(this::loadPrices, 0, 30, TimeUnit.MINUTES); - executor.submit(this::loadStats); + scheduledExecutorService.scheduleWithFixedDelay(this::loadPrices, 0, 30, TimeUnit.MINUTES); + scheduledExecutorService.submit(this::loadStats); itemImages = CacheBuilder.newBuilder() .maximumSize(128L) @@ -184,19 +178,19 @@ public class ItemManager .build(new CacheLoader() { @Override - public AsyncBufferedImage load(@NotNull ImageKey key) + public AsyncBufferedImage load(ImageKey key) throws Exception { return loadImage(key.itemId, key.itemQuantity, key.stackable); } }); - itemDefinitions = CacheBuilder.newBuilder() + itemCompositions = CacheBuilder.newBuilder() .maximumSize(1024L) .expireAfterAccess(1, TimeUnit.HOURS) - .build(new CacheLoader() + .build(new CacheLoader() { @Override - public ItemDefinition load(@NotNull Integer key) + public ItemComposition load(Integer key) throws Exception { return client.getItemDefinition(key); } @@ -208,59 +202,77 @@ public class ItemManager .build(new CacheLoader() { @Override - public BufferedImage load(@NotNull OutlineKey key) + public BufferedImage load(OutlineKey key) throws Exception { return loadItemOutline(key.itemId, key.itemQuantity, key.outlineColor); } }); - - eventbus.subscribe(GameStateChanged.class, this, this::onGameStateChanged); - eventbus.subscribe(PostItemDefinition.class, this, this::onPostItemDefinition); } private void loadPrices() { - itemClient.getPrices() - .subscribeOn(Schedulers.io()) - .subscribe( - m -> itemPrices = m, - e -> log.warn("Error loading prices", e), - () -> log.debug("Loaded {} prices", itemPrices.size()) - ); + try + { + ItemPrice[] prices = itemClient.getPrices(); + if (prices != null) + { + ImmutableMap.Builder map = ImmutableMap.builderWithExpectedSize(prices.length); + for (ItemPrice price : prices) + { + map.put(price.getId(), price); + } + itemPrices = map.build(); + } + + log.debug("Loaded {} prices", itemPrices.size()); + } + catch (IOException e) + { + log.warn("error loading prices!", e); + } } private void loadStats() { - itemClient.getStats() - .subscribeOn(Schedulers.io()) - .subscribe( - m -> itemStats = m, - e -> log.warn("Error fetching stats", e), - () -> log.debug("Loaded {} stats", itemStats.size()) - ); - } - - private void onGameStateChanged(final GameStateChanged event) - { - if (event.getGameState() == GameState.HOPPING || event.getGameState() == GameState.LOGIN_SCREEN) + try { - itemDefinitions.invalidateAll(); + final Map stats = itemClient.getStats(); + if (stats != null) + { + itemStats = ImmutableMap.copyOf(stats); + } + + log.debug("Loaded {} stats", itemStats.size()); + } + catch (IOException e) + { + log.warn("error loading stats!", e); } } - private void onPostItemDefinition(PostItemDefinition event) + + @Subscribe + public void onGameStateChanged(final GameStateChanged event) { - itemDefinitions.put(event.getItemDefinition().getId(), event.getItemDefinition()); + if (event.getGameState() == GameState.HOPPING || event.getGameState() == GameState.LOGIN_SCREEN) + { + itemCompositions.invalidateAll(); + } + } + + @Subscribe + public void onPostItemComposition(PostItemComposition event) + { + itemCompositions.put(event.getItemComposition().getId(), event.getItemComposition()); } /** * Invalidates internal item manager item composition cache (but not client item composition cache) - * - * @see Client#getItemDefinitionCache() + * @see Client#getItemCompositionCache() */ - public void invalidateItemDefinitionCache() + public void invalidateItemCompositionCache() { - itemDefinitions.invalidateAll(); + itemCompositions.invalidateAll(); } /** @@ -280,7 +292,7 @@ public class ItemManager * @param itemID item id * @param ignoreUntradeableMap should the price returned ignore items that are not tradeable for coins in regular way * @return item price - * */ + */ public int getItemPrice(int itemID, boolean ignoreUntradeableMap) { if (itemID == COINS_995) @@ -292,10 +304,10 @@ public class ItemManager return 1000; } - ItemDefinition itemDefinition = getItemDefinition(itemID); - if (itemDefinition.getNote() != -1) + ItemComposition itemComposition = getItemComposition(itemID); + if (itemComposition.getNote() != -1) { - itemID = itemDefinition.getLinkedNoteId(); + itemID = itemComposition.getLinkedNoteId(); } itemID = WORN_ITEMS.getOrDefault(itemID, itemID); @@ -328,67 +340,17 @@ public class ItemManager return price; } - public int getAlchValue(ItemDefinition composition) - { - if (composition.getId() == ItemID.COINS_995) - { - return 1; - } - if (composition.getId() == ItemID.PLATINUM_TOKEN) - { - return 1000; - } - - return Math.max(1, composition.getHaPrice()); - } - - public int getAlchValue(int itemID) - { - if (itemID == ItemID.COINS_995) - { - return 1; - } - if (itemID == ItemID.PLATINUM_TOKEN) - { - return 1000; - } - - return Math.max(1, getItemDefinition(itemID).getHaPrice()); - } - - public int getRepairValue(int itemId) - { - return getRepairValue(itemId, false); - } - - private int getRepairValue(int itemId, boolean fullValue) - { - final ItemReclaimCost b = ItemReclaimCost.of(itemId); - - if (b != null) - { - if (fullValue || b.getItemID() == GRANITE_MAUL_24225 || b.getItemID() == GRANITE_MAUL_24227) - { - return b.getValue(); - } - return (int) (b.getValue() * (75.0f / 100.0f)); - } - - return 0; - } - /** * Look up an item's stats - * * @param itemId item id * @return item stats */ @Nullable public ItemStats getItemStats(int itemId, boolean allowNote) { - ItemDefinition itemDefinition = getItemDefinition(itemId); + ItemComposition itemComposition = getItemComposition(itemId); - if (!allowNote && itemDefinition.getNote() != -1) + if (itemComposition == null || itemComposition.getName() == null || (!allowNote && itemComposition.getNote() != -1)) { return null; } @@ -425,10 +387,10 @@ public class ItemManager * @return item composition */ @Nonnull - public ItemDefinition getItemDefinition(int itemId) + public ItemComposition getItemComposition(int itemId) { - assert client.isClientThread() : "getItemDefinition must be called on client thread"; - return itemDefinitions.getUnchecked(itemId); + assert client.isClientThread() : "getItemComposition must be called on client thread"; + return itemCompositions.getUnchecked(itemId); } /** @@ -436,16 +398,16 @@ public class ItemManager */ public int canonicalize(int itemID) { - ItemDefinition itemDefinition = getItemDefinition(itemID); + ItemComposition itemComposition = getItemComposition(itemID); - if (itemDefinition.getNote() != -1) + if (itemComposition.getNote() != -1) { - return itemDefinition.getLinkedNoteId(); + return itemComposition.getLinkedNoteId(); } - if (itemDefinition.getPlaceholderTemplateId() != -1) + if (itemComposition.getPlaceholderTemplateId() != -1) { - return itemDefinition.getPlaceholderId(); + return itemComposition.getPlaceholderId(); } return WORN_ITEMS.getOrDefault(itemID, itemID); @@ -466,7 +428,7 @@ public class ItemManager { return false; } - Sprite sprite = client.createItemSprite(itemId, quantity, 1, Sprite.DEFAULT_SHADOW_COLOR, + SpritePixels sprite = client.createItemSprite(itemId, quantity, 1, SpritePixels.DEFAULT_SHADOW_COLOR, stackable ? 1 : 0, false, CLIENT_DEFAULT_ZOOM); if (sprite == null) { @@ -520,21 +482,21 @@ public class ItemManager /** * Create item sprite and applies an outline. * - * @param itemId item id + * @param itemId item id * @param itemQuantity item quantity * @param outlineColor outline color * @return image */ private BufferedImage loadItemOutline(final int itemId, final int itemQuantity, final Color outlineColor) { - final Sprite itemSprite = client.createItemSprite(itemId, itemQuantity, 1, 0, 0, false, CLIENT_DEFAULT_ZOOM); + final SpritePixels itemSprite = client.createItemSprite(itemId, itemQuantity, 1, 0, 0, false, CLIENT_DEFAULT_ZOOM); return itemSprite.toBufferedOutline(outlineColor); } /** * Get item outline with a specific color. * - * @param itemId item id + * @param itemId item id * @param itemQuantity item quantity * @param outlineColor outline color * @return image diff --git a/runelite-client/src/main/java/net/runelite/client/game/ItemMapping.java b/runelite-client/src/main/java/net/runelite/client/game/ItemMapping.java index 24155b355f..725fabb239 100644 --- a/runelite-client/src/main/java/net/runelite/client/game/ItemMapping.java +++ b/runelite-client/src/main/java/net/runelite/client/game/ItemMapping.java @@ -352,9 +352,4 @@ public enum ItemMapping return mapping; } - - public static boolean isMapped(int itemId) - { - return MAPPINGS.containsValue(itemId); - } -} \ No newline at end of file +} diff --git a/runelite-client/src/main/java/net/runelite/client/game/ItemReclaimCost.java b/runelite-client/src/main/java/net/runelite/client/game/ItemReclaimCost.java deleted file mode 100644 index 9b6a1b4cbc..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/game/ItemReclaimCost.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (c) 2018, TheStonedTurtle - * 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.game; - -import com.google.common.collect.ImmutableMap; -import javax.annotation.Nullable; -import lombok.AllArgsConstructor; -import lombok.Getter; -import net.runelite.api.ItemID; - -/** - * Some non tradeable items are kept on death inside low level wilderness (1-20) but are turned into a broken variant. - *

- * The non-broken variant will be shown inside the interface. - */ -@AllArgsConstructor -@Getter -public enum ItemReclaimCost -{ - // Capes - FIRE_CAPE(ItemID.FIRE_CAPE, 50000), - FIRE_MAX_CAPE(ItemID.FIRE_MAX_CAPE, 99000), - INFERNAL_CAPE(ItemID.INFERNAL_CAPE, 50000), - INFERNAL_MAX_CAPE(ItemID.INFERNAL_MAX_CAPE, 99000), - AVAS_ASSEMBLER(ItemID.AVAS_ASSEMBLER, 75000), - ASSEMBLER_MAX_CAPE(ItemID.ASSEMBLER_MAX_CAPE, 99000), - IMBUED_GUTHIX_CAPE(ItemID.IMBUED_GUTHIX_CAPE, 75000), - IMBUED_GUTHIX_MAX_CAPE(ItemID.GUTHIX_MAX_CAPE, 99000), - IMBUED_SARADOMIN_CAPE(ItemID.IMBUED_SARADOMIN_CAPE, 75000), - IMBUED_SARADOMIN_MAX_CAPE(ItemID.SARADOMIN_MAX_CAPE, 99000), - IMBUED_ZAMORAK_CAPE(ItemID.IMBUED_ZAMORAK_CAPE, 75000), - IMBUED_ZAMORAK_MAX_CAPE(ItemID.ZAMORAK_MAX_CAPE, 99000), - - // Defenders - BRONZE_DEFENDER(ItemID.BRONZE_DEFENDER, 1000), - IRON_DEFENDER(ItemID.IRON_DEFENDER, 2000), - STEEL_DEFENDER(ItemID.STEEL_DEFENDER, 2500), - BLACK_DEFENDER(ItemID.BLACK_DEFENDER, 5000), - MITHRIL_DEFENDER(ItemID.MITHRIL_DEFENDER, 15000), - ADAMANT_DEFENDER(ItemID.ADAMANT_DEFENDER, 25000), - RUNE_DEFENDER(ItemID.RUNE_DEFENDER, 35000), - DRAGON_DEFENDER(ItemID.DRAGON_DEFENDER, 40000), - AVERNIC_DEFENDER(ItemID.AVERNIC_DEFENDER, 1000000), - - // Void - VOID_MAGE_HELM(ItemID.VOID_MAGE_HELM, 40000), - VOID_RANGER_HELM(ItemID.VOID_RANGER_HELM, 40000), - VOID_MELEE_HELM(ItemID.VOID_MELEE_HELM, 40000), - VOID_KNIGHT_TOP(ItemID.VOID_KNIGHT_TOP, 45000), - VOID_KNIGHT_ROBE(ItemID.VOID_KNIGHT_ROBE, 45000), - VOID_KNIGHT_GLOVES(ItemID.VOID_KNIGHT_GLOVES, 30000), - ELITE_VOID_TOP(ItemID.ELITE_VOID_TOP, 50000), - ELITE_VOID_ROBE(ItemID.ELITE_VOID_ROBE, 50000), - - // Barb Assault - FIGHTER_HAT(ItemID.FIGHTER_HAT, 45000), - RANGER_HAT(ItemID.RANGER_HAT, 45000), - HEALER_HAT(ItemID.HEALER_HAT, 45000), - FIGHTER_TORSO(ItemID.FIGHTER_TORSO, 50000), - PENANCE_SKIRT(ItemID.PENANCE_SKIRT, 20000), - - // Castle Wars - SARADOMIN_HALO(ItemID.SARADOMIN_HALO, 25000), - ZAMORAK_HALO(ItemID.ZAMORAK_HALO, 25000), - GUTHIX_HALO(ItemID.GUTHIX_HALO, 25000), - DECORATIVE_MAGIC_HAT(ItemID.DECORATIVE_ARMOUR_11898, 5000), - DECORATIVE_MAGIC_ROBE_TOP(ItemID.DECORATIVE_ARMOUR_11896, 5000), - DECORATIVE_MAGIC_ROBE_LEGS(ItemID.DECORATIVE_ARMOUR_11897, 5000), - DECORATIVE_RANGE_TOP(ItemID.DECORATIVE_ARMOUR_11899, 5000), - DECORATIVE_RANGE_BOTTOM(ItemID.DECORATIVE_ARMOUR_11900, 5000), - DECORATIVE_RANGE_QUIVER(ItemID.DECORATIVE_ARMOUR_11901, 5000), - GOLD_DECORATIVE_HELM(ItemID.DECORATIVE_HELM_4511, 5000), - GOLD_DECORATIVE_BODY(ItemID.DECORATIVE_ARMOUR_4509, 5000), - GOLD_DECORATIVE_LEGS(ItemID.DECORATIVE_ARMOUR_4510, 5000), - GOLD_DECORATIVE_SKIRT(ItemID.DECORATIVE_ARMOUR_11895, 5000), - GOLD_DECORATIVE_SHIELD(ItemID.DECORATIVE_SHIELD_4512, 5000), - GOLD_DECORATIVE_SWORD(ItemID.DECORATIVE_SWORD_4508, 5000), - - // Granite Maul - GRANITE_MAUL(ItemID.GRANITE_MAUL_24225, 375000), - GRANITE_MAUL_OR(ItemID.GRANITE_MAUL_24227, 375000); - - private static final ImmutableMap idMap; - - static - { - ImmutableMap.Builder builder = ImmutableMap.builder(); - - for (ItemReclaimCost items : values()) - { - builder.put(items.itemID, items); - } - - idMap = builder.build(); - } - - private final int itemID; - private final int value; - - @Nullable - public static ItemReclaimCost of(int itemId) - { - return idMap.get(itemId); - } - - public static boolean breaksOnDeath(int itemId) - { - return idMap.containsKey(itemId); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/game/ItemVariationMapping.java b/runelite-client/src/main/java/net/runelite/client/game/ItemVariationMapping.java index 84b09b296b..5daec1aee9 100644 --- a/runelite-client/src/main/java/net/runelite/client/game/ItemVariationMapping.java +++ b/runelite-client/src/main/java/net/runelite/client/game/ItemVariationMapping.java @@ -98,9 +98,4 @@ public class ItemVariationMapping { return INVERTED_MAPPINGS.asMap().getOrDefault(itemId, Collections.singletonList(itemId)); } - - static int getSize() - { - return MAPPINGS.size(); - } } diff --git a/runelite-client/src/main/java/net/runelite/client/game/LootManager.java b/runelite-client/src/main/java/net/runelite/client/game/LootManager.java index 61e23e2c9e..e44c5d7572 100644 --- a/runelite-client/src/main/java/net/runelite/client/game/LootManager.java +++ b/runelite-client/src/main/java/net/runelite/client/game/LootManager.java @@ -1,449 +1,428 @@ -/* - * Copyright (c) 2018, Adam - * 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.game; - -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ListMultimap; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import javax.inject.Inject; -import javax.inject.Singleton; -import lombok.extern.slf4j.Slf4j; -import net.runelite.api.AnimationID; -import net.runelite.api.Client; -import net.runelite.api.ItemID; -import net.runelite.api.NPC; -import net.runelite.api.NPCDefinition; -import net.runelite.api.NpcID; -import net.runelite.api.Player; -import net.runelite.api.Tile; -import net.runelite.api.TileItem; -import net.runelite.api.coords.LocalPoint; -import net.runelite.api.coords.WorldPoint; -import net.runelite.api.events.AnimationChanged; -import net.runelite.api.events.GameTick; -import net.runelite.api.events.ItemDespawned; -import net.runelite.api.events.ItemQuantityChanged; -import net.runelite.api.events.ItemSpawned; -import net.runelite.api.events.NpcDespawned; -import net.runelite.api.events.PlayerDespawned; -import net.runelite.api.events.NpcDefinitionChanged; -import net.runelite.client.eventbus.EventBus; -import net.runelite.client.events.NpcLootReceived; -import net.runelite.client.events.PlayerLootReceived; - -@Singleton -@Slf4j -public class LootManager -{ - private static final Map NPC_DEATH_ANIMATIONS = ImmutableMap.builder() - .put(NpcID.CAVE_KRAKEN, AnimationID.CAVE_KRAKEN_DEATH) - .put(NpcID.CRYSTALLINE_BAT, AnimationID.CRYSTALLINE_BAT_DEATH) - .put(NpcID.CRYSTALLINE_RAT, AnimationID.CRYSTALLINE_RAT_DEATH) - .put(NpcID.CRYSTALLINE_SPIDER, AnimationID.CRYSTALLINE_SPIDER_DEATH) - .put(NpcID.CRYSTALLINE_WOLF, AnimationID.CRYSTALLINE_WOLF_DEATH) - .put(NpcID.CRYSTALLINE_UNICORN, AnimationID.CRYSTALLINE_UNICORN_DEATH) - .put(NpcID.CRYSTALLINE_SCORPION, AnimationID.CORRUPTED_SCORPION_DEATH) - .put(NpcID.CRYSTALLINE_DRAGON, AnimationID.CRYSTALLINE_DRAGON_DEATH) - .put(NpcID.CRYSTALLINE_DARK_BEAST, AnimationID.CRYSTALLINE_DARK_BEAST_DEATH) - .put(NpcID.CRYSTALLINE_BEAR, AnimationID.CRYSTALLINE_BEAR_DEATH) - .put(NpcID.CORRUPTED_BAT, AnimationID.CRYSTALLINE_BAT_DEATH) - .put(NpcID.CORRUPTED_RAT, AnimationID.CRYSTALLINE_RAT_DEATH) - .put(NpcID.CORRUPTED_SPIDER, AnimationID.CRYSTALLINE_SPIDER_DEATH) - .put(NpcID.CORRUPTED_WOLF, AnimationID.CRYSTALLINE_WOLF_DEATH) - .put(NpcID.CORRUPTED_UNICORN, AnimationID.CRYSTALLINE_UNICORN_DEATH) - .put(NpcID.CORRUPTED_SCORPION, AnimationID.CORRUPTED_SCORPION_DEATH) - .put(NpcID.CORRUPTED_DRAGON, AnimationID.CRYSTALLINE_DRAGON_DEATH) - .put(NpcID.CORRUPTED_DARK_BEAST, AnimationID.CRYSTALLINE_DARK_BEAST_DEATH) - .put(NpcID.CORRUPTED_BEAR, AnimationID.CRYSTALLINE_BEAR_DEATH) - .put(NpcID.THE_NIGHTMARE_9433, AnimationID.NIGHTMARE_DEATH) - .build(); - - private final EventBus eventBus; - private final Client client; - private final ListMultimap itemSpawns = ArrayListMultimap.create(); - private final Set killPoints = new HashSet<>(); - private WorldPoint playerLocationLastTick; - private WorldPoint krakenPlayerLocation; - - private NPC delayedLootNpc; - private int delayedLootTickLimit; - - @Inject - private LootManager( - final EventBus eventBus, - final Client client - ) - { - this.eventBus = eventBus; - this.client = client; - - eventBus.subscribe(GameTick.class, this, this::onGameTick); - eventBus.subscribe(NpcDespawned.class, this, this::onNpcDespawned); - eventBus.subscribe(PlayerDespawned.class, this, this::onPlayerDespawned); - eventBus.subscribe(ItemSpawned.class, this, this::onItemSpawned); - eventBus.subscribe(ItemDespawned.class, this, this::onItemDespawned); - eventBus.subscribe(ItemQuantityChanged.class, this, this::onItemQuantityChanged); - eventBus.subscribe(AnimationChanged.class, this, this::onAnimationChanged); - eventBus.subscribe(NpcDefinitionChanged.class, this, this::onNpcDefinitionChanged); - } - - private void onNpcDespawned(NpcDespawned npcDespawned) - { - final NPC npc = npcDespawned.getNpc(); - - if (npc == delayedLootNpc) - { - delayedLootNpc = null; - delayedLootTickLimit = 0; - } - - if (!npc.isDead()) - { - int id = npc.getId(); - switch (id) - { - case NpcID.GARGOYLE: - case NpcID.GARGOYLE_413: - case NpcID.GARGOYLE_1543: - case NpcID.MARBLE_GARGOYLE: - case NpcID.MARBLE_GARGOYLE_7408: - case NpcID.DUSK_7888: - case NpcID.DUSK_7889: - - case NpcID.ROCKSLUG: - case NpcID.ROCKSLUG_422: - case NpcID.GIANT_ROCKSLUG: - - case NpcID.SMALL_LIZARD: - case NpcID.SMALL_LIZARD_463: - case NpcID.DESERT_LIZARD: - case NpcID.DESERT_LIZARD_460: - case NpcID.DESERT_LIZARD_461: - case NpcID.LIZARD: - - case NpcID.ZYGOMITE: - case NpcID.ZYGOMITE_1024: - case NpcID.ANCIENT_ZYGOMITE: - - // these monsters die with >0 hp, so we just look for coincident - // item spawn with despawn - break; - default: - return; - } - } - - processNpcLoot(npc); - } - - private void onPlayerDespawned(PlayerDespawned playerDespawned) - { - final Player player = playerDespawned.getPlayer(); - // Only care about dead Players - if (player.getHealthRatio() != 0) - { - return; - } - - final LocalPoint location = LocalPoint.fromWorld(client, player.getWorldLocation()); - if (location == null || killPoints.contains(location)) - { - return; - } - - final int x = location.getSceneX(); - final int y = location.getSceneY(); - final int packed = x << 8 | y; - final Collection items = itemSpawns.get(packed); - - if (items.isEmpty()) - { - return; - } - - killPoints.add(location); - eventBus.post(PlayerLootReceived.class, new PlayerLootReceived(player, items)); - } - - private void onItemSpawned(ItemSpawned itemSpawned) - { - final TileItem item = itemSpawned.getItem(); - final Tile tile = itemSpawned.getTile(); - final LocalPoint location = tile.getLocalLocation(); - final int packed = location.getSceneX() << 8 | location.getSceneY(); - itemSpawns.put(packed, new ItemStack(item.getId(), item.getQuantity(), location)); - log.debug("Item spawn {} ({}) location {}", item.getId(), item.getQuantity(), location); - } - - private void onItemDespawned(ItemDespawned itemDespawned) - { - final TileItem item = itemDespawned.getItem(); - final LocalPoint location = itemDespawned.getTile().getLocalLocation(); - log.debug("Item despawn {} ({}) location {}", item.getId(), item.getQuantity(), location); - } - - private void onItemQuantityChanged(ItemQuantityChanged itemQuantityChanged) - { - final TileItem item = itemQuantityChanged.getItem(); - final Tile tile = itemQuantityChanged.getTile(); - final LocalPoint location = tile.getLocalLocation(); - final int packed = location.getSceneX() << 8 | location.getSceneY(); - final int diff = itemQuantityChanged.getNewQuantity() - itemQuantityChanged.getOldQuantity(); - - if (diff <= 0) - { - return; - } - - itemSpawns.put(packed, new ItemStack(item.getId(), diff, location)); - } - - private void onAnimationChanged(AnimationChanged e) - { - if (!(e.getActor() instanceof NPC)) - { - return; - } - - final NPC npc = (NPC) e.getActor(); - int id = npc.getId(); - - // We only care about certain NPCs - final Integer deathAnim = NPC_DEATH_ANIMATIONS.get(id); - - // Current animation is death animation? - if (deathAnim != null && deathAnim == npc.getAnimation()) - { - if (id == NpcID.CAVE_KRAKEN) - { - // Big Kraken drops loot wherever player is standing when animation starts. - krakenPlayerLocation = client.getLocalPlayer().getWorldLocation(); - } - else - { - // These NPCs drop loot on death animation, which is right now. - processNpcLoot(npc); - } - } - } - - public void onNpcDefinitionChanged(NpcDefinitionChanged npcChanged) - { - final NPC npc = npcChanged.getNpc(); - if (npc.getId() == NpcID.THE_NIGHTMARE_9433) - { - delayedLootNpc = npc; - delayedLootTickLimit = 15; - } - } - - private void onGameTick(GameTick gameTick) - { - if (delayedLootNpc != null && delayedLootTickLimit-- > 0) - { - processDelayedLoot(); - } - - playerLocationLastTick = client.getLocalPlayer().getWorldLocation(); - itemSpawns.clear(); - killPoints.clear(); - } - - private void processDelayedLoot() - { - final WorldPoint adjacentLootTile = getAdjacentSquareLootTile(delayedLootNpc); - final LocalPoint localPoint = LocalPoint.fromWorld(client, adjacentLootTile); - - if (localPoint == null) - { - log.debug("Scene changed away from delayed loot location"); - delayedLootNpc = null; - delayedLootTickLimit = 0; - return; - } - - final int sceneX = localPoint.getSceneX(); - final int sceneY = localPoint.getSceneY(); - final int packed = sceneX << 8 | sceneY; - final List itemStacks = itemSpawns.get(packed); - if (itemStacks.isEmpty()) - { - // no loot yet - return; - } - - log.debug("Got delayed loot stack from {}: {}", delayedLootNpc.getName(), itemStacks); - eventBus.post(NpcLootReceived.class, new NpcLootReceived(delayedLootNpc, itemStacks)); - - delayedLootNpc = null; - delayedLootTickLimit = 0; - } - - private void processNpcLoot(NPC npc) - { - final LocalPoint location = LocalPoint.fromWorld(client, getDropLocation(npc, npc.getWorldLocation())); - if (location == null || killPoints.contains(location)) - { - return; - } - - final int x = location.getSceneX(); - final int y = location.getSceneY(); - final int size = npc.getDefinition().getSize(); - - // Some NPCs drop items onto multiple tiles - final List allItems = new ArrayList<>(); - for (int i = 0; i < size; ++i) - { - for (int j = 0; j < size; ++j) - { - final int packed = (x + i) << 8 | (y + j); - final Collection items = itemSpawns.get(packed); - allItems.addAll(items); - } - } - - if (allItems.isEmpty()) - { - return; - } - - killPoints.add(location); - eventBus.post(NpcLootReceived.class, new NpcLootReceived(npc, allItems)); - } - - private WorldPoint getDropLocation(NPC npc, WorldPoint worldLocation) - { - switch (npc.getId()) - { - case NpcID.KRAKEN: - case NpcID.KRAKEN_6640: - case NpcID.KRAKEN_6656: - worldLocation = playerLocationLastTick; - break; - case NpcID.CAVE_KRAKEN: - worldLocation = krakenPlayerLocation; - break; - case NpcID.ZULRAH: // Green - case NpcID.ZULRAH_2043: // Red - case NpcID.ZULRAH_2044: // Blue - for (Map.Entry entry : itemSpawns.entries()) - { - if (entry.getValue().getId() == ItemID.ZULRAHS_SCALES) - { - int packed = entry.getKey(); - int unpackedX = packed >> 8; - int unpackedY = packed & 0xFF; - worldLocation = WorldPoint.fromScene(client, unpackedX, unpackedY, worldLocation.getPlane()); - break; - } - } - break; - case NpcID.VORKATH: - case NpcID.VORKATH_8058: - case NpcID.VORKATH_8059: - case NpcID.VORKATH_8060: - case NpcID.VORKATH_8061: - int x = worldLocation.getX() + 3; - int y = worldLocation.getY() + 3; - if (playerLocationLastTick.getX() < x) - { - x -= 4; - } - else if (playerLocationLastTick.getX() > x) - { - x += 4; - } - if (playerLocationLastTick.getY() < y) - { - y -= 4; - } - else if (playerLocationLastTick.getY() > y) - { - y += 4; - } - worldLocation = new WorldPoint(x, y, worldLocation.getPlane()); - break; - } - - return worldLocation; - } - - private WorldPoint getAdjacentSquareLootTile(NPC npc) - { - final NPCDefinition composition = npc.getDefinition(); - final WorldPoint worldLocation = npc.getWorldLocation(); - int x = worldLocation.getX(); - int y = worldLocation.getY(); - - if (playerLocationLastTick.getX() < x) - { - x -= 1; - } - else - { - x += Math.min(playerLocationLastTick.getX() - x, composition.getSize()); - } - - if (playerLocationLastTick.getY() < y) - { - y -= 1; - } - else - { - y += Math.min(playerLocationLastTick.getY() - y, composition.getSize()); - } - - return new WorldPoint(x, y, worldLocation.getPlane()); - } - - /** - * Get the list of items present at the provided WorldPoint that spawned this tick. - * - * @param worldPoint the location in question - * @return the list of item stacks - */ - public Collection getItemSpawns(WorldPoint worldPoint) - { - LocalPoint localPoint = LocalPoint.fromWorld(client, worldPoint); - if (localPoint == null) - { - return Collections.emptyList(); - } - - final int sceneX = localPoint.getSceneX(); - final int sceneY = localPoint.getSceneY(); - final int packed = sceneX << 8 | sceneY; - final List itemStacks = itemSpawns.get(packed); - return Collections.unmodifiableList(itemStacks); - } -} \ No newline at end of file +/* + * Copyright (c) 2018, Adam + * 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.game; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ListMultimap; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.inject.Inject; +import javax.inject.Singleton; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.AnimationID; +import net.runelite.api.Client; +import net.runelite.api.ItemID; +import net.runelite.api.NPC; +import net.runelite.api.NPCComposition; +import net.runelite.api.NpcID; +import net.runelite.api.Player; +import net.runelite.api.Tile; +import net.runelite.api.TileItem; +import net.runelite.api.coords.LocalPoint; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.AnimationChanged; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.ItemDespawned; +import net.runelite.api.events.ItemQuantityChanged; +import net.runelite.api.events.ItemSpawned; +import net.runelite.api.events.NpcChanged; +import net.runelite.api.events.NpcDespawned; +import net.runelite.api.events.PlayerDespawned; +import net.runelite.client.eventbus.EventBus; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.NpcLootReceived; +import net.runelite.client.events.PlayerLootReceived; + +@Singleton +@Slf4j +public class LootManager +{ + private static final Map NPC_DEATH_ANIMATIONS = ImmutableMap.of( + NpcID.CAVE_KRAKEN, AnimationID.CAVE_KRAKEN_DEATH + ); + + private final EventBus eventBus; + private final Client client; + private final ListMultimap itemSpawns = ArrayListMultimap.create(); + private final Set killPoints = new HashSet<>(); + private WorldPoint playerLocationLastTick; + private WorldPoint krakenPlayerLocation; + + private NPC delayedLootNpc; + private int delayedLootTickLimit; + + @Inject + private LootManager(EventBus eventBus, Client client) + { + this.eventBus = eventBus; + this.client = client; + } + + @Subscribe + public void onNpcDespawned(NpcDespawned npcDespawned) + { + final NPC npc = npcDespawned.getNpc(); + + if (npc == delayedLootNpc) + { + delayedLootNpc = null; + delayedLootTickLimit = 0; + } + + if (!npc.isDead()) + { + int id = npc.getId(); + switch (id) + { + case NpcID.GARGOYLE: + case NpcID.GARGOYLE_413: + case NpcID.GARGOYLE_1543: + case NpcID.MARBLE_GARGOYLE: + case NpcID.MARBLE_GARGOYLE_7408: + case NpcID.DUSK_7888: + case NpcID.DUSK_7889: + + case NpcID.ROCKSLUG: + case NpcID.ROCKSLUG_422: + case NpcID.GIANT_ROCKSLUG: + + case NpcID.SMALL_LIZARD: + case NpcID.SMALL_LIZARD_463: + case NpcID.DESERT_LIZARD: + case NpcID.DESERT_LIZARD_460: + case NpcID.DESERT_LIZARD_461: + case NpcID.LIZARD: + + case NpcID.ZYGOMITE: + case NpcID.ZYGOMITE_1024: + case NpcID.ANCIENT_ZYGOMITE: + + // these monsters die with >0 hp, so we just look for coincident + // item spawn with despawn + break; + default: + return; + } + } + + processNpcLoot(npc); + } + + @Subscribe + public void onPlayerDespawned(PlayerDespawned playerDespawned) + { + final Player player = playerDespawned.getPlayer(); + // Only care about dead Players + if (player.getHealthRatio() != 0) + { + return; + } + + final LocalPoint location = LocalPoint.fromWorld(client, player.getWorldLocation()); + if (location == null || killPoints.contains(location)) + { + return; + } + + final int x = location.getSceneX(); + final int y = location.getSceneY(); + final int packed = x << 8 | y; + final Collection items = itemSpawns.get(packed); + + if (items.isEmpty()) + { + return; + } + + killPoints.add(location); + eventBus.post(new PlayerLootReceived(player, items)); + } + + @Subscribe + public void onItemSpawned(ItemSpawned itemSpawned) + { + final TileItem item = itemSpawned.getItem(); + final Tile tile = itemSpawned.getTile(); + final LocalPoint location = tile.getLocalLocation(); + final int packed = location.getSceneX() << 8 | location.getSceneY(); + itemSpawns.put(packed, new ItemStack(item.getId(), item.getQuantity(), location)); + log.debug("Item spawn {} ({}) location {}", item.getId(), item.getQuantity(), location); + } + + @Subscribe + public void onItemDespawned(ItemDespawned itemDespawned) + { + final TileItem item = itemDespawned.getItem(); + final LocalPoint location = itemDespawned.getTile().getLocalLocation(); + log.debug("Item despawn {} ({}) location {}", item.getId(), item.getQuantity(), location); + } + + @Subscribe + public void onItemQuantityChanged(ItemQuantityChanged itemQuantityChanged) + { + final TileItem item = itemQuantityChanged.getItem(); + final Tile tile = itemQuantityChanged.getTile(); + final LocalPoint location = tile.getLocalLocation(); + final int packed = location.getSceneX() << 8 | location.getSceneY(); + final int diff = itemQuantityChanged.getNewQuantity() - itemQuantityChanged.getOldQuantity(); + + if (diff <= 0) + { + return; + } + + itemSpawns.put(packed, new ItemStack(item.getId(), diff, location)); + } + + @Subscribe + public void onAnimationChanged(AnimationChanged e) + { + if (!(e.getActor() instanceof NPC)) + { + return; + } + + final NPC npc = (NPC) e.getActor(); + int id = npc.getId(); + + // We only care about certain NPCs + final Integer deathAnim = NPC_DEATH_ANIMATIONS.get(id); + + // Current animation is death animation? + if (deathAnim != null && deathAnim == npc.getAnimation()) + { + if (id == NpcID.CAVE_KRAKEN) + { + // Big Kraken drops loot wherever player is standing when animation starts. + krakenPlayerLocation = client.getLocalPlayer().getWorldLocation(); + } + else + { + // These NPCs drop loot on death animation, which is right now. + processNpcLoot(npc); + } + } + } + + @Subscribe + public void onNpcChanged(NpcChanged npcChanged) + { + final NPC npc = npcChanged.getNpc(); + if (npc.getId() == NpcID.THE_NIGHTMARE_9433) + { + delayedLootNpc = npc; + delayedLootTickLimit = 15; + } + } + + @Subscribe + public void onGameTick(GameTick gameTick) + { + if (delayedLootNpc != null && delayedLootTickLimit-- > 0) + { + processDelayedLoot(); + } + + playerLocationLastTick = client.getLocalPlayer().getWorldLocation(); + + itemSpawns.clear(); + killPoints.clear(); + } + + private void processDelayedLoot() + { + final WorldPoint adjacentLootTile = getAdjacentSquareLootTile(delayedLootNpc); + final LocalPoint localPoint = LocalPoint.fromWorld(client, adjacentLootTile); + + if (localPoint == null) + { + log.debug("Scene changed away from delayed loot location"); + delayedLootNpc = null; + delayedLootTickLimit = 0; + return; + } + + final int sceneX = localPoint.getSceneX(); + final int sceneY = localPoint.getSceneY(); + final int packed = sceneX << 8 | sceneY; + final List itemStacks = itemSpawns.get(packed); + if (itemStacks.isEmpty()) + { + // no loot yet + return; + } + + log.debug("Got delayed loot stack from {}: {}", delayedLootNpc.getName(), itemStacks); + eventBus.post(new NpcLootReceived(delayedLootNpc, itemStacks)); + + delayedLootNpc = null; + delayedLootTickLimit = 0; + } + + private void processNpcLoot(NPC npc) + { + final LocalPoint location = LocalPoint.fromWorld(client, getDropLocation(npc, npc.getWorldLocation())); + if (location == null || killPoints.contains(location)) + { + return; + } + + final int x = location.getSceneX(); + final int y = location.getSceneY(); + final int size = npc.getComposition().getSize(); + + // Some NPCs drop items onto multiple tiles + final List allItems = new ArrayList<>(); + for (int i = 0; i < size; ++i) + { + for (int j = 0; j < size; ++j) + { + final int packed = (x + i) << 8 | (y + j); + final Collection items = itemSpawns.get(packed); + allItems.addAll(items); + } + } + + if (allItems.isEmpty()) + { + return; + } + + killPoints.add(location); + eventBus.post(new NpcLootReceived(npc, allItems)); + } + + private WorldPoint getDropLocation(NPC npc, WorldPoint worldLocation) + { + switch (npc.getId()) + { + case NpcID.KRAKEN: + case NpcID.KRAKEN_6640: + case NpcID.KRAKEN_6656: + worldLocation = playerLocationLastTick; + break; + case NpcID.CAVE_KRAKEN: + worldLocation = krakenPlayerLocation; + break; + case NpcID.ZULRAH: // Green + case NpcID.ZULRAH_2043: // Red + case NpcID.ZULRAH_2044: // Blue + for (Map.Entry entry : itemSpawns.entries()) + { + if (entry.getValue().getId() == ItemID.ZULRAHS_SCALES) + { + int packed = entry.getKey(); + int unpackedX = packed >> 8; + int unpackedY = packed & 0xFF; + worldLocation = WorldPoint.fromScene(client, unpackedX, unpackedY, worldLocation.getPlane()); + break; + } + } + break; + case NpcID.VORKATH: + case NpcID.VORKATH_8058: + case NpcID.VORKATH_8059: + case NpcID.VORKATH_8060: + case NpcID.VORKATH_8061: + int x = worldLocation.getX() + 3; + int y = worldLocation.getY() + 3; + if (playerLocationLastTick.getX() < x) + { + x -= 4; + } + else if (playerLocationLastTick.getX() > x) + { + x += 4; + } + if (playerLocationLastTick.getY() < y) + { + y -= 4; + } + else if (playerLocationLastTick.getY() > y) + { + y += 4; + } + worldLocation = new WorldPoint(x, y, worldLocation.getPlane()); + break; + } + + return worldLocation; + } + + private WorldPoint getAdjacentSquareLootTile(NPC npc) + { + final NPCComposition composition = npc.getComposition(); + final WorldPoint worldLocation = npc.getWorldLocation(); + int x = worldLocation.getX(); + int y = worldLocation.getY(); + + if (playerLocationLastTick.getX() < x) + { + x -= 1; + } + else + { + x += Math.min(playerLocationLastTick.getX() - x, composition.getSize()); + } + + if (playerLocationLastTick.getY() < y) + { + y -= 1; + } + else + { + y += Math.min(playerLocationLastTick.getY() - y, composition.getSize()); + } + + return new WorldPoint(x, y, worldLocation.getPlane()); + } + + /** + * Get the list of items present at the provided WorldPoint that spawned this tick. + * + * @param worldPoint the location in question + * @return the list of item stacks + */ + public Collection getItemSpawns(WorldPoint worldPoint) + { + LocalPoint localPoint = LocalPoint.fromWorld(client, worldPoint); + if (localPoint == null) + { + return Collections.emptyList(); + } + + final int sceneX = localPoint.getSceneX(); + final int sceneY = localPoint.getSceneY(); + final int packed = sceneX << 8 | sceneY; + final List itemStacks = itemSpawns.get(packed); + return Collections.unmodifiableList(itemStacks); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/game/NPCManager.java b/runelite-client/src/main/java/net/runelite/client/game/NPCManager.java index 95a8c11929..1a51afea3f 100644 --- a/runelite-client/src/main/java/net/runelite/client/game/NPCManager.java +++ b/runelite-client/src/main/java/net/runelite/client/game/NPCManager.java @@ -1,6 +1,5 @@ /* * Copyright (c) 2018, Adam - * Copyright (c) 2019, TheStonedTurtle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,136 +24,54 @@ */ package net.runelite.client.game; -import com.google.common.collect.ImmutableMap; -import com.google.gson.stream.JsonReader; -import io.reactivex.rxjava3.core.Completable; -import io.reactivex.rxjava3.schedulers.Schedulers; import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.util.Set; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.ScheduledExecutorService; import javax.annotation.Nullable; import javax.inject.Inject; import javax.inject.Singleton; import lombok.extern.slf4j.Slf4j; -import net.runelite.api.NpcID; +import net.runelite.http.api.npc.NpcInfo; +import net.runelite.http.api.npc.NpcInfoClient; +import okhttp3.OkHttpClient; -@Slf4j @Singleton +@Slf4j public class NPCManager { - private static final Set blacklistXpMultiplier = Set.of( - // Vorkath - NpcID.VORKATH, NpcID.VORKATH_8058, NpcID.VORKATH_8059, NpcID.VORKATH_8060, NpcID.VORKATH_8061, - - // Grotesque Guardians - NpcID.DAWN, NpcID.DAWN_7852, NpcID.DAWN_7853, NpcID.DAWN_7884, NpcID.DAWN_7885, - NpcID.DUSK, NpcID.DUSK_7851, NpcID.DUSK_7854, NpcID.DUSK_7855, NpcID.DUSK_7882, NpcID.DUSK_7883, NpcID.DUSK_7886, NpcID.DUSK_7887, NpcID.DUSK_7888, NpcID.DUSK_7889, - - // Kraken - NpcID.KRAKEN, NpcID.KRAKEN_6640, NpcID.KRAKEN_6656, - - // Zulrah - NpcID.ZULRAH, NpcID.ZULRAH_2043, NpcID.ZULRAH_2044 - ); - private ImmutableMap statsMap; + private final OkHttpClient okHttpClient; + private Map npcMap = Collections.emptyMap(); @Inject - private NPCManager() + private NPCManager(OkHttpClient okHttpClient, ScheduledExecutorService scheduledExecutorService) { - Completable.fromAction(this::loadStats) - .subscribeOn(Schedulers.computation()) - .subscribe( - () -> log.debug("Loaded {} NPC stats", statsMap.size()), - ex -> log.warn("Error loading NPC stats", ex) - ); + this.okHttpClient = okHttpClient; + scheduledExecutorService.execute(this::loadNpcs); } - private void loadStats() throws IOException - { - try (JsonReader reader = new JsonReader(new InputStreamReader(NPCManager.class.getResourceAsStream("/npc_stats.json"), StandardCharsets.UTF_8))) - { - ImmutableMap.Builder builder = ImmutableMap.builderWithExpectedSize(2821); - reader.beginObject(); - - while (reader.hasNext()) - { - builder.put( - Integer.parseInt(reader.nextName()), - NPCStats.NPC_STATS_TYPE_ADAPTER.read(reader) - ); - } - - reader.endObject(); - statsMap = builder.build(); - } - } - - /** - * Returns the {@link NPCStats} for target NPC id - * - * @param npcId NPC id - * @return the {@link NPCStats} or null if unknown - */ @Nullable - public NPCStats getStats(final int npcId) + public NpcInfo getNpcInfo(int npcId) { - return statsMap.get(npcId); + return npcMap.get(npcId); } - /** - * Returns health for target NPC ID - * - * @param npcId NPC id - * @return health or null if unknown - */ - public int getHealth(final int npcId) + @Nullable + public Integer getHealth(int npcId) { - final NPCStats s = statsMap.get(npcId); - if (s == null || s.getHitpoints() == -1) - { - return -1; - } - - return s.getHitpoints(); + NpcInfo npcInfo = npcMap.get(npcId); + return npcInfo == null ? null : npcInfo.getHitpoints(); } - /** - * Returns the attack speed for target NPC ID. - * - * @param npcId NPC id - * @return attack speed in game ticks for NPC ID. - */ - public int getAttackSpeed(final int npcId) + private void loadNpcs() { - final NPCStats s = statsMap.get(npcId); - if (s == null || s.getAttackSpeed() == -1) + try { - return -1; + npcMap = new NpcInfoClient(okHttpClient).getNpcs(); } - - return s.getAttackSpeed(); - } - - /** - * Returns the exp modifier for target NPC ID based on its stats. - * - * @param npcId NPC id - * @return npcs exp modifier. Assumes default xp rate if npc stats are unknown (returns 1) - */ - public double getXpModifier(final int npcId) - { - if (blacklistXpMultiplier.contains(npcId)) + catch (IOException e) { - return 1; + log.warn("error loading npc stats", e); } - - final NPCStats s = statsMap.get(npcId); - if (s == null) - { - return 1; - } - - return s.calculateXpModifier(); } } diff --git a/runelite-client/src/main/java/net/runelite/client/game/NPCStats.java b/runelite-client/src/main/java/net/runelite/client/game/NPCStats.java deleted file mode 100644 index 51da78ca50..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/game/NPCStats.java +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (c) 2019, TheStonedTurtle - * 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.game; - -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import java.io.IOException; -import lombok.Builder; -import lombok.Value; - -@Value -@Builder(builderClassName = "Builder") -public class NPCStats -{ - private final String name; - - private final int hitpoints; - private final int combatLevel; - private final int slayerLevel; - private final int attackSpeed; - - private final int attackLevel; - private final int strengthLevel; - private final int defenceLevel; - private final int rangeLevel; - private final int magicLevel; - - private final int stab; - private final int slash; - private final int crush; - private final int range; - private final int magic; - - private final int stabDef; - private final int slashDef; - private final int crushDef; - private final int rangeDef; - private final int magicDef; - - private final int bonusAttack; - private final int bonusStrength; - private final int bonusRangeStrength; - private final int bonusMagicDamage; - - private final boolean poisonImmune; - private final boolean venomImmune; - - private final boolean dragon; - private final boolean demon; - private final boolean undead; - - /** - * Based off the formula found here: http://services.runescape.com/m=forum/c=PLuJ4cy6gtA/forums.ws?317,318,712,65587452,209,337584542#209 - * - * @return bonus XP modifier - */ - public double calculateXpModifier() - { - final double averageLevel = Math.floor((attackLevel + strengthLevel + defenceLevel + hitpoints) / 4); - final double averageDefBonus = Math.floor((stabDef + slashDef + crushDef) / 3); - - return (1 + Math.floor(averageLevel * (averageDefBonus + bonusStrength + bonusAttack) / 5120) / 40); - } - - // Because this class is here we can't add the TypeAdapter to gson (easily) - // doesn't mean we can't use one to do it a bit quicker - public static final TypeAdapter NPC_STATS_TYPE_ADAPTER = new TypeAdapter() - { - @Override - public void write(JsonWriter out, NPCStats value) - { - throw new UnsupportedOperationException("Not supported"); - } - - @Override - public NPCStats read(JsonReader in) throws IOException - { - in.beginObject(); - NPCStats.Builder builder = NPCStats.builder(); - - // Name is the only one that's guaranteed - in.skipValue(); - builder.name(in.nextString()); - - while (in.hasNext()) - { - switch (in.nextName()) - { - case "hitpoints": - builder.hitpoints(in.nextInt()); - break; - case "combatLevel": - builder.combatLevel(in.nextInt()); - break; - case "slayerLevel": - builder.slayerLevel(in.nextInt()); - break; - case "attackSpeed": - builder.attackSpeed(in.nextInt()); - break; - case "attackLevel": - builder.attackLevel(in.nextInt()); - break; - case "strengthLevel": - builder.strengthLevel(in.nextInt()); - break; - case "defenceLevel": - builder.defenceLevel(in.nextInt()); - break; - case "rangeLevel": - builder.rangeLevel(in.nextInt()); - break; - case "magicLevel": - builder.magicLevel(in.nextInt()); - break; - case "stab": - builder.stab(in.nextInt()); - break; - case "slash": - builder.slash(in.nextInt()); - break; - case "crush": - builder.crush(in.nextInt()); - break; - case "range": - builder.range(in.nextInt()); - break; - case "magic": - builder.magic(in.nextInt()); - break; - case "stabDef": - builder.stabDef(in.nextInt()); - break; - case "slashDef": - builder.slashDef(in.nextInt()); - break; - case "crushDef": - builder.crushDef(in.nextInt()); - break; - case "rangeDef": - builder.rangeDef(in.nextInt()); - break; - case "magicDef": - builder.magicDef(in.nextInt()); - break; - case "bonusAttack": - builder.bonusAttack(in.nextInt()); - break; - case "bonusStrength": - builder.bonusStrength(in.nextInt()); - break; - case "bonusRangeStrength": - builder.bonusRangeStrength(in.nextInt()); - break; - case "bonusMagicDamage": - builder.bonusMagicDamage(in.nextInt()); - break; - case "poisonImmune": - builder.poisonImmune(in.nextBoolean()); - break; - case "venomImmune": - builder.venomImmune(in.nextBoolean()); - break; - case "dragon": - builder.dragon(in.nextBoolean()); - break; - case "demon": - builder.demon(in.nextBoolean()); - break; - case "undead": - builder.undead(in.nextBoolean()); - break; - } - } - - in.endObject(); - return builder.build(); - } - }; -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/game/PlayerContainer.java b/runelite-client/src/main/java/net/runelite/client/game/PlayerContainer.java deleted file mode 100644 index ec778e7b51..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/game/PlayerContainer.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2019, ganom - * 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.game; - -import java.util.LinkedHashMap; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; -import net.runelite.api.Player; -import net.runelite.http.api.hiscore.HiscoreResult; - -@Getter -@Setter -@ToString(exclude = "player") -public class PlayerContainer -{ - private AttackStyle attackStyle; - private AttackStyle weakness; - private HiscoreResult skills; - private LinkedHashMap gear; - private LinkedHashMap riskedGear; - private MeleeStyle meleeStyle; - private Player player; - private String location; - private String name; - private String targetString; - private CombatStats combatStats; - private boolean httpRetry; - private boolean hiscoresRequested; - private boolean scouted; - private boolean attacking; - private boolean friend; - private boolean clan; - private int hpLevel; - private int potionBoost; - private int prayerLevel; - private int risk; - private int scoutTimer; - private int shield; - private int timer; - private int weapon; - private int wildyLevel; - - PlayerContainer(Player player) - { - this.attackStyle = AttackStyle.UNKNOWN; - this.gear = new LinkedHashMap<>(); - this.hpLevel = 0; - this.location = "N/A"; - this.meleeStyle = MeleeStyle.STAB; - this.name = player.getName(); - this.player = player; - this.riskedGear = new LinkedHashMap<>(); - this.scoutTimer = 500; - this.scouted = false; - this.skills = null; - this.targetString = ""; - this.weakness = AttackStyle.UNKNOWN; - } - - void reset() - { - setMeleeStyle(MeleeStyle.NONE); - if (getTimer() > 0) - { - setTimer(getTimer() - 1); - if (getTimer() == 0) - { - setAttacking(false); - } - } - } - - @Getter(AccessLevel.PACKAGE) - enum MeleeStyle - { - CRUSH, - SLASH, - STAB, - NONE - } -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/game/PlayerManager.java b/runelite-client/src/main/java/net/runelite/client/game/PlayerManager.java deleted file mode 100644 index d395567dfc..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/game/PlayerManager.java +++ /dev/null @@ -1,546 +0,0 @@ -package net.runelite.client.game; - -import java.io.IOException; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.stream.Collectors; -import javax.annotation.Nullable; -import javax.inject.Inject; -import javax.inject.Singleton; -import lombok.extern.slf4j.Slf4j; -import net.runelite.api.Actor; -import net.runelite.api.Client; -import net.runelite.api.ItemDefinition; -import net.runelite.api.ItemID; -import net.runelite.api.NPC; -import net.runelite.api.Player; -import net.runelite.api.WorldType; -import net.runelite.api.events.AnimationChanged; -import net.runelite.api.events.PlayerAppearanceChanged; -import net.runelite.api.events.PlayerDespawned; -import net.runelite.api.kit.KitType; -import net.runelite.client.eventbus.EventBus; -import net.runelite.client.events.AttackStyleChanged; -import net.runelite.client.util.PvPUtil; -import net.runelite.http.api.hiscore.HiscoreClient; -import net.runelite.http.api.hiscore.HiscoreResult; -import net.runelite.http.api.item.ItemEquipmentStats; -import net.runelite.http.api.item.ItemStats; -import okhttp3.OkHttpClient; - -@Singleton -@Slf4j -@SuppressWarnings("unused") -public class PlayerManager -{ - private final HiscoreClient hiscoreClient; - private final Client client; - private final ItemManager itemManager; - private final EventBus eventBus; - private final FriendChatManager friendChatManager; - private final Map playerMap = new ConcurrentHashMap<>(); - private final Map resultCache = new ConcurrentHashMap<>(); - private final ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2); - - @Inject - PlayerManager( - final Client client, - final EventBus eventBus, - final ItemManager itemManager, - final FriendChatManager friendChatManager, - final OkHttpClient okHttpClient - ) - { - this.client = client; - this.itemManager = itemManager; - this.eventBus = eventBus; - this.friendChatManager = friendChatManager; - this.hiscoreClient = new HiscoreClient(okHttpClient); - - eventBus.subscribe(PlayerDespawned.class, this, this::onPlayerDespawned); - eventBus.subscribe(AnimationChanged.class, this, this::onAnimationChanged); - eventBus.subscribe(PlayerAppearanceChanged.class, this, this::onAppearenceChanged); - } - - /** - * @return Collection of {@link PlayerContainer} that are attacking you, this can be empty. - */ - public Set getAllAttackers() - { - Set result = new HashSet<>(); - for (PlayerContainer playerContainer : playerMap.values()) - { - if (playerContainer.isAttacking()) - { - result.add(playerContainer); - } - } - return Collections.unmodifiableSet(result); - } - - /** - * @return Collection of {@link PlayerContainer}, this can be empty. - */ - public Collection getPlayerContainers() - { - return playerMap.values(); - } - - /** - * @param name Players name. - * @return {@link PlayerContainer} if provided with proper name, else null. - */ - @Nullable - public PlayerContainer getPlayer(String name) - { - return playerMap.get(name); - } - - /** - * @param player Player object. - * @return {@link PlayerContainer} if provided with proper name, else null. - */ - @Nullable - public PlayerContainer getPlayer(Player player) - { - if (player == null) - { - return null; - } - - return playerMap.get(player.getName()); - } - - /** - * This will keep submitting an http request until it successfully updates. - * - * @param name The player name you wish to update. - */ - public void updateStats(String name) - { - final PlayerContainer p = playerMap.get(name); - - if (p == null) - { - return; - } - - updateStats(p.getPlayer()); - } - - /** - * This will keep submitting an http request until it successfully updates. - * - * @param requestedPlayer The player object you wish to update. - */ - public void updateStats(Player requestedPlayer) - { - if (requestedPlayer == null) - { - return; - } - - final PlayerContainer player = playerMap.get(requestedPlayer.getName()); - - if (player == null) - { - return; - } - - if (resultCache.containsKey(player.getName())) - { - player.setSkills(resultCache.get(player.getName())); - player.setPrayerLevel(player.getSkills().getPrayer().getLevel()); - player.setHpLevel(player.getSkills().getHitpoints().getLevel()); - return; - } - - if (player.isHiscoresRequested() && !player.isHttpRetry()) - { - return; - } - - player.setHiscoresRequested(true); - - executorService.submit(() -> - { - int timeout = 0; - HiscoreResult result; - do - { - try - { - result = hiscoreClient.lookup(player.getName()); - } - catch (IOException ex) - { - if (timeout == 10) - { - log.error("HiScore Lookup timed out on: {}", player.getName()); - player.setHttpRetry(true); - return; - } - result = null; - timeout++; - try - { - Thread.sleep(1000); - } - catch (InterruptedException ignored) - { - } - } - } - while (result == null); - - resultCache.put(player.getName(), result); - player.setSkills(result); - player.setPrayerLevel(player.getSkills().getPrayer().getLevel()); - player.setHpLevel(player.getSkills().getHitpoints().getLevel()); - player.setHttpRetry(false); - player.setHiscoresRequested(false); - }); - } - - private void onAppearenceChanged(PlayerAppearanceChanged event) - { - PlayerContainer player = playerMap.computeIfAbsent(event.getPlayer().getName(), s -> new PlayerContainer(event.getPlayer())); - update(player); - player.setFriend(client.isFriended(player.getName(), false)); - player.setClan(friendChatManager.isMember(player.getName())); - } - - private void onPlayerDespawned(PlayerDespawned event) - { - playerMap.remove(event.getPlayer().getName()); - } - - private void onAnimationChanged(AnimationChanged event) - { - final Actor actor = event.getActor(); - - if (actor.getInteracting() != client.getLocalPlayer() || !(actor instanceof Player) || actor.getAnimation() == -1) - { - return; - } - - final PlayerContainer player = playerMap.get(actor.getName()); - - if (player == null) - { - return; - } - - assert player.getPlayer() == actor; - - if (player.getSkills() == null) - { - updateStats(player.getPlayer()); - } - - player.setAttacking(true); - player.setTimer(8); - } - - private void update(PlayerContainer player) - { - player.setRisk(0); - updatePlayerGear(player); - updateAttackStyle(player); - updateWeakness(player); - player.setLocation(WorldLocation.location(player.getPlayer().getWorldLocation())); - player.setWildyLevel(PvPUtil.getWildernessLevelFrom(player.getPlayer().getWorldLocation())); - player.setTargetString(targetStringBuilder(player)); - } - - private void updatePlayerGear(PlayerContainer player) - { - final Map prices = new HashMap<>(); - - if (player.getPlayer().getPlayerAppearance() == null) - { - return; - } - - int magicAttack = 0, - magicDefence = 0, - magicStr = 0, - meleeAtkCrush = 0, - meleeAtkStab = 0, - meleeAtkSlash = 0, - meleeDefCrush = 0, - meleeDefStab = 0, - meleeDefSlash = 0, - meleeStr = 0, - rangeAttack = 0, - rangeDefence = 0, - rangeStr = 0, - speed = 0; - - for (KitType kitType : KitType.values()) - { - if (kitType.equals(KitType.RING) || kitType.equals(KitType.AMMUNITION)) - { - continue; - } - - final int id = player.getPlayer().getPlayerAppearance().getEquipmentId(kitType); - - if (id == -1) - { - continue; - } - - if (kitType.equals(KitType.WEAPON)) - { - player.setWeapon(id); - - switch (id) - { - case ItemID.HEAVY_BALLISTA: - case ItemID.HEAVY_BALLISTA_23630: - case ItemID.LIGHT_BALLISTA: - rangeStr += 150; - break; - case ItemID.MAPLE_LONGBOW: - case ItemID.MAPLE_SHORTBOW: - rangeStr += 31; - break; - case ItemID.MAGIC_SHORTBOW: - case ItemID.MAGIC_SHORTBOW_20558: - case ItemID.MAGIC_SHORTBOW_I: - rangeStr += +55; - break; - case ItemID.DARK_BOW: - case ItemID.DARK_BOW_12765: - case ItemID.DARK_BOW_12766: - case ItemID.DARK_BOW_12767: - case ItemID.DARK_BOW_12768: - case ItemID.DARK_BOW_20408: - rangeStr += +60; - break; - case ItemID.RUNE_CROSSBOW: - case ItemID.RUNE_CROSSBOW_23601: - rangeStr += +117; - break; - case ItemID.DRAGON_CROSSBOW: - case ItemID.ARMADYL_CROSSBOW: - case ItemID.ARMADYL_CROSSBOW_23611: - rangeStr += +122; - break; - } - } - - final ItemStats item = itemManager.getItemStats(id, false); - final ItemDefinition itemDefinition = itemManager.getItemDefinition(id); - - if (item == null) - { - log.debug("Item is null: {}", id); - continue; - } - - final ItemEquipmentStats stats = item.getEquipment(); - - speed += stats.getAspeed(); - meleeAtkCrush += stats.getAcrush(); - meleeAtkStab += stats.getAstab(); - meleeAtkSlash += stats.getAslash(); - meleeDefCrush += stats.getDcrush(); - meleeDefStab += stats.getDstab(); - meleeDefSlash += stats.getDslash(); - magicAttack += stats.getAmagic(); - rangeAttack += stats.getArange(); - magicDefence += stats.getDmagic(); - rangeDefence += stats.getDrange(); - rangeStr += stats.getRstr(); - meleeStr += stats.getStr(); - magicStr += stats.getMdmg(); - - if (ItemReclaimCost.breaksOnDeath(id)) - { - prices.put(id, itemManager.getRepairValue(id)); - log.debug("Item has a broken value: Id {}, Value {}", id, itemManager.getRepairValue(id)); - continue; - } - - if (!itemDefinition.isTradeable() && !ItemMapping.isMapped(id)) - { - prices.put(id, itemDefinition.getPrice()); - } - else if (itemDefinition.isTradeable()) - { - prices.put(id, itemManager.getItemPrice(id, false)); - } - } - - player.setCombatStats(new CombatStats( - magicAttack, - magicDefence, - magicStr, - meleeAtkCrush, - meleeAtkSlash, - meleeAtkStab, - (meleeAtkCrush + meleeAtkSlash + meleeAtkStab) / 3, - meleeDefCrush, - (meleeDefCrush + meleeDefSlash + meleeDefStab) / 3, - meleeDefSlash, - meleeDefStab, - meleeStr, - rangeAttack, - rangeDefence, - rangeStr, - speed - )); - updateGear(player, prices); - updateMeleeStyle(player); - } - - private void updateGear(PlayerContainer player, Map prices) - { - player.setGear(prices.entrySet() - .stream() - .sorted(Collections.reverseOrder(Map.Entry.comparingByValue())) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (oldValue, newValue) -> oldValue, LinkedHashMap::new)) - ); - - player.setRiskedGear(prices.entrySet() - .stream() - .sorted(Collections.reverseOrder(Map.Entry.comparingByValue())) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (oldValue, newValue) -> oldValue, LinkedHashMap::new)) - ); - - if (client.getWorldType().stream().noneMatch(x -> x == WorldType.HIGH_RISK)) - { - if (player.getPlayer().getSkullIcon() == null) - { - removeEntries(player.getRiskedGear(), player.getPrayerLevel() < 25 ? 3 : 4); - } - else - { - removeEntries(player.getRiskedGear(), player.getPrayerLevel() < 25 ? 0 : 1); - } - } - - int risk = 0; - for (int val : player.getRiskedGear().values()) - risk += val; - player.setRisk(risk); - } - - private void updateMeleeStyle(PlayerContainer player) - { - final CombatStats stats = player.getCombatStats(); - - if (stats.getMeleeAtkCrush() >= stats.getMeleeAtkSlash() && stats.getMeleeAtkCrush() >= stats.getMeleeAtkStab()) - { - player.setMeleeStyle(PlayerContainer.MeleeStyle.CRUSH); - } - else if (stats.getMeleeAtkSlash() >= stats.getMeleeAtkCrush() && stats.getMeleeAtkSlash() >= stats.getMeleeAtkStab()) - { - player.setMeleeStyle(PlayerContainer.MeleeStyle.SLASH); - } - else - { - player.setMeleeStyle(PlayerContainer.MeleeStyle.STAB); - } - } - - private void updateAttackStyle(PlayerContainer player) - { - final AttackStyle oldStyle = player.getAttackStyle(); - boolean staff = false; - - for (int id : player.getGear().keySet()) - { - ItemDefinition def = itemManager.getItemDefinition(id); - if (def.getName().toLowerCase().contains("staff")) - { - player.setAttackStyle(AttackStyle.MAGE); - if (oldStyle != player.getAttackStyle()) - { - eventBus.post(AttackStyleChanged.class, new AttackStyleChanged( - player.getPlayer(), oldStyle, player.getAttackStyle()) - ); - } - return; - } - } - - final CombatStats stats = player.getCombatStats(); - - if (stats.getMagicStr() >= stats.getRangeStr() && stats.getMagicStr() >= stats.getMeleeStr()) - { - player.setAttackStyle(AttackStyle.MAGE); - } - else if (stats.getRangeStr() >= stats.getMagicStr() && stats.getRangeStr() >= stats.getMeleeStr()) - { - player.setAttackStyle(AttackStyle.RANGE); - } - else - { - player.setAttackStyle(AttackStyle.MELEE); - } - - if (oldStyle != player.getAttackStyle()) - { - eventBus.post(AttackStyleChanged.class, new AttackStyleChanged( - player.getPlayer(), oldStyle, player.getAttackStyle()) - ); - } - } - - private void updateWeakness(PlayerContainer player) - { - final CombatStats stats = player.getCombatStats(); - - if (stats.getMagicDefence() <= stats.getRangeDefence() && stats.getMagicDefence() <= stats.getMeleeDefence()) - { - player.setWeakness(AttackStyle.MAGE); - } - else if (stats.getRangeDefence() <= stats.getMagicDefence() && stats.getRangeDefence() <= stats.getMeleeDefence()) - { - player.setWeakness(AttackStyle.RANGE); - } - else - { - player.setWeakness(AttackStyle.MELEE); - } - } - - private static void removeEntries(LinkedHashMap map, int quantity) - { - final Iterator> it = map.entrySet().iterator(); - for (int i = 0; it.hasNext() && i < quantity; i++) - { - it.next(); - it.remove(); // LinkedHashMap iterator supports this - } - } - - private String targetStringBuilder(PlayerContainer player) - { - if (player.getPlayer().getInteracting() != null) - { - Actor actor = player.getPlayer().getInteracting(); - if (actor instanceof Player) - { - return "(Player) " + actor.getName(); - } - else if (actor instanceof NPC) - { - return "(NPC) " + actor.getName(); - } - } - return "No Target Detected"; - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/game/Sound.java b/runelite-client/src/main/java/net/runelite/client/game/Sound.java deleted file mode 100644 index 095aef5a27..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/game/Sound.java +++ /dev/null @@ -1,42 +0,0 @@ -package net.runelite.client.game; - -public enum Sound -{ - FIFTEEN_SECONDS(1, "net/runelite/client/game/sounds/15seconds.wav"), - FIVE_SECONDS(2, "net/runelite/client/game/sounds/5seconds.wav"), - ATTACK_WITH_MAGIC(3, "net/runelite/client/game/sounds/attackmagic.wav"), - ATTACK_WITH_MELEE(4, "net/runelite/client/game/sounds/attackmelee.wav"), - ATTACK_WITH_RANGED(5, "net/runelite/client/game/sounds/attackranged.wav"), - INCOMING(6, "net/runelite/client/game/sounds/incoming.wav"), - MOVE(7, "net/runelite/client/game/sounds/move.wav"), - PRAY_MAGIC(8, "net/runelite/client/game/sounds/praymagic.wav"), - PRAY_MELEE(9, "net/runelite/client/game/sounds/praymelee.wav"), - PRAY_RANGED(10, "net/runelite/client/game/sounds/prayranged.wav"), - REENABLE_PRAYER(11, "net/runelite/client/game/sounds/reenableprayer.wav"), - RUNAWAY(12, "net/runelite/client/game/sounds/runaway.wav"), - LOW_HEATLH(13, "net/runelite/client/game/sounds/lowhealth.wav"), - LOW_PRAYER(14, "net/runelite/client/game/sounds/lowprayer.wav"), - OUT_OF_COMBAT(15, "net/runelite/client/game/sounds/outofcombat.wav"), - RESTORED_SPECIAL_ATTACK(16, "net/runelite/client/game/sounds/restorespec.wav"), - IDLE(17, "net/runelite/client/game/sounds/idle.wav"), - BREAK(18, "net/runelite/client/game/sounds/break.wav"); - - private final String filePath; - private final int id; - - Sound(int id, String filePath) - { - this.id = id; - this.filePath = filePath; - } - - public String getFilePath() - { - return this.filePath; - } - - public int getId() - { - return this.id; - } -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/game/SoundManager.java b/runelite-client/src/main/java/net/runelite/client/game/SoundManager.java deleted file mode 100644 index a306b4bf63..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/game/SoundManager.java +++ /dev/null @@ -1,95 +0,0 @@ -package net.runelite.client.game; - -import com.google.inject.Inject; -import java.io.IOException; -import javax.inject.Singleton; -import javax.sound.sampled.AudioFormat; -import javax.sound.sampled.AudioInputStream; -import javax.sound.sampled.AudioSystem; -import javax.sound.sampled.BooleanControl; -import javax.sound.sampled.DataLine; -import javax.sound.sampled.FloatControl; -import javax.sound.sampled.LineUnavailableException; -import javax.sound.sampled.SourceDataLine; -import javax.sound.sampled.UnsupportedAudioFileException; -import net.runelite.client.config.RuneLiteConfig; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@Singleton -public class SoundManager -{ - private static final Logger log = LoggerFactory.getLogger(SoundManager.class); - private final RuneLiteConfig runeliteConfig; - - @Inject - private SoundManager(RuneLiteConfig runeLiteConfig) - { - this.runeliteConfig = runeLiteConfig; - } - - public void playSound(final Sound sound) - { - new Thread(new Runnable() - { - - @Override - public void run() - { - try - { - AudioInputStream in = AudioSystem.getAudioInputStream(this.getClass().getClassLoader().getResource(sound.getFilePath())); - AudioFormat outFormat = SoundManager.this.getOutFormat(in.getFormat()); - DataLine.Info info = new DataLine.Info(SourceDataLine.class, outFormat); - SourceDataLine line = (SourceDataLine) AudioSystem.getLine(info); - if (line != null) - { - line.open(outFormat, 2200); - if (line.isControlSupported(FloatControl.Type.MASTER_GAIN)) - { - int volume = SoundManager.this.runeliteConfig.volume(); - FloatControl gainControl = (FloatControl) line.getControl(FloatControl.Type.MASTER_GAIN); - BooleanControl muteControl = (BooleanControl) line.getControl(BooleanControl.Type.MUTE); - if (volume == 0) - { - muteControl.setValue(true); - } - else - { - muteControl.setValue(false); - gainControl.setValue((float) (Math.log((double) volume / 100.0) / Math.log(10.0) * 20.0)); - } - } - line.start(); - SoundManager.this.stream(AudioSystem.getAudioInputStream(outFormat, in), line); - line.drain(); - line.stop(); - } - } - catch (IOException | LineUnavailableException | UnsupportedAudioFileException e) - { - throw new IllegalStateException(e); - } - } - }).start(); - } - - private AudioFormat getOutFormat(AudioFormat inFormat) - { - int ch = inFormat.getChannels(); - float rate = inFormat.getSampleRate(); - return new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, rate, 16, ch, ch * 2, rate, false); - } - - private void stream(AudioInputStream in, SourceDataLine line) throws IOException - { - byte[] buffer = new byte[2200]; - int n = 0; - while (n != -1) - { - line.write(buffer, 0, n); - n = in.read(buffer, 0, buffer.length); - } - } - -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/game/SpriteManager.java b/runelite-client/src/main/java/net/runelite/client/game/SpriteManager.java index fbc7dc9c2a..3d7271af7f 100644 --- a/runelite-client/src/main/java/net/runelite/client/game/SpriteManager.java +++ b/runelite-client/src/main/java/net/runelite/client/game/SpriteManager.java @@ -39,7 +39,7 @@ import javax.swing.JLabel; import javax.swing.SwingUtilities; import net.runelite.api.Client; import net.runelite.api.GameState; -import net.runelite.api.Sprite; +import net.runelite.api.SpritePixels; import net.runelite.client.callback.ClientThread; import net.runelite.client.ui.overlay.infobox.InfoBox; import net.runelite.client.ui.overlay.infobox.InfoBoxManager; @@ -57,7 +57,7 @@ public class SpriteManager @Inject private InfoBoxManager infoBoxManager; - private final Cache cache = CacheBuilder.newBuilder() + public Cache cache = CacheBuilder.newBuilder() .maximumSize(128L) .expireAfterAccess(1, TimeUnit.HOURS) .build(); @@ -78,7 +78,7 @@ public class SpriteManager return cached; } - Sprite[] sp = client.getSprites(client.getIndexSprites(), archive, 0); + SpritePixels[] sp = client.getSprites(client.getIndexSprites(), archive, 0); if (sp == null) { return null; @@ -127,8 +127,12 @@ public class SpriteManager public void addSpriteTo(JButton c, int archive, int file) { getSpriteAsync(archive, file, img -> + { SwingUtilities.invokeLater(() -> - c.setIcon(new ImageIcon(img)))); + { + c.setIcon(new ImageIcon(img)); + }); + }); } /** @@ -137,8 +141,12 @@ public class SpriteManager public void addSpriteTo(JLabel c, int archive, int file) { getSpriteAsync(archive, file, img -> + { SwingUtilities.invokeLater(() -> - c.setIcon(new ImageIcon(img)))); + { + c.setIcon(new ImageIcon(img)); + }); + }); } public void addSpriteOverrides(SpriteOverride[] add) @@ -150,12 +158,12 @@ public class SpriteManager clientThread.invokeLater(() -> { - Map overrides = client.getSpriteOverrides(); + Map overrides = client.getSpriteOverrides(); Class owner = add[0].getClass(); for (SpriteOverride o : add) { BufferedImage image = ImageUtil.getResourceStreamFromClass(owner, o.getFileName()); - Sprite sp = ImageUtil.getImageSprite(image, client); + SpritePixels sp = ImageUtil.getImageSpritePixels(image, client); overrides.put(o.getSpriteId(), sp); } }); @@ -165,7 +173,7 @@ public class SpriteManager { clientThread.invokeLater(() -> { - Map overrides = client.getSpriteOverrides(); + Map overrides = client.getSpriteOverrides(); for (SpriteOverride o : remove) { overrides.remove(o.getSpriteId()); diff --git a/runelite-client/src/main/java/net/runelite/client/game/SpriteOverride.java b/runelite-client/src/main/java/net/runelite/client/game/SpriteOverride.java index 56da04b0cc..a4f894d5c0 100644 --- a/runelite-client/src/main/java/net/runelite/client/game/SpriteOverride.java +++ b/runelite-client/src/main/java/net/runelite/client/game/SpriteOverride.java @@ -24,10 +24,14 @@ */ package net.runelite.client.game; +import net.runelite.api.SpriteID; + public interface SpriteOverride { /** * An ID for a sprite. Negative numbers are used by RuneLite specific sprites + * + * @see SpriteID */ int getSpriteId(); diff --git a/runelite-client/src/main/java/net/runelite/client/game/WorldLocation.java b/runelite-client/src/main/java/net/runelite/client/game/WorldLocation.java deleted file mode 100644 index a8bd1d5b3a..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/game/WorldLocation.java +++ /dev/null @@ -1,906 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2019 openosrs - * Redistributions and modifications of this software are permitted as long as this notice remains in its original unmodified state at the top of this file. - * If there are any questions comments, or feedback about this software, please direct all inquiries directly to the file authors: - * ST0NEWALL#9112 - * Macweese#1169 UID 159941566994186240, macweese@pm.me - * openosrs Discord: https://discord.gg/Q7wFtCe - * openosrs website: https://openosrs.com - ******************************************************************************/ - -package net.runelite.client.game; - -import com.google.common.collect.ImmutableMap; -import java.util.Arrays; -import java.util.Collection; -import java.util.Map; -import java.util.stream.Collectors; -import lombok.Getter; -import net.runelite.api.coords.WorldArea; -import net.runelite.api.coords.WorldPoint; -import net.runelite.client.util.PvPUtil; - - /* - * Enums sorted alphabetically by main regions (Kingdoms) and then their sub-regions or notable features - * Example: - * Wilderness - * Mage Bank - */ -public enum WorldLocation -{ - - /*- - * Ape Atoll - * Crash Island - * Marim - */ - APE_ATOLL_TEMPLE("Ape Atoll Temple", new Location(2784, 2802, 2810, 2770), 0), - APE_ATOLL_GATE("Ape Atoll Gate", new Location(2712, 2761, 2730, 2749), 0), - APE_ATOLL_GLIDER("Ape Atoll Glider", new Location(2707, 2808, 2719, 2797), 0), - APE_ATOLL_TEAKS("Ape Atoll Teak Trees", new Location(2756, 2708, 2791, 2689), 0), - CRASH_ISLAND("Crash Island", new Location(2881, 2749, 2943, 2691), 0), - KRUK_DUNGEON_1("Monkey Madness 2 Dungeon", new Location(2689, 9150, 2815, 9088), 0), - KRUK_DUNGEON_2("Monkey Madness 2 Dungeon", new Location(2689, 9150, 2815, 9088), 1), - KRUK_DUNGEON_3("Monkey Madness 2 Dungeon", new Location(2309, 9277, 2454, 9131), 1), - MARIM_NORTH("North Marim", new Location(2731, 2804, 2783, 2786), 0), - MARIM_SOUTH("South Marim", new Location(2731, 2785, 2783, 2762), 0), - MONKEY_MADNESS_DUNGEON("Monkey Madness 1 Dungeon", new Location(2689, 9150, 2815, 9088), 0), - - /*- - * Asgarnia - * Faldor - * Burthorpe - * Edgeville - * Entrana - * Port Sarim - * Rimmington - * Taverly - */ - ASGARNIAN_ICE_DUNGEON_WYVERNS("Asgarnian Ice Dungeon - Skeletal Wyverns", new Location(3022, 9559, 3070, 9537), 0), - ASGARNIAN_ICE_DUNGEON_ICE_MONSTERS("Asgarnian Ice Dungeon - Ice Warriors & Ice Giants", new Location(3043, 9587, 3065, 9570), 0), - ASGARNIAN_ICE_DUNGEON_PIRATES("Asgarnian Ice Dungeon - Pirates", new Location(2986, 9568, 2999, 9585), 0), - BURTHOPRE_GAMES_TELEPORT("Burthorpe Games Tele", new Location(2890, 3557, 2907, 3549), 0), - CRAFTING_GUILD("Crafting Guild", new Location(2921, 3292, 2944, 3275), 0), - EDGEVILLE_MONASTERY("Edgeville Monastery", new Location(3044, 3507, 3060, 3471), 0), - FALADOR_BANK("Fally Bank", new Location(2943, 3372, 2949, 3358), 0), - FALADOR_CENTER("Fally Center", new Location(2959, 3385, 2972, 3374), 0), - FALADOR_EAST_BANK("Fally East Bank", new Location(3008, 3358, 3021, 3353), 0), - FALADOR_FARM("Falador Farm", new Location(3014, 3314, 3067, 3283), 0), - FALADOR_PARK("Fally Park", new Location(2982, 3390, 3025, 3368), 0), - FALADOR_PARTYROOM("Falador Partyroom", new Location(3035, 3386, 3056, 3370), 0), - FALADOR_RESPAWN("Fally Respawn", new Location(2957, 3355, 2998, 3325), 0), - GOBLIN_VILLAGE("Goblin Village", new Location(2948, 3516, 2963, 3493), 0), - HEROES_GUILD("Heroes' Guild", new Location(2881, 3517, 2902, 3504), 0), - HEROES_GUILD_DUNGEON("Heroes' Guild Dungeon", new Location(2885, 9918, 2945, 9882), 0), - ICE_MOUNTAIN("Ice Mountain", new Location(3001, 3508, 3024, 3463), 0), - MIND_ATLAR("Mind Altar", new Location(2970, 3520, 2990, 3509), 0), - MUDSKIPPER_POINT("Mudskipper point", new Location(2980, 3145, 3011, 3104), 0), - PORT_SARIM("Port Sarim", new Location(3024, 3250, 3055, 3192), 0), - PORT_SARIM_JAIL("Port Sarim Jail", new Location(3009, 3193, 3021, 3178), 0), - RIMMINGTON("Rimmington", new Location(2946, 3213, 2970, 3188), 0), - RIMMINGTON_DOCKS("Rimmington Docks", new Location(2905, 3228, 2922, 3222), 0), - RIMMINGTON_MINE("Rimmington Mine", new Location(2968, 3252, 2991, 3230), 0), - RIMMINGTON_PORTAL("Rimmington Portal", new Location(2946, 3228, 2960, 3218), 0), - ROGUES_DEN("Rogue's Den", new Location(3036, 4957, 3067, 4986), 1), - TAVERLY("Taverly", new Location(2880, 3442, 2917, 3409), 0), - TAVERLY_DUNGEON_BLACK_KNGIHTS("Taverly Dungeon - Black Knights", new Location(2883, 9717, 2939, 9667), 0), - TAVERLY_DUNGEON_HILL_GIANTS("Taverly Dungeon - Hill Giants", new Location(2895, 9743, 2920, 9718), 0), - TAVERLY_DUNGEON_BLACK_DRAGONS("Taverly Dungeon - Black Dragons", new Location(2812, 9836, 2846, 9822), 0), - TAVERLY_DUNGEON_HELLHOUNDS("Taverly Dungeon - Hell Hounds", new Location(2847, 9854, 2873, 9822), 0), - TAVERLY_DUNGEON_BLUE_DRAGONS("Taverly Dungeon - Blue Dragons", new Location(2890, 9778, 2923, 9813), 0), - TAVERLY_DUNGEON_BLACK_DEMONS("Taverly Dungeon - Black Demons", new Location(2844, 9800, 2873, 9773), 0), - TAVERLY_DUNGEON_POISON_SPIDERS("Taverly Dungeon - Poison Spiders", new Location(3010, 4756, 3068, 4803), 0), - TAVERLY_DUNGEON_CHAOS_DRUIDS("Taverly Dungeon - Chaos Druids", new Location(2915, 9856, 2944, 9833), 0), - TAVERLY_DUNGEON_LESSER_DEMONS("Taverly Dungeon - Lesser Demons", new Location(2924, 9813, 2946, 9777), 0), - TAVERLY_DUNGEON_MAGIC_AXES("Taverly Dungeon - Magic Axes", new Location(2947, 9798, 2971, 9769), 0), - TAVERLY_DUNGEON_CHAOS_DWARVES("Taverly Dungeon - Chaos Dwarves", new Location(2920, 9776, 2938, 9745), 0), - TAVERLY_DUNGEON_MAIN_CORRIDOR("Taverly Dungeon - Main Corridor", new Location(2880, 9793, 2889, 9850), 0), - TAVERLY_GATE("Taverly Gate", new Location(2931, 3456, 2944, 3444), 0), - TAVERLY_POH_PORTAL("Taverly POH Portal", new Location(2885, 3471, 2899, 3458), 0), - WARRIORS_GUILD("Warriors' Guild", new Location(2838, 3536, 2876, 3555), 0), - WARRIORS_GUILD_BASEMENT("Warriors' Guild Basement (Dragon Defender)", new Location(2904, 9974, 2941, 9956), 0), - - /*- - * Entrana - */ - ENTRANA_BALLOON("Entrana Balloon", new Location(2803, 3359, 2815, 3347), 0), - ENTRANA_CHURCH("Entrana Church", new Location(2840, 3356, 2858, 3341), 0), - ENTRANA_DOCKS("Entrana Docks", new Location(2825, 3338, 2847, 3328), 0), - ENTRANA_NORTH("Entrana (North Portion)", new Location(2541, 2875, 2595, 2837), 0), - - /*- - * Feldip Hills - * Corsair Cove - * Gu'Tanoth - */ - CORSAIR_COVE("Corsair Cove", new Location(2541, 2875, 2595, 2837), 0), - CORSAIR_RESOURCE_AREA("Corsair Resource Area", new Location(2453, 2905, 2488, 2883), 0), - FELDIP_HILLS_GLIDER("Feldip Hills Glider", new Location(2536, 2975, 2546, 2965), 0), - FELDIP_HILLS_RED_CHINS("Feldip Hills Red Chins", new Location(2525, 2935, 2561, 2902), 0), - GU_TANOTH("Gu'Tanoth", new Location(2497, 3060, 2558, 3008), 0), - MYTHS_GUILD("Myth's Guild", new Location(2470, 2872, 2442, 2834), 0), - - /* - * Fossil Island - */ - MUSEUM_CAMP("Fossil Island Museum Camp", new Location(3708, 3797, 3751, 3833), 0), - FOSSIL_ISLAND_HOUSE_ON_THE_HILL("House on the Hill (Fossil Island)", new Location(3747, 3891, 3795, 3855), 0), - FOSSIL_ISLAND_MUSHROOM_FOREST("Fossil Island Mushroom Forest (Herbiboar)", new Location(3670, 3894, 3707, 3814), 0), - FOSSIL_ISLAND_SWAMP_NORTH("Fossil Island Swamp (North half)", new Location(3707, 3758, 3643, 3696), 0), - FOSSIL_ISLAND_SWAMP_SOUTH("Fossil Island Swamp (South half)", new Location(3707, 3813, 3643, 3759), 0), - FOSSIL_ISLAND_VERDANT_VALLEY("Fossil Island Verdant Valley (South East Island)", new Location(3670, 3894, 3707, 3814), 0), - FOSSIL_ISLAND_VOLCANO_BANK("Fossil Island Volcano Bank", new Location(3807, 3818, 3825, 3800), 0), - - /*- - * Fremennik Province - * Fremennik Isles (Neitiznot & Jatizo) - * Fremennik Slayer Dungeon - * Lunar Isle - * Miscellania and Etceteria - * Rellekka - * Waterbirth Island - */ - ETCETERIA("Etceteria", new Location(2626, 3904, 2571, 3861), 0), - ETCETERIA_DOCKS("Etceteria Docks", new Location(2571, 3904, 2626, 3861), 0), - FREMENNIK_BASILISK_KNIGHT_DUNGEON("Fremennik Basilisk Knight Dungeon", new Location(2398, 10468, 2496, 10370), 0), - FREMENNIK_SLAYER_DUNGEON("Fremennik Slayer Dungeon", new Location(2771, 10023, 2811, 9989), 0), - FREMENNIK_SLAYER_DUNGEON_BASILISKS("Fremennik Slayer Dungeon - Baslisks", new Location(2734, 10015, 2751, 9988), 0), - FREMENNIK_SLAYER_DUNGEON_ENTRANCE("Fremennik Slayer Dungeon Entrance", new Location(2776, 3604, 2801, 3626), 0), - FREMENNIK_SLAYER_DUNGEON_JELLIES("Fremennik Slayer Dungeon - Jellies", new Location(2694, 10035, 2733, 10016), 0), - FREMENNIK_SLAYER_DUNGEON_KURASKS("Fremennik Slayer Dungeon - Kurasks", new Location(2708, 10007, 2690, 9988), 0), - FREMENNIK_SLAYER_DUNGEON_PYREFIENDS("Fremennik Slayer Dungeon - Pyrefiends", new Location(2752, 10015, 2770, 9988), 0), - FREMENNIK_SLAYER_DUNGEON_TUROTHS("Fremennik Slayer Dungeon - Turoths", new Location(2709, 10015, 2733, 9988), 0), - JATIZSO("Jatizso", new Location(2369, 3826, 2428, 3776), 0), - KELDAGRIM_EAST("Eastern Keldagrim", new Location(2884, 10236, 2943, 10181), 0), - KELDAGRIM_ENTRANCE("Keldagrim Entrance", new Location(2722, 3720, 2738, 3703), 0), - KELDAGRIM_WEST("Western Keldagrim", new Location(2819, 10236, 2875, 10182), 0), - LUNAR_ISLE_CENTRAL("Lunar Isle Central", new Location(2055, 3933, 2112, 3888), 0), - LUNAR_ISLE_EAST("Lunar Isle East", new Location(2113, 3921, 2185, 3888), 0), - LUNAR_ISLE_NORTH("Lunar Isle North", new Location(2063, 3958, 2112, 3934), 0), - LUNAR_ISLE_NORTH_EAST("Lunar Isle North East", new Location(2113, 3958, 2185, 3922), 0), - LUNAR_ISLE_SOUTH("Lunar Isle South", new Location(2057, 3887, 2112, 3843), 0), - LUNAR_ISLE_SOUTHEAST("Lunar Isle SouthEast", new Location(2113, 3887, 2185, 3843), 0), - MISCELLANIA("Miscellania", new Location(2492, 3904, 2570, 3836), 0), - MISCELLANIA_DOCKS("Miscellania Docks", new Location(2623, 3851, 2603, 3840), 0), - MOUNTAIN_CAMP("Mountain Camp", new Location(2789, 3682, 2813, 3658), 0), - NEITIZNOT("Neitiznot", new Location(2300, 3826, 2368, 3776), 0), - PIRATES_COVE("Pirate's Cove", new Location(2186, 3842, 2228, 3785), 0), - RELLEKKA_MAIN_HALL("Rellekka Main Hall", new Location(2652, 3685, 2670, 3658), 0), - RELLEKKA_MARKET("Rellekka Market", new Location(2629, 3682, 2651, 3654), 0), - RELLEKKA_NORTH_DOCKS("Rellekka North Docks", new Location(2640, 3712, 2651, 3706), 0), - RELLEKKA_NORTH_EAST("Rellekka North East", new Location(2652, 3712, 2690, 3686), 0), - RELLEKKA_POH_PORTAL("Rellekka POH Portal", new Location(2662, 3635, 2676, 3624), 0), - RELLEKKA_SOUTH_DOCKS("Rellekka South Docks", new Location(2619, 3699, 2641, 3681), 0), - RELLEKKA_ZONE("Rellekka", new Location(2600, 3708, 2690, 3645), 0), - ROCK_CRABS_EAST("Rock Crabs East (Near Keldagrim)", new Location(2691, 3738, 2730, 3713), 0), - ROCK_CRABS_WEST("Rock Crabs West (North of Rellekka)", new Location(2650, 3738, 2690, 3713), 0), - VORKATH("Vorkath", new Location(2237, 4096, 2301, 4031), 0), - WATERBIRTH_DUNGEON_ROCK_LOBSTERS("Waterbirth Dungeon - Rock Lobsters", new Location(1875, 4380, 1919, 4412), 0), - WATERBIRTH_DUNGEON_DKS_1("DKS", new Location(2886, 4473, 2941, 4424), 0), // One of these is private, not sure which - WATERBIRTH_DUNGEON_DKS_2("DKS", new Location(2886, 4409, 2941, 4361), 0), // One of these is private, not sure which - WATERBIRTH_DUNGEON_ZONE_1("Waterbirth Dungeon", new Location(2435, 10176, 2558, 10112), 0), - WATERBIRTH_DUNGEON_ZONE_2("Waterbirth Dungeon", new Location(1788, 4413, 1966, 4352), 1), - WATERBIRTH_ISLAND("Waterbirth Island", new Location(2494, 3774, 2562, 3710), 0), - - /*- - * Great Kourend - * Arceuus - * Battlefront - * Catacombs of Kourend - * Crabclaw Caves - * Forthos Dungeon - * Hosidius - * Kebos Lowlands - * Kingstown - * Kourend Woodland - * Lake Molch - * Lizardman Settlement - * Lovakengj - * Mount Karuulm - * Mount Quidamortem - * Northern Tundras - * Port Piscarilius - * Shayzien - * Wintertodt - */ - ARCEUUS("Arceuus", new Location(1620, 3780, 1739, 3708), 0), - ARCEUUS_BANK("Arceuus Bank", new Location(1620, 3754, 1639, 3735), 0), - ARCEUUS_DENSE_ESSENCE_MINE("Arceuus Dense Essence Mine", new Location(1741, 3880, 1786, 3831), 0), - ARCEUUS_LIBRARY("Arceuus Library", new Location(1605, 3833, 1662, 3781), 0), - BATTLEFRONT("Battlefront Teleport", new Location(1344, 3745, 1362, 3726), 0), - BLAST_MINE("Lovakengj Blast Mine", new Location(1467, 3888, 1513, 3840), 0), - BLOOD_ALTAR("Blood Altar", new Location(1710, 3835, 1737, 3822), 0), - CHASM_OF_FIRE("Chasm of Fire", new Location(1411, 10108, 1468, 10050), 1), - COX("CoX", new Location(1226, 3574, 1270, 3559), 0), - CRAB_CLAW_ISLE("Crab Claw Isle", new Location(1745, 3449, 1795, 3399), 0), - DARK_ALTAR("Arceuus Dark Altar", new Location(1699, 3895, 1734, 3869), 0), - FARMING_GUILD("Farming Guild", new Location(1223, 3718, 1273, 3765), 0), - FISHING_HAMLET("Fishing Hamlet (East of Wintertodt Camp)", new Location(1683, 3969, 1720, 3917), 0), - FOODHALL("Piscarilius Foodhall", new Location(1830, 3762, 1854, 3734), 0), - FORTHOS_RUINS("Forthos Ruins", new Location(1666, 3590, 1684, 3561), 0), - FORTHOS_DUNGEON_ALTAR("Forthos Dungeon - Altar", new Location(1794, 9954, 1804, 9946), 0), - FORTHOS_DUNGEON_GRUBBY_CHEST("Forthos Dungeon - Grubby Chest", new Location(1793, 9928, 1799, 9922), 0), - FORTHOS_DUNGEON_LADDER_EAST("Forthos Dungeon - East Ladder", new Location(1825, 9978, 1835, 9969), 0), - FORTHOS_DUNGEON_LADDER_WEST("Forthos Dungeon - West Ladder", new Location(1795, 9972, 1805, 9958), 0), - FORTHOS_DUNGEON_RED_DRAGONS("Forthos Dungeon - Red Dragons", new Location(1807, 9944, 1828, 9933), 0), - FORTHOS_DUNGEON_SARACHNIS("Forthos Dungeon - Sarachnis", new Location(1829, 9890, 1854, 9913), 0), - FORTHOS_DUNGEON_SPIDERS("Forthos Dungeon - Red Spiders", new Location(1830, 9968, 1848, 9947), 0), - FORTHOS_DUNGEON_UNDEAD_DRUIDS_1("Forthos Dungeon - Undead Druids", new Location(1795, 9944, 1806, 9933), 0), - FORTHOS_DUNGEON_UNDEAD_DRUIDS_2("Forthos Dungeon - Undead Druids", new Location(1806, 9973, 1814, 9958), 0), - FORTHOS_DUNGEON_ZONE("Forthos Dungeon", new Location(1789, 9985, 1858, 9914), 0), - HOSIDIUS_BANK("Hosidius Bank", new Location(1743, 3603, 1753, 3594), 0), - HOSIDIUS_FRUIT_STALLS("Hosidius Fruit Stalls", new Location(1790, 3614, 1806, 3603), 0), - HOSIDIUS_KITCHEN("Hosidius Kitchen (Bank)", new Location(1671, 3625, 1687, 3610), 0), - HOSIDIUS_PLOW_FIELD("Hosidius Plow Fields", new Location(1761, 3558, 1781, 3519), 0), - HOSIDIUS_POH_PORTAL("Hosidius POH Portal", new Location(1735, 3522, 1747, 3511), 0), - HOSIDIUS_SQUARE("Hosidius Square", new Location(1754, 3607, 1772, 3589), 0), - HOSIDIUS_VINERY("Hosidius Vinery", new Location(1799, 3573, 1816, 3537), 0), - HOSIDIUS_ZONE("Hosidius", new Location(1737, 3627, 1789, 3582), 0), - KOUREND_CASTLE("Kourend Castle", new Location(1592, 3700, 1692, 3646), 0), - KOUREND_CATACOMBS_ABYSSAL_DEMONS("Kourend Catacombs - Abyssal Demons", new Location(1667, 10101, 1683, 10082), 0), - KOUREND_CATACOMBS_BLACK_DEMONS("Kourend Catacombs - Black Demons", new Location(1713, 10073, 1724, 10086), 0), - KOUREND_CATACOMBS_BRUTAL_BLACK_DRAGONS("Kourend Catacombs - Brutal Black Dragons", new Location(1604, 10105, 1635, 10068), 0), - KOUREND_CATACOMBS_CENTER("Kourend Catacombs Center", new Location(1655, 10055, 1670, 10038), 0), - KOUREND_CATACOMBS_DUST_DEVILS("Kourend Catacombs - Dust Devils", new Location(1704, 10037, 1734, 9985), 0), - KOUREND_CATACOMBS_GREATER_DEMONS("Kourend Catacombs - Greater Demons", new Location(1684, 10105, 1724, 10087), 0), - KOUREND_CATACOMBS_NECHRYAELS("Kourend Catacombs - Nechryaels", new Location(1684, 10086, 1712, 10073), 0), - KOUREND_CATACOMBS_SOUTH("Kourend Catacombs - South", new Location(1639, 10014, 1702, 9985), 0), - KOUREND_CATACOMBS_SOUTH_WEST("Kourend Catacombs South-West Corner", new Location(1596, 10028, 1634, 9984), 0), - KOUREND_CATACOMBS_STEEL_DRAGONS("Kourend Catacombs - Steel Dragons", new Location(1599, 10066, 1630, 10029), 0), - KOUREND_CATACOMBS_ZONE("Kourend Catacombs", new Location(1595, 10106, 1735, 9984), 0), - LANDS_END("Land's End", new Location(1481, 3448, 1527, 3396), 0), - LAKE_MOLCH("Lake Molch", new Location(1357, 3643, 1377, 3624), 0), - LIZARDMAN_SHAMANS("Lizardman Shamans", new Location(1414, 3726, 1461, 3688), 0), - LOVAKENGJ("Lovakengj", new Location(1425, 3810, 1520, 3730), 0), - MOUNT_KARUULM("Mount Karuulm", new Location(1287, 3829, 1331, 3787), 0), - PISCARILIUS_ANGLERFISH("Piscarilius Angler Fishing Spot", new Location(1807, 3779, 1842, 3766), 0), - PISCARILIUS_BANK("Piscarilius Bank", new Location(1793, 3794, 1812, 3782), 0), - PISCARILIUS_PORT("Port Piscarilius", new Location(1788, 3712, 1849, 3673), 0), - PISCARILIUS_ZONE("Piscarilius", new Location(1740, 3814, 1854, 3713), 0), - SANDCRABS_BANK("Sandcrabs Bank", new Location(1706, 3475, 1730, 3455), 0), - SANDCRABS_NORTH("Sandcrabs (East of Vinery)", new Location(1848, 3572, 1884, 3532), 0), - SANDCRABS_SOUTH_1("Sandcrabs (South of Tithe Farm)", new Location(1796, 3468, 1849, 3436), 0), - SANDCRABS_SOUTH_2("Sandcrabs (South Coast)", new Location(1745, 3474, 1795, 3450), 0), - SANDCRABS_SOUTH_EAST("Sandcrabs (East of Tithe Farm)", new Location(1850, 3529, 1884, 3465), 0), - SHAYZIEN_BANK("Shayzien Bank", new Location(1494, 3622, 1515, 3611), 0), - SHAYZIEN_CRYPTS_ENTRANCE("Shayzien Crypts Entrance", new Location(1474, 3570, 1502, 3535), 0), - SHAYZIEN_INFIRMARY("Shayzien Infirmary", new Location(1565, 3574, 1590, 3604), 0), - SHAYZIEN_ZONE("Shayzien", new Location(1472, 3644, 1591, 3521), 0), - SOUL_ALTAR("Soul Altar", new Location(1804, 3869, 1834, 3841), 0), - SULPHUR_MINE("Lovakengj Sulphur Mine", new Location(1415, 3888, 1466, 3840), 0), - SULPHUR_MINE_BANK("Lovakengj Sulphur Mine Bank", new Location(1430, 3838, 1443, 3817), 0), - TITHE_FARM("Tithe Farm", new Location(1794, 3480, 1841, 3517), 0), - WINTERTODT_CAMP("Wintertodt Camp", new Location(1616, 3963, 1645, 3932), 0), - WINTERTODT_ENTRANCE("Wintertodt Entrance", new Location(1617, 3986, 1641, 3964), 0), - WINTERTODT_NORTHEAST("Wintertodt NorthEast", new Location(1630, 4027, 1651, 4008), 0), - WINTERTODT_NORTHWEST("Wintertodt NorthWest", new Location(1608, 4028, 1629, 4008), 0), - WINDERTODT_SOUTH_EAST("Windertodt South East", new Location(1630, 4007, 1651, 3987), 0), - WINTERTODT_SOUTHWEST("Wintertodt SouthWest", new Location(1608, 4007, 1629, 3987), 0), - WOODCUTTING_GUILD_BANK("Woodcutting Guild Bank", new Location(1588, 3481, 1594, 3473), 0), - WOODCUTTING_GUILD_EAST("Woodcutting Guild (East Portion)", new Location(1623, 3519, 1657, 3488), 0), - WOODCUTTING_GUILD_WEST("Woodcutting Guild (Redwoods)", new Location(1562, 3503, 1586, 3476), 0), - WOODCUTTING_GUILD_ZONE("Woodcutting Guild", new Location(1560, 3520, 1659, 3471), 0), - - /*- - * Kandarin - * Ardougne - * Battlefield - * Camelot - * Catherby - * Fishing Guild & McGrubor's Woods - * Observatory - * Ourania - * Piscatoris Fishing Colony - * Port Khazard - * Seers' Village - * Tree Gnome Stronghold - * Tree Gnome Village - * Witchaven - * Yanille - */ - ARDOUGNE_CASTLE("Ardy Castle", new Location(2567, 3311, 2591, 3283), 0), - ARDOUGNE_DOCKS("Ardy Docks", new Location(2660, 3284, 2689, 3264), 0), - ARDOUGNE_MONASTERY("Ardougne Monastery", new Location(2587, 3227, 2623, 3202), 0), - ARDOUGNE_NORTH_BANK("Ardy North Bank", new Location(2611, 3336, 2622, 3329), 0), - ARDOUGNE_SOUTH_BANK("Ardy South Bank", new Location(2645, 3288, 2659, 3279), 0), - ARDOUGNE_STALLS("Ardy Stalls", new Location(2651, 3318, 2673, 3293), 0), - ARDOUGNE_ZOO("Ardy Zoo", new Location(2598, 3288, 2636, 3265), 0), - BARBARIAN_OUTPOST("Barbarian Outpost", new Location(2517, 3580, 2557, 3540), 0), - BAXTORIAN_WATERFALL_DUNGEON("Waterfall Dungeon (Baxtorian Falls)", new Location(2556, 9861, 2594, 9918), 0), - CAMELOT_CASTLE("Camelot Castle", new Location(2743, 3481, 2775, 3468), 0), - CASTLE_WARS_BANK("Castle Wars Bank", new Location(2435, 3100, 2448, 3078), 0), - CASTLE_WARS_ZONE("Castle Wars", new Location(2435, 3127, 2474, 3074), 0), - CATHERBY("Catherby", new Location(2791, 3457, 2833, 3436), 0), - CATHERBY_DOCKS("Catherby Docks", new Location(2790, 3432, 2808, 3409), 0), - CATHERBY_FISHING_SPOTS("Catherby Fishing Spots", new Location(2834, 3441, 2862, 3425), 0), - CATHERBY_FARMING_PATCH("Catherby Farming Patch", new Location(2791, 3472, 2833, 3458), 0), - EAGLES_PEAK("Eagles' Peak", new Location(2308, 3495, 2350, 3479), 0), - FALCONRY_HUNTING_AREA("Falconry Hunting Area", new Location(2365, 3621, 2390, 3572), 0), - FISHING_GUILD("Fishing Guild", new Location(2627, 3426, 2579, 3391), 0), - FISHING_PLATFORM("Fishing Platform", new Location(2763, 3290, 2792, 3273), 0), - GNOME_AGILITY("Gnome Agility", new Location(2469, 3441, 2489, 3412), 0), - GNOME_BALL("Gnome Ball", new Location(2384, 3495, 2408, 3479), 0), - GRAND_TREE("Grand Tree", new Location(2442, 3515, 2490, 3478), 0), - KRAKEN_COVE_DUNGEON("Kraken Dungeon", new Location(2303, 10047, 2240, 9983), 0), - KRAKEN_COVE_ENTRANCE("Kraken Cove Entrance", new Location(2262, 3623, 2295, 3596), 0), - LEGENDS_GUILD("Legends' Guild", new Location(2716, 3388, 2741, 3346), 0), - LEGENDS_GUILD_DUNGEON("Legends' Guild Dungeon", new Location(2690, 9784, 2740, 9730), 0), - LIGHTHOUSE("Lighthouse", new Location(2494, 3649, 2524, 3616), 0), - MCGRUBORS_WOODS("McGrubor's Woods", new Location(2624, 3501, 2647, 3481), 0), - NIEVE("Nieve", new Location(2430, 3425, 2435, 3419), 0), - NIGHTMARE_ZONE("Nightmare Zone", new Location(2599, 3119, 2614, 3111), 0), - OBSERVATORY("Observatory", new Location(2429, 3198, 2452, 3149), 0), - OBSERVATORY_DUNGEON("Obsvervatory Dungeon", new Location(2305, 9406, 2366, 9344), 0), - OTTOS_GROTTO("Barbarian Fishing", new Location(2491, 3519, 2527, 3488), 0), - OURANIA_CAVE("ZMI", new Location(3006, 5567, 3072, 5634), 0), - THE_OUTPOST("The Outpost", new Location(2428, 3356, 2443, 3338), 0), - PISCATORIS_FISHING_COLONY("Piscatoris Fishing Colony", new Location(2302, 3708, 2364, 3653), 0), - PORT_KHAZARD("Port Khazard", new Location(2624, 3182, 2680, 3143), 0), - RANGING_GUILD("Ranging Guild", new Location(2650, 3445, 2685, 3411), 0), - RED_SALAMANDERS("Red Salamanders", new Location(2441, 3229, 2464, 3204), 0), - SEERS_VILLAGE("Seers Village", new Location(2688, 3498, 2742, 3468), 0), - SINCLAIR_MANSION("Sinclair Mansion", new Location(2723, 3584, 2756, 3552), 0), - SMOKE_DEVIL_DUNGEON("CW Smoke Devil Dungeon", new Location(2379, 9467, 2427, 9415), 0), - SMOKE_DEVIL_DUNGEON_BOSS("CW Smoke Dungeon (Boss Room)", new Location(2347, 9462, 2377, 9438), 0), - SMOKE_DEVIL_DUNGEON_ENTRANCE("Smoke Devil Dungeon Entrance", new Location(2430, 3425, 2435, 3419), 0), - TRAINING_GROUND("Training Ground (Caged Ogres)", new Location(2501, 3387, 2534, 3358), 0), - TREE_GNOME_VILLAGE("Tree Gnome Village", new Location(2514, 3175, 2547, 3158), 0), - WEST_ARDOUGNE("West Ardy", new Location(2452, 3336, 2557, 3265), 0), - WITCHAVEN("Witchaven", new Location(2704, 3267, 2741, 3295), 0), - WITCHAVEN_DUNGEON("Witchaven Dungeon", new Location(2750, 9665, 2690, 9719), 0), - WIZARDS_GUILD("Wizards Guild", new Location(2585, 3092, 2596, 3082), 0), - WHITE_WOLF_MOUNTAIN_GNOME_GLIDER("White Wolf Mountain Gnome Glider", new Location(2838, 3509, 2852, 3496), 0), - YANILLE_AGILITY_DUNGEON("Yanille Agilty Dungeon", new Location(2559, 9536, 2624, 9475), 0), - YANILE_BANK("Yanile Bank", new Location(2608, 3097, 2616, 3087), 0), - YANILLE_EAST("Yanille East", new Location(2576, 3110, 2621, 3071), 0), - YANILLE_POH_PORTAL("Yanille POH Portal", new Location(2537, 3108, 2551, 3091), 0), - YANILLE_WEST("Yanille West", new Location(2532, 3110, 2575, 3071), 0), - - /*- - * Karamja - * Brimhaven - * Cairn Isle - * Crandor & Karamja Dungeon - * Kharazi Jungle - * Mor Ul Rel (TzHaar City) - * Musa Point - * Shilo Village - * Ship Yard - * Tai Bwo Wannai - */ - BRIMHAVEN_AGILITY_ARENA("Brimhaven Agility Arena", new Location(2757, 9594, 2809, 9541), 3), - BRIMHAVEN_DOCKS("Brimhaven Docks", new Location(2758, 3241, 2777, 3220), 0), - BRIMHAVEN_DUNGEON("Brimhaven Dungeon - Main Corridor", new Location(2690, 9572, 2714, 9556), 0), - BRIMHAVEN_DUNGEON_BLACK_DEMONS("Brimhaven Dungeon - Black Demons", new Location(2694, 9495, 2726, 9475), 0), - BRIMHAVEN_DUNGEON_BRONZE_DRAGONS("Brimhaven Dungeon - Bronze Dragons", new Location(2727, 9504, 2750, 9475), 0), - BRIMHAVEN_DUNGEON_DOGS("Brimhaven Dungeon - Dogs", new Location(2653, 9530, 2675, 9509), 0), - BRIMHAVEN_DUNGEON_FIRE_GIANTS("Brimhaven Dungeon - Fire Giants", new Location(2638, 9506, 2673, 9476), 0), - BRIMHAVEN_DUNGEON_METAL_DRAGONS_SLAYER("Brimhaven Dungeon - Metal Dragons (Slayer Only)", new Location(2626, 9469, 2685, 9409), 0), - BRIMHAVEN_DUNGEON_METAL_DRAGONS("Brimhaven Dungeon - Metal Dragons", new Location(2693, 9469, 2748, 9412), 0), - BRIMHAVEN_DUNGEON_MOSS_GIANTS("Brimhaven Dungeon - Moss Giants", new Location(2630, 9575, 2670, 9531), 0), - BRIMHAVEN_DUNGEON_RED_DRAGONS("Brimhaven Dungeon - Red Dragons", new Location(2686, 9553, 2726, 9496), 0), - BRIMHAVEN_POH_PORTAL("Brimhaven POH Portal", new Location(2749, 3184, 2765, 3170), 0), - CAIRN_ISLE("Cairn Isle", new Location(2752, 2993, 2775, 2963), 0), - CRANDOR("Crandor", new Location(2813, 3310, 2864, 3231), 0), - HARDWOOD_GROVE("Hardwood Grove", new Location(2815, 3092, 2830, 3073), 0), - KARAMBWAN_FISHING_SPOT("Karambwan Fishing Spot", new Location(2896, 3116, 2920, 3104), 0), - KARAMJA_DOCKS("Karamja Docks", new Location(2813, 3310, 2864, 3231), 0), - KARAMJA_GLORY_TELEPORT("Karamja Glory Tele", new Location(2910, 3177, 2934, 3156), 0), - KARAMJA_GNOME_GLIDER("Karamja Gnome Glider", new Location(2961, 2960, 2984, 2983), 0), - KARAMJA_SHIP_YARD("Karamja Ship Yard", new Location(2949, 3066, 3004, 3016), 0), - KARAMJA_VOLCANO("Karamja Volcano", new Location(2828, 3194, 2866, 3157), 0), - KARAMJA_VOLCANO_DUNGEON("Karamja Dungeon", new Location(2827, 9589, 2866, 9549), 0), - KARAMJA_VOLCANO_DUNGEON_ELVARG("Karamja Dungeon (Elvarg)", new Location(2826, 9661, 2868, 9603), 0), - KHARAZI_JUNGLE_EAST("Kharazi Jungle (Eastern Section)", new Location(2905, 2930, 2976, 2883), 0), - KHARAZI_JUNGLE_CENTER("Kharazi Jungle (Middle Section)", new Location(2816, 2930, 2905, 2883), 0), - KHARAZI_JUNGLE_WEST("Kharazi Jungle (Western Section)", new Location(2756, 2930, 2816, 2883), 0), - MOR_UL_REK_BANK("TzHaar Bank (Inferno)", new Location(2534, 5146, 2547, 5133), 0), - NATURE_ALTAR("Nature Altar", new Location(2841, 3025, 2846, 3020), 0), - SHILO_VILLAGE_NORTH("Shilo Village North", new Location(2817, 3006, 2878, 2973), 0), - SHILO_VILLAGE_SOUTH("Shilo Village South", new Location(2816, 2972, 2879, 2944), 0), - TAI_BWO_WANNAI("Tai Bwo Wannai", new Location(2770, 3105, 2830, 3050), 0), - TZHAAR_BANK("TzHaar Bank (Jad)", new Location(2437, 5184, 2452, 5172), 0), - TZHAAR_EXIT("Tzhaar City Exit", new Location(2471, 5179, 2490, 5162), 0), - TZHAAR_FIGHT_PITS("TzHaar Fight Pit", new Location(2396, 5183, 2403, 5174), 0), - TZHAAR_INNER_SOUTH_EAST("Tzhaar Inner City South-East", new Location(2499, 5112, 2559, 5057), 0), - TZHAAR_INNER_SOUTH_WEST("Tzhaar Inner City South-West", new Location(2444, 5112, 2499, 5058), 0), - - /*- - * Kharidian Desert - * Agility Pyramid - * Al Kharid - * Bandit Camp (Desert) - * Bedabin Camp - * Citharede Abbey - * Duel Arena - * Nardah - * Pollnivneach - * Smoke Dungeon - * Sophanem - * Uzer - */ - AGILITY_PYRAMID("Agility Pyramid", new Location(3334, 2864, 3386, 2819), 0), - AL_KHARID_BANK("Al Kharid Bank", new Location(3265, 3173, 3272, 3161), 0), - AL_KHARID_GATE("Al Kharid Gate", new Location(3263, 3232, 3271, 3223), 0), - AL_KHARID_GLIDER("Al Kharid_Glider", new Location(3276, 3214, 3283, 3209), 0), - AL_KHARID_MINE("Al Kharid Mine", new Location(3295, 3316, 3303, 3278), 0), - AL_KHARID_PALACE("Al Kharid Palace", new Location(3281, 3178, 3304, 3158), 0), - BEDABIN_CAMP("Bedabin Camp", new Location(3157, 3052, 3188, 3019), 0), - CITHAREDE_ABBEY("Citharede Abbey", new Location(3355, 3190, 3425, 3150), 0), - DESERT_BANDIT_CAMP("Desert Bandit Camp", new Location(3154, 2993, 3189, 2963), 0), - DESERT_QUARRY("Desert Granite Quarry", new Location(3156, 2928, 3184, 2897), 0), - DUEL_ARENA("Duel Arena", new Location(3338, 3252, 3391, 3204), 0), // This polygon is deliberately offset - DUEL_ARENA_BANK("Duel Arena Bank", new Location(3379, 3274, 3386, 3265), 0), - DUEL_ARENA_PALM_TREES("Duel Arena Palm Trees", new Location(3340, 3280, 3354, 3264), 0), - DUEL_ARENA_TELEPORT("Duel Arena Tele", new Location(3308, 3246, 3326, 3225), 0), - FIRE_ALTAR("Fire Altar", new Location(3301, 3256, 3307, 3250), 0), - KALPHITE_LAIR("Kalphite Lair Entrance", new Location(3205, 3124, 3253, 3082), 0), - NARDAH_BANK("Nardah Bank", new Location(3417, 2902, 3437, 2883), 0), - NARDAH_ZONE("Nardah", new Location(3397, 2942, 3453, 2882), 0), - POLLNIVNEACH("Pollnivneach", new Location(3331, 2990, 3379, 2945), 0), - POLLNIVNEACH_POH_PORTAL("Pollnivneach POH Portal", new Location(3333, 3008, 3346, 2995), 0), - POLLNIVNEACH_SMOKE_DUNGEON("Pollnivneach Smoke Dungeon", new Location(3199, 9404, 3327, 9345), 0), - POLLNIVNEACH_SMOKE_DUNGEON_ENTRANCE("Pollnivneach Smoke Dungeon Entrance", new Location(3303, 2967, 3314, 2955), 0), - RUINS_OF_UZER("Uzer", new Location(3463, 3114, 3506, 3075), 0), - SHANTAY_PASS("Shantay Pass", new Location(3293, 3137, 3312, 3116), 0), - SOPHANEM("Sophanem", new Location(3272, 2811, 3324, 2751), 0), - - /*- - * Misthalin - * Barbarian Village - * Digsite - * Draynor Village - * Edgeville - * Grand Exchange - * Lumbridge - * Lumbridge Swamp - * Paterdomus - * Silvarea - * Varrock - * Wizards' Tower - */ - BARB_VILLAGE("Barb Village", new Location(3071, 3448, 3092, 3405), 0), - COOKS_GUILD("Cooks Guild", new Location(3135, 3455, 3155, 3427), 0), - CHAMPIONS_GUILD("Champoins' Guild", new Location(3184, 3364, 3199, 3348), 0), - DARK_WIZARDS("Varrock Dark Wizards", new Location(3220, 3377, 3235, 3361), 0), - DIGSITE("Digsite", new Location(3340, 3435, 3380, 3390), 0), - DIGSITE_EXAM_CENTER("Digsite Exam Center", new Location(3357, 3339, 3367, 3331), 0), - DRAYNOR_MANOR("Draynor Manor", new Location(3089, 3375, 3127, 3350), 0), - DRAYNOR_SEWERS("Draynor Sewers", new Location(3077, 9699, 3135, 9642), 0), - DRYANOR_VILLAGE("Dryanor Village", new Location(3074, 3283, 3112, 3241), 0), - EDGEVILLE_BANK("Edge Bank", new Location(3090, 3499, 3099, 3487), 0), - EDGEVILLE_DUNGEON("Edgeville Dungeon - Main Corridor (Paddewwa Tele)", new Location(3091, 9890, 3105, 9866), 0), - EDGEVILLE_DUNGEON_HILLGIANTS("Varrock Underground - Hill Giants", new Location(3095, 9854, 3125, 9821), 0), - EDGEVILLE_DUNGEON_HOB_GOBLINS("Edgeville Dungeon - Hob Goblins", new Location(3115, 9880, 3143, 9857), 0), - EDGEVILLE_DUNGEON_SLAYER_MASTER("Edgeville Dungeon - Slayer Master", new Location(3128, 9917, 3151, 9881), 0), - GRAND_EXCHANGE("Grand Exchange", new Location(3155, 3499, 3174, 3480), 0), - GRAND_EXCHANGE_AGILITY_SHORTCUT("GE Agility Shortcut", new Location(3136, 3518, 3143, 3511), 0), - GRAND_EXCHANGE_ENTRANCE("GE Entrance", new Location(3159, 3472, 3170, 3460), 0), - HAM_DUNGEON("H.A.M. Dungeon", new Location(3138, 9660, 3191, 9604), 0), - HAM_ENTRANCE("H.A.M. Hideout", new Location(3159, 3254, 3172, 3243), 0), - LUMBERYARD("Lumberyard", new Location(3289, 3520, 3327, 3488), 0), - LUMBRIDGE_BASEMENT("Lumbridge Basement", new Location(3206, 9626, 3221, 9613), 0), - LUMBRIDGE_CASTLE("Lumbridge Castle", new Location(3201, 3235, 3225, 3201), 0), - LUMBRIDGE_SWAMP("Lumby Swamp", new Location(3135, 3203, 3245, 3140), 0), - LUMBRIDGE_SWAMP_CAVES("Lumbridge Swamp Caves", new Location(3142, 9598, 3260, 9537), 0), - PATERDOMUS("Priest in Peril Temple", new Location(3404, 3495, 3419, 3481), 0), - SENNTISTEN_TELEPORT("Senntisten Tele", new Location(3305, 3342, 3319, 3328), 0), - SILVAREA("Rag and Bone Man", new Location(3350, 3505, 3378, 3492), 0), - STRONGHOLD_OF_SECURITY_FLOOR_1("Stronghold of Security - Floor 1 (Minatours)", new Location(1855, 5246, 1917, 5183), 0), - STRONGHOLD_OF_SECURITY_FLOOR_2("Stronghold of Security - Floor 2 (Flesh Crawlers)", new Location(1983, 5246, 2049, 5183), 0), - STRONGHOLD_OF_SECURITY_FLOOR_3("Stronghold of Security - Floor 3 (Catablepons)", new Location(2113, 5310, 2178, 5248), 0), - STRONGHOLD_OF_SECURITY_FLOOR_4("Stronghold of Security - Floor 4 (Ankous)", new Location(2302, 5249, 2367, 5185), 0), - VARROCK_CHURCH("Varrock Church", new Location(3249, 3488, 3259, 3471), 0), - VARROCK_BANK_EAST("Varrock East Bank", new Location(3246, 3428, 3261, 3412), 0), - VARROCK_BANK_WEST("Varrock West Bank", new Location(3172, 3450, 3197, 3425), 0), - VARROCK_MAGIC_SHOP("Varrock Magic Shop", new Location(3249, 3405, 3256, 3398), 0), - VARROCK_MINE("Varrock Mine", new Location(3278, 3372, 3294, 3355), 0), - VARROCK_MOSS_GIANTS("Varrock Sewers - Moss Giants", new Location(3190, 9910, 3153, 9876), 0), - VARROCK_MUSEUM("Varrock Museum", new Location(3249, 3455, 3267, 3442), 0), - VARROCK_PALACE("Varrock Palace", new Location(3198, 3502, 3228, 3455), 0), - VARROCK_SEWERS("Varrock Sewers", new Location(3200, 9918, 3285, 9857), 0), - VARROCK_SQUARE("Varrock Square", new Location(3201, 3444, 3229, 3412), 0), - WIZARDS_TOWER("Wizards Tower", new Location(3093, 3171, 3121, 3146), 0), - - /*- - * Morytania - * Abandoned Mine - * Barrows - * Burgh de Rott - * Canifis - * Darkmeyer - * Fenkenstrain's Castle - * Hallowvale - * Haunted Woods - * Meiyerditch - * Mort'ton - * Mort Myre Swamp - * Port Phasmatys - * Slepe - * The Sisterhood Sanctuary (Nightmare Dungeon) - */ - ABANDONED_MINE("Haunted Mine", new Location(3426, 3260, 3459, 3205), 0), - BARROWS("Barrows", new Location(3546, 3314, 3583, 3268), 0), - BARROWS_CRYPT("Barrows Crypt", new Location(3523, 9723, 3580, 9666), 0), - BURGH_DE_ROTT("Burgh de Rott", new Location(3474, 3247, 3535, 3189), 0), - CANIFIS_BANK("Canifis Bank", new Location(3508, 3483, 3516, 3474), 0), - CANIFIS_ZONE("Canifis", new Location(3472, 3506, 3519, 3467), 0), - CROMBWICK_MANOR("Crombwick Manor in Slepe", new Location(3710, 3377, 3742, 3341), 0), - DARKMEYER_BANK("Darkmeyer Bank", new Location(3600, 3370, 3610, 3364), 0), - DARKMEYER_ZONE("Darkmeyer", new Location(3592, 3392, 3662, 3331), 0), - FENKENSTRAINS_CASTLE("Fenkenstrain's Castle", new Location(3533, 3568, 3564, 3534), 0), - ECTOFUNTUS("Ectofuntus", new Location(3651, 3528, 3668, 3510), 0), - HALLOWED_SEPULCHER_ENTRANCE("Hallowed Sepulcher Entrance", new Location(3649, 3389, 3659, 3379), 0), - HALLOWED_SEPULCHER_LOBBY("Hallowed Sepulcher Lobby", new Location(2383, 5996, 2417, 5963), 0), - MORT_TON("Mort'ton", new Location(3473, 3301, 3504, 3271), 0), - MORYTANIA_FARM_PATCH("Morytania Farming Patch", new Location(3596, 3531, 3607, 3520), 0), - MORYTANIA_SWAMP_NORTH("Northern half of Morytania Swamp", new Location(3412, 3450, 3481, 3410), 0), - MORYTANIA_SWAMP_SOUTH("Southern half of Morytania Swamp", new Location(3412, 3410, 3481, 3370), 0), - NATURE_GROTTO("Nature Grotto", new Location(3410, 3356, 3461, 3322), 0), - NIGHTMARE_BOSS("The Nightmare", new Location(3798, 9769, 3818, 9749), 1), - PORT_PHASMATYS_BANK("Port Phasmatys Bank", new Location(3686, 3471, 3699, 3461), 0), - PORT_PHASMATYS_DOCKS("Port Phasmatys Docks", new Location(3689, 3512, 3711, 3481), 0), - PORT_PHASMATYS_PUB("Port Phasmatys Pub", new Location(3671, 3499, 3681, 3489), 0), - PORT_PHASMATYS_SOUTH_GATE("Port Phasmatys South Gate", new Location(3663, 3455, 3674, 3445), 0), - SALVE_GRAVEYARD("Salve Graveyard", new Location(3425, 3468, 3438, 3457), 0), - SISTERHOOD_SANCTUARY("Sisterhood Sanctuary (Slepe Dungeon)", new Location(3720, 9832, 3898, 9690), 1), - SLAYER_TOWER("Slayer Tower", new Location(3403, 3579, 3454, 3530), 0), - SLEPE("Slepe", new Location(3692, 3381, 3750, 3293), 0), - SWAMP_LIZARDS("Swamp Lizards", new Location(3521, 3451, 3568, 3426), 0), - VER_SINHAZA("ToB", new Location(3640, 3236, 3685, 3202), 0), - - /*- - * Tirannwn - * Arandar - * Gwenith - * Iowerth Dungeon - * Isafdar - * Lletya - * Mynydd - * Poison Waste - * Port Tyras - * Prifddinas - * Tyras Camp - * Zul-Andra - */ - LLETYA("Lletya", new Location(2312, 3196, 2362, 3145), 0), - ELF_CAMP("Elf Camp", new Location(2212, 3265, 2182, 3237), 0), - GWENTIH("Gwenith", new Location(2187, 3425, 2220, 3393), 0), - PRIFDDINAS("Prifddinas", new Location(3221, 6056, 3241, 6039), 0), // Fallback if there are gaps - PRIFDDINAS_BANK_NORTH("Prifddinas North Bank", new Location(3254, 6113, 3260, 6101), 0), - PRIFDDINAS_BANK_SOUTH("Prifddinas South Bank", new Location(3288, 6067, 3304, 6052), 0), - PRIFDDINAS_CITY_CENTER("Prifddinas Center", new Location(3246, 6100, 3281, 6065), 0), - PRIFDDINAS_CITY_E("Eastern Part of Prifddinas", new Location(3282, 6100, 3305, 6065), 0), - PRIFDDINAS_CITY_N("Northern Part of Prifddinas", new Location(3246, 6124, 3281, 6101), 0), - PRIFDDINAS_CITY_NE("North-Eastern Prifddinas", new Location(3282, 6136, 3319, 6101), 0), - PRIFDDINAS_CITY_NW("North-Western Prifddinas", new Location(3208, 6135, 3245, 6101), 0), - PRIFDDINAS_CITY_S("Southern Part of Prifddinas", new Location(3246, 6064, 3281, 6040), 0), - PRIFDDINAS_CITY_SE("South-Eastern Prifddinas", new Location(3282, 6039, 3321, 6023), 0), - PRIFDDINAS_CITY_SW("South-Western Prifddinas", new Location(3207, 6064, 3245, 6023), 0), - PRIFDDINAS_CITY_W("Western Part of Prifddinas", new Location(3222, 6100, 3245, 6065), 0), - PRIFDDINAS_GATE_EAST("Prifddinas East Gate (Arandar / Elven Pass)", new Location(2297, 3334, 2323, 3305), 0), - PRIFDDINAS_GATE_EAST_INSIDE("Prifddinas East Gate", new Location(3306, 6100, 3319, 6064), 0), - PRIFDDINAS_GATE_NORTH("Prifddinas North Gate", new Location(2230, 3387, 2249, 3384), 0), - PRIFDDINAS_GATE_NORTH_INSIDE("Prifddinas North Gate", new Location(3246, 6136, 3281, 6125), 0), - PRIFDDINAS_GATE_SOUTH("Prifddinas South Gate", new Location(2229, 3270, 2252, 3251), 0), - PRIFDDINAS_GATE_SOUTH_INSIDE("Prifddinas South Gate", new Location(3246, 6039, 3281, 6024), 0), - PRIFDDINAS_GATE_WEST("Prifddinas West Gate (Docks)", new Location(2154, 3338, 2182, 3317), 0), - PRIFDDINAS_GATE_WEST_INSIDE("Prifddinas West Gate", new Location(3207, 6100, 3221, 6065), 0), - PRIFDDINAS_GAUNTLET_PORTAL("Prifddinas Gauntlet Portal", new Location(3224, 6112, 3243, 6087), 0), - PRIFDDINAS_POH_PORTAL("Prifddinas POH Portal", new Location(3230, 6081, 3247, 6067), 0), - PRIFDDINAS_RED_CHINS("Prifddinas Red Chins", new Location(2255, 3418, 2283, 3397), 0), - PRIFDDINAS_SLAYER_CAVE_ENTRANCE("Prifddinas Slayer Cave Entrance", new Location(3221, 6056, 3241, 6039), 0), - PRIFDDINAS_ZALCANO_ENTRANCE("Prifddinas Zalcano Entrance", new Location(3277, 6065, 3287, 6053), 0), - TYRAS_CAMP("Tyras Camp", new Location(2168, 3163, 2201, 3134), 0), - TYRAS_DOCKS("Port Tyras", new Location(2135, 3133, 2167, 3110), 0), - ZALCANO("Zalcano", new Location(3019, 6074, 3048, 6035), 0), - GAUNTLET_LOBBY("Gauntlet Lobby", new Location(3025, 6130, 3040, 6115), 1), - ZUL_ANDRA("Zul-Andra", new Location(2182, 3070, 2214, 3042), 0), - - /*- - * Troll Country - * Death Plateau - * God Wars Dungeon - * Ice Path - * Troll Stronghold - * Trollheim - * Trollweiss Mountain - * Weiss - */ - DEATH_PLATEAU("Death Plateau", new Location(2838, 3610, 2880, 3580), 0), - GOD_WARS_DUNGEON("GWD", new Location(2820, 5375, 2944, 5253), 2), - GOD_WARS_DUNGEON_ENTRANCE("GWD Entrance", new Location(2904, 3756, 2921, 3742), 0), - TROLL_STRONGHOLD("Troll Stronghold", new Location(2836, 3698, 2862, 3659), 0), - TROLLHEIM_TELEPORT("Trollheim Tele", new Location(2882, 3685, 2899, 3669), 0), - WEISS("Weiss", new Location(2837, 3967, 2890, 3914), 0), - - /* - * Dungeons, Caves, Islands and other miscellaneous areas - */ - ABYSS("Abyss", new Location(3010, 4862, 3068, 4804), 0), - ABYSSAL_AREA("Abyssal Area", new Location(3008, 4926, 3071, 4864), 0), - ABYSSAL_NEXUS("Abyssal Nexus", new Location(3010, 4803, 3068, 4756), 0), - BLAST_FURNACE("Blast Furnace", new Location(1934, 4974, 1958, 4955), 0), - CAVE_HORROR_ENTRANCE("Mos Le'Harmless Cave Entrance (Cave Horrors)", new Location(3737, 2986, 3759, 2961), 0), - COSMIC_ALTAR("Zanaris Cosmic Altar", new Location(2400, 4387, 2425, 4367), 0), - DWARVEN_MINE_CAMP("Dwarven Mine - North Exit", new Location(3013, 9854, 3033, 9820), 0), - DWARVEN_MINE_CART("Dwarven Mine - Cart Transport", new Location(2988, 9849, 3006, 9821), 0), - DWARVEN_MINE_FALADOR("Dwarven Mine - Falador Exit", new Location(3030, 9788, 3062, 9758), 0), - GORAK_PLANE("Gorak Plane", new Location(3006, 5377, 3070, 5313), 0), - FISHER_REALM("Fisher Realm (Fairy Ring BJR)", new Location(2622, 4738, 2688, 4667), 0), - HARMONY("Harmony Island", new Location(3778, 2879, 3835, 2816), 0), - MINING_GUILD("Mining Guild", new Location(3008, 9756, 3061, 9698), 0), - MOLE_LAIR("Mole Lair", new Location(1730, 5246, 1787, 5131), 0), - MOS_LE_HARMLESS("Mos Le'Harmless", new Location(3649, 3005, 3709, 2958), 0), - MOS_LE_HARMLESS_DOCKS("Mos Le'Harmless Docks", new Location(3664, 2957, 3692, 2929), 0), - MOTHERLODE_MINE("Motherlode Mine", new Location(3713, 5695, 3777, 5632), 0), - PEST_CONTROL("Pest Control", new Location(2630, 2679, 2682, 2627), 0), - PURO_PURO("Puro-Puro", new Location(2561, 4349, 2622, 4289), 0), - SORCERESS_GARDEN("Sorceress's Garden", new Location(2884, 5499, 2938, 5444), 0), - TROUBLE_BREWING("Trouble Brewing", new Location(3774, 3024, 3843, 2942), 0), - ZANARIS_BANK("Zanaris Bank", new Location(2374, 4468, 2390, 4451), 0), - ZANARIS("Zanaris", new Location(2398, 4478, 2460, 4419), 0), - - /* - * Wilderness Locations - */ - ANNAKARL_TELEPORT("GDZ", new Location(3279, 3895, 3296, 3875), 0), - AXE_HUT("Axe Hut", new Location(3187, 3962, 3194, 3957), 0), - BANDIT_CAMP("Bandit Camp", new Location(3017, 3712, 3059, 3681), 0), - BLACK_SALAMANDERS("Black Salamanders", new Location(3291, 3677, 3301, 3664), 0), - CALLISTO("Callisto", new Location(3266, 3863, 3315, 3827), 0), - CEMETERY("Cemetery", new Location(2956, 3767, 2996, 3736), 0), - CHAOS_ALTAR_PRAYER("Chaos Altar", new Location(2945, 3826, 2970, 3813), 0), - CHAOS_ALTAR_RUNECRAFT("Chaos Runecrafting Altar", new Location(3055, 3596, 3067, 3585), 0), - CHAOS_FANATIC("Chaos Fanatic", new Location(2971, 3854, 2992, 3834), 0), - CHAOS_TEMPLE("Chaos Temple", new Location(3220, 3632, 3255, 3593), 0), - BLACK_CHINCHOMPAS("Chins", new Location(3128, 3792, 3160, 3754), 0), - CORP_CAVE("Corp Cave", new Location(3201, 3684, 3219, 3672), 0), - CRAZY_ARCHAEOLOGIST("Crazy Archaeologist", new Location(2952, 3709, 2985, 3678), 0), - DARK_CRAB_TELEPORT("Dark Crab Tele", new Location(3343, 3800, 3355, 3780), 0), - DARK_WARRIORS("Dark Warriors", new Location(3014, 3648, 3046, 3616), 0), - DEEP_WILDERNESS_DUNGEON("Deep Wilderness Dungeon", new Location(3038, 10330, 3053, 10305), 0), - DEEP_WILDERNESS_DUNGEON_ENTRANCE("Deep Wild Dungeon", new Location(3042, 3929, 3051, 3920), 0), - DEEP_WILDERNESS_DUNGEON_FIRE_GIANTS("Deep Wilderness Dungeon Fire Giants", new Location(3035, 10349, 3060, 10331), 0), - DEEP_WILDERNESS_DUNGEON_WINES("Deep Wilderness Dungeon Wines", new Location(3013, 10365, 3060, 10350), 0), - DWARVES("Dwarves", new Location(3230, 3805, 3264, 3779), 0), - EDGEVILLE_DUNGEON_EARTH_WARRIORS("Edgeville Dungeon - Earth Warriors", new Location(3114, 9999, 3129, 9960), 0), - EDGEVILLE_DUNGEON_CHAOS_DRUIDS("Edgeville Dungeon - Chaos Druids", new Location(3104, 9944, 3135, 9923), 0), - EDGEVILLE_DUNGEON_SPIDERS("Edgeville Dungeon - Spiders", new Location(3104, 9959, 3135, 9945), 0), - EDGEVILLE_DUNGEON_BLACK_DEMONS("Edgeville Dungeon - Black Demons", new Location(3077, 9966, 3103, 9941), 0), - // is this label description intuitive? - ENTS("Ents", new Location(3300, 3627, 3320, 3584), 0), - FEROX_ENCLAVE("Ferox Enclave", new Location(3119, 3646, 3160, 3616), 0), - GLORY_HILL("Glory Hill", new Location(3331, 3890, 3348, 3866), 0), - GLORY_HOLE("Glory Hole", new Location(3352, 3897, 3386, 3869), 0), - CARRALLANGAR_GRAVES("Graves", new Location(3128, 3686, 3181, 3658), 0), - GREEN_DRAGONS_EAST("East Drags", new Location(3326, 3704, 3365, 3671), 0), - GREEN_DRAGONS_GRAVEYARD("Graveyard Drags", new Location(3129, 3717, 3172, 3691), 0), - GREEN_DRAGONS_WEST("West Drags", new Location(2960, 3627, 2992, 3598), 0), - HOBGOBLINS("Hobgoblins", new Location(3073, 3775, 3104, 3745), 0), - ICE_GATE("Ice Gate", new Location(2945, 3913, 2978, 3878), 0), - ICE_ROCK("Ice Rock", new Location(2957, 3942, 2984, 3929), 0), - KBD_CAGE("KBD CAGE", new Location(3007, 3855, 3021, 3839), 0), - LAVA_DRAGON_GAP("Gap", new Location(3238, 3855, 3258, 3841), 0), - LAVA_DRAGON_ISLE("Lava Drags", new Location(3175, 3857, 3221, 3805), 0), - LAVA_MAZE_DUNGEON("Lava Maze Dungeon", new Location(3075, 10239, 3008, 10291), 0), - LAVA_MAZE_TELE("Lava Maze Tele", new Location(3019, 3842, 3044, 3812), 0), - MAGE_ARENA("Mage Arena", new Location(3088, 3949, 3123, 3919), 0), - MAGE_BANK("Mage Bank", new Location(3082, 3960, 3103, 3952), 0), - MAGE_BANK_SAFE_ZONE("Mage Bank Safe Zone", new Location(2526, 4727, 2550, 4707), 0), - NEW_GATE("New Gate", new Location(3348, 3890, 3325, 3911), 0), - OBELISK_13("13s Port", new Location(3152, 3624, 3160, 3616), 0), - OBELISK_19("19s", new Location(3220, 3672, 3234, 3660), 0), -// OBELISK_27("27 GWDs Portal", new Location(3031, 3736, 3039, 3728), 0), - OBELISK_35("36 Port", new Location(3097, 3804, 3115, 3785), 0), - OBELISK_44("44s", new Location(2973, 3870, 2987, 3859), 0), - OBELISK_50("50 ports", new Location(3301, 3923, 3315, 3909), 0), - OLD_GATE("Old Gate", new Location(3211, 3906, 3238, 3882), 0), - PIRATE_HUT("Pirate Hut", new Location(3037, 3959, 3045, 3948), 0), - POISON_SPIDERS("Poison Spiders", new Location(3282, 3803, 3302, 3785), 0), - REV_CAVE_AGILITY_65("Rev Cave Green Dragon Agility Jump", new Location(3216, 10090, 3226, 10080), 0), - REV_CAVE_AGILITY_75_1("Rev Cave 75 Agility Jump", new Location(3195, 10200, 3212, 10190), 0), - REV_CAVE_AGILITY_75_2("Rev Cave 75 Agility Jump (North of Ankous)", new Location(3173, 10214, 3186, 10205), 0), - REV_CAVE_AGILITY_89("Rev Cave 89 Agility Jump", new Location(3233, 10148, 3244, 10140), 0), - REV_CAVE_ANKOUS("Rev Cave Ankous", new Location(3160, 10204, 3191, 10177), 0), - REV_CAVE_BLACK_DEMONS("Rev Cave Black Demons", new Location(3158, 10171, 3187, 10145), 0), - REV_CAVE_BLACK_DRAGS("Rev Cave Black Drags", new Location(3223, 10216, 3254, 10190), 0), - REV_CAVE_CORRIDOR_NORTH("Revenant Cave Corridor", new Location(3255, 10213, 3263, 10191), 0), - REV_CAVE_CORRIDOR_SOUTH("Rev Cave Green Dragon Corridor", new Location(3238, 10106, 3252, 10077), 0), - REV_CAVE_ENTRANCE_NORTH("Rev Entrance", new Location(3118, 3837, 3142, 3818), 0), - REV_CAVE_ENTRANCE_SOUTH("South Rev Entrance", new Location(3071, 3660, 3092, 3645), 0), - REV_CAVE_EXIT_NORTH("Rev Cave North Exit", new Location(3238, 10236, 3243, 10231), 0), - REV_CAVE_EXIT_SOUTH("Rev Cave South Exit", new Location(3190, 10062, 3215, 10052), 0), - REV_CAVE_GREATER_DEMONS("Rev Cave Greater Demons", new Location(3210, 10140, 3240, 10115), 0), - REV_CAVE_GREEN_DRAGONS_1("Rev Cave Green Dragons", new Location(3215, 10078, 3234, 10052), 0), - REV_CAVE_GREEN_DRAGONS_2("Rev Cave Green Dragons", new Location(3200, 10106, 3231, 10091), 0), - REV_CAVE_HELL_HOUNDS("Rev Cave Hell Hounds", new Location(3190, 10078, 3210, 10063), 0), - REV_CAVE_ICE_GIANTS("Rev Cave Ice Giants", new Location(3200, 10173, 3221, 10155), 0), - REV_CAVE_LESSER_DEMONS("Rev Cave Lesser Demons", new Location(3143, 10125, 3176, 10104), 0), - REVENANT_DARK_BEAST("Revenant Dark Beast", new Location(3244, 10154, 3260, 10136), 0), - REVENANT_MAIN_CHAMBER("Main Rev Chamber", new Location(3227, 10187, 3261, 10157), 0), - ROGUE_CASTLE("Rogue Castle", new Location(3275, 3947, 3299, 3920), 0), - RUNE_ROCKS("Rune Rocks", new Location(3055, 3890, 3072, 3876), 0), - SCORPIA("Scorpia", new Location(3216, 3949, 3248, 3935), 0), - SPERM_HILL("Sperm Hill", new Location(3282, 3687, 3300, 3677), 0), - SPIDER_HILL("Spider Hill", new Location(3156, 3896, 3182, 3871), 0), - VENENATIS("Venenatis", new Location(3298, 3759, 3353, 3722), 0), - VETTION("Vet'tion", new Location(3183, 3796, 3227, 3765), 0), - VOLCANO("Volcano", new Location(3345, 3957, 3390, 3916), 0), - WEB("Web", new Location(3153, 3961, 3163, 3948), 0), - WILDY_AGILITY_COURSE("Wildy Agility Course", new Location(2988, 3967, 3008, 3906), 0), - WILDERNESS_GOD_WARS_DUNGEON("God Wars Dungeon", new Location(3010, 3745, 3027, 3727), 0), - WILDERNESS_GOD_WARS("Wildy GWD Chamber", new Location(3012, 10168, 3068, 10113), 0), - WILDERNESS_LEVER("Lever", new Location(3149, 3933, 3162, 3917), 0), - WILDERNESS_RESOURCE_AREA("Resource Area", new Location(3174, 3946, 3195, 3923), 0), - ZAMORAK_MAGE("Zammy Mage", new Location(3099, 3561, 3107, 3553), 0); - - @Getter - private final String name; - @Getter - private final WorldArea worldArea; - @Getter - private final Location location; - @Getter - private static final Map LOCATION_MAP; - - static - { - ImmutableMap.Builder builder = ImmutableMap.builder(); - - for (WorldLocation value : values()) - { - builder.put(value.getWorldArea(), value.getName()); - } - - LOCATION_MAP = builder.build(); - } - - /** - * Creates a location used to get the name of a location by a WorldPoint - * - * @param name - The name that is used to represent the area in overlays etc - * @param location - A Location made out of 4 points on the world map - * @param plane - The plane of the World Area - */ - WorldLocation(String name, Location location, int plane) - { - this.name = name; - this.location = location; - this.worldArea = new WorldArea(location.x, location.y, location.width, location.height, plane); - } - - /** - * Returns all locations that aren't in the wild - * - * @return - A Collection of non-wilderness WorldLocations - */ - public static Collection getNonWildernessLocations() - { - return Arrays.stream(WorldLocation.values()).filter(loc -> - PvPUtil.getWildernessLevelFrom(loc.worldArea.toWorldPoint()) < 0).collect(Collectors.toList()); - } - - /** - * Returns only the WorldLocations that are in the wilderness - * - * @return - A Collection of WorldLocations in the wilderness - */ - public static Collection getWildernessLocations() - { - return Arrays.stream(WorldLocation.values()).filter(loc -> - PvPUtil.getWildernessLevelFrom(loc.worldArea.toWorldPoint()) > 0).collect(Collectors.toList()); - } - - /** - * Returns the WorldLocation that a WorldPoint is in, or the closest WorldLocation to the point - * - * @param worldPoint - the WorldPoint to find the WorldLocation of - * @return - Containing location or closest location if it isn't in any - */ - public static String location(WorldPoint worldPoint) - { - int dist = 128; // x2 Region lengths - String s = ""; - WorldArea closestArea = null; - - for (Map.Entry entry : LOCATION_MAP.entrySet()) - { - final WorldArea worldArea = entry.getKey(); - - if (worldArea.toWorldPointList().contains(worldPoint)) - { - s = entry.getValue(); - return s; - } - - final int distTo = worldArea.distanceTo(worldPoint); - - if (distTo < dist) - { - dist = distTo; - closestArea = worldArea; - } - } - - if (closestArea == null) - { - return s; - } - - if (worldPoint.getY() > closestArea.toWorldPoint().getY() + closestArea.getHeight()) - { - s = s + "N"; - } - - if (worldPoint.getY() < closestArea.toWorldPoint().getY()) - { - s = s + "S"; - } - - if (worldPoint.getX() < closestArea.toWorldPoint().getX()) - { - s = s + "W"; - } - - if (worldPoint.getX() > (closestArea.toWorldPoint().getX() + closestArea.getWidth())) - { - s = s + "E"; - } - - s = s + " of "; - s = s + LOCATION_MAP.get(closestArea); - - if (s.startsWith(" of ")) - { - s = s.substring(3); - } - - return s; - } - - - public static class Location - { - @Getter - private final int x; - @Getter - private final int y; - @Getter - private final int x1; - @Getter - private final int y1; - final int width; - final int height; - - Location(int x, int y, int x1, int y1) - { - this.x = Math.min(x, x1); - this.y = Math.min(y, y1); - this.x1 = Math.max(x, x1); - this.y1 = Math.max(y, y1); - this.width = Math.abs(x1 - x); - this.height = Math.abs(y1 - y); - } - - @Override - public String toString() - { - return "Location{" + - "x=" + x + - ", y=" + y + - ", width=" + width + - ", height=" + height + - '}'; - } - } - - @Override - public String toString() - { - return "WorldLocation{" + - "name='" + name + '\'' + - ", worldArea=" + worldArea + - '}'; - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/game/WorldService.java b/runelite-client/src/main/java/net/runelite/client/game/WorldService.java index 7fb9f1992d..4cc102016d 100644 --- a/runelite-client/src/main/java/net/runelite/client/game/WorldService.java +++ b/runelite-client/src/main/java/net/runelite/client/game/WorldService.java @@ -60,10 +60,7 @@ public class WorldService private WorldResult worlds; @Inject - private WorldService( - Client client, - ScheduledExecutorService scheduledExecutorService, - OkHttpClient okHttpClient, + private WorldService(Client client, ScheduledExecutorService scheduledExecutorService, OkHttpClient okHttpClient, EventBus eventBus) { this.client = client; @@ -98,7 +95,7 @@ public class WorldService WorldResult worldResult = worldClient.lookupWorlds(); worldResult.getWorlds().sort(Comparator.comparingInt(World::getId)); worlds = worldResult; - eventBus.post(WorldsFetch.class, new WorldsFetch(worldResult)); + eventBus.post(new WorldsFetch(worldResult)); } catch (IOException ex) { @@ -128,4 +125,4 @@ public class WorldService return worlds; } -} \ No newline at end of file +} diff --git a/runelite-client/src/main/java/net/runelite/client/game/XpDropEvent.java b/runelite-client/src/main/java/net/runelite/client/game/XpDropEvent.java deleted file mode 100644 index 6f1f36e2a1..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/game/XpDropEvent.java +++ /dev/null @@ -1,12 +0,0 @@ -package net.runelite.client.game; - -import lombok.Data; -import net.runelite.api.Skill; -import net.runelite.api.events.Event; - -@Data -public class XpDropEvent implements Event -{ - private Skill skill; - private int exp; -} diff --git a/runelite-client/src/main/java/net/runelite/client/game/XpDropManager.java b/runelite-client/src/main/java/net/runelite/client/game/XpDropManager.java deleted file mode 100644 index 36055ae9a4..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/game/XpDropManager.java +++ /dev/null @@ -1,59 +0,0 @@ -package net.runelite.client.game; - -import java.util.EnumMap; -import java.util.Map; -import javax.inject.Inject; -import javax.inject.Singleton; -import lombok.AccessLevel; -import lombok.Getter; -import net.runelite.api.Client; -import net.runelite.api.Skill; -import net.runelite.api.events.GameStateChanged; -import net.runelite.api.events.StatChanged; -import net.runelite.client.eventbus.EventBus; - -@Singleton -public class XpDropManager -{ - - private final Map previousSkillExpTable = new EnumMap<>(Skill.class); - @Getter(AccessLevel.PACKAGE) - private int damage = 0; - @Getter(AccessLevel.PACKAGE) - private int tickShow = 0; - private final Client client; - private final EventBus eventBus; - - @Inject - private XpDropManager( - final EventBus eventBus, - final Client client - ) - { - this.client = client; - this.eventBus = eventBus; - eventBus.subscribe(GameStateChanged.class, this, this::onGameStateChanged); - eventBus.subscribe(StatChanged.class, this, this::onStatChanged); - } - - private void onGameStateChanged(GameStateChanged event) - { - damage = 0; - tickShow = 0; - } - - private void onStatChanged(StatChanged event) - { - final Skill skill = event.getSkill(); - final int xp = client.getSkillExperience(skill); - Integer previous = previousSkillExpTable.put(skill, xp); - if (previous != null) - { - int previousExpGained = xp - previous; - XpDropEvent xpDropEvent = new XpDropEvent(); - xpDropEvent.setExp(previousExpGained); - xpDropEvent.setSkill(skill); - eventBus.post(XpDropEvent.class, xpDropEvent); - } - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/game/chatbox/ChatboxItemSearch.java b/runelite-client/src/main/java/net/runelite/client/game/chatbox/ChatboxItemSearch.java index 494373b0ff..52fcf92cd8 100644 --- a/runelite-client/src/main/java/net/runelite/client/game/chatbox/ChatboxItemSearch.java +++ b/runelite-client/src/main/java/net/runelite/client/game/chatbox/ChatboxItemSearch.java @@ -37,7 +37,7 @@ import javax.inject.Singleton; import lombok.Getter; import lombok.Value; import net.runelite.api.Client; -import net.runelite.api.ItemDefinition; +import net.runelite.api.ItemComposition; import net.runelite.api.widgets.ItemQuantityMode; import net.runelite.api.widgets.JavaScriptCallback; import net.runelite.api.widgets.Widget; @@ -63,7 +63,7 @@ public class ChatboxItemSearch extends ChatboxTextInput private final ItemManager itemManager; private final Client client; - private final Map results = new LinkedHashMap<>(); + private final Map results = new LinkedHashMap<>(); private String tooltipText; private int index = -1; @@ -80,7 +80,7 @@ public class ChatboxItemSearch extends ChatboxTextInput @Inject private ChatboxItemSearch(ChatboxPanelManager chatboxPanelManager, ClientThread clientThread, - ItemManager itemManager, Client client) + ItemManager itemManager, Client client) { super(chatboxPanelManager, clientThread); this.chatboxPanelManager = chatboxPanelManager; @@ -133,7 +133,7 @@ public class ChatboxItemSearch extends ChatboxTextInput int x = PADDING; int y = PADDING * 3; int idx = 0; - for (ItemDefinition itemDefinition : results.values()) + for (ItemComposition itemComposition : results.values()) { Widget item = container.createChild(-1, WidgetType.GRAPHIC); item.setXPositionMode(WidgetPositionMode.ABSOLUTE_LEFT); @@ -142,8 +142,8 @@ public class ChatboxItemSearch extends ChatboxTextInput item.setOriginalY(y + FONT_SIZE * 2); item.setOriginalHeight(ICON_HEIGHT); item.setOriginalWidth(ICON_WIDTH); - item.setName(JagexColors.MENU_TARGET_TAG + itemDefinition.getName()); - item.setItemId(itemDefinition.getId()); + item.setName(JagexColors.MENU_TARGET_TAG + itemComposition.getName()); + item.setItemId(itemComposition.getId()); item.setItemQuantity(10000); item.setItemQuantityMode(ItemQuantityMode.NEVER); item.setBorderType(1); @@ -164,7 +164,7 @@ public class ChatboxItemSearch extends ChatboxTextInput { if (onItemSelected != null) { - onItemSelected.accept(itemDefinition.getId()); + onItemSelected.accept(itemComposition.getId()); } chatboxPanelManager.close(); @@ -198,7 +198,7 @@ public class ChatboxItemSearch extends ChatboxTextInput { if (onItemSelected != null) { - onItemSelected.accept(results.keySet().toArray(new Integer[0])[index]); + onItemSelected.accept(results.keySet().toArray(new Integer[results.size()])[index]); } chatboxPanelManager.close(); @@ -307,7 +307,7 @@ public class ChatboxItemSearch extends ChatboxTextInput Set itemIcons = new HashSet<>(); for (int i = 0; i < client.getItemCount() && results.size() < MAX_RESULTS; i++) { - ItemDefinition itemComposition = itemManager.getItemDefinition(itemManager.canonicalize(i)); + ItemComposition itemComposition = itemManager.getItemComposition(itemManager.canonicalize(i)); String name = itemComposition.getName().toLowerCase(); // The client assigns "null" to item names of items it doesn't know about diff --git a/runelite-client/src/main/java/net/runelite/client/game/chatbox/ChatboxPanelManager.java b/runelite-client/src/main/java/net/runelite/client/game/chatbox/ChatboxPanelManager.java index a2377e35f2..38bb623b95 100644 --- a/runelite-client/src/main/java/net/runelite/client/game/chatbox/ChatboxPanelManager.java +++ b/runelite-client/src/main/java/net/runelite/client/game/chatbox/ChatboxPanelManager.java @@ -41,6 +41,7 @@ import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetInfo; import net.runelite.client.callback.ClientThread; import net.runelite.client.eventbus.EventBus; +import net.runelite.client.eventbus.Subscribe; import net.runelite.client.input.KeyListener; import net.runelite.client.input.KeyManager; import net.runelite.client.input.MouseListener; @@ -66,8 +67,8 @@ public class ChatboxPanelManager @Inject private ChatboxPanelManager(EventBus eventBus, Client client, ClientThread clientThread, - KeyManager keyManager, MouseManager mouseManager, - Provider chatboxTextMenuInputProvider, Provider chatboxTextInputProvider) + KeyManager keyManager, MouseManager mouseManager, + Provider chatboxTextMenuInputProvider, Provider chatboxTextInputProvider) { this.client = client; this.clientThread = clientThread; @@ -78,10 +79,6 @@ public class ChatboxPanelManager this.chatboxTextMenuInputProvider = chatboxTextMenuInputProvider; this.chatboxTextInputProvider = chatboxTextInputProvider; - - - eventBus.subscribe(ScriptCallbackEvent.class, this, this::onScriptCallbackEvent); - eventBus.subscribe(GameStateChanged.class, this, this::onGameStateChanged); } public void close() @@ -106,7 +103,7 @@ public class ChatboxPanelManager { client.runScript(ScriptID.MESSAGE_LAYER_OPEN, 0); - // eventBus.register(input); + eventBus.register(input); if (input instanceof KeyListener) { keyManager.registerKeyListener((KeyListener) input); @@ -153,7 +150,8 @@ public class ChatboxPanelManager .prompt(prompt); } - private void onScriptCallbackEvent(ScriptCallbackEvent ev) + @Subscribe + public void onScriptCallbackEvent(ScriptCallbackEvent ev) { if (currentInput != null && "resetChatboxInput".equals(ev.getEventName())) { @@ -161,6 +159,7 @@ public class ChatboxPanelManager } } + @Subscribe private void onGameStateChanged(GameStateChanged ev) { if (currentInput != null && ev.getGameState() == GameState.LOGIN_SCREEN) diff --git a/runelite-client/src/main/java/net/runelite/client/game/chatbox/ChatboxTextInput.java b/runelite-client/src/main/java/net/runelite/client/game/chatbox/ChatboxTextInput.java index fe7fefc781..a11aad1e1c 100644 --- a/runelite-client/src/main/java/net/runelite/client/game/chatbox/ChatboxTextInput.java +++ b/runelite-client/src/main/java/net/runelite/client/game/chatbox/ChatboxTextInput.java @@ -25,6 +25,7 @@ package net.runelite.client.game.chatbox; import com.google.common.base.Strings; +import com.google.common.primitives.Ints; import com.google.inject.Inject; import java.awt.Point; import java.awt.Rectangle; @@ -48,7 +49,6 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; import net.runelite.api.FontID; import net.runelite.api.FontTypeFace; -import net.runelite.api.util.Text; import net.runelite.api.widgets.JavaScriptCallback; import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetPositionMode; @@ -58,7 +58,7 @@ import net.runelite.api.widgets.WidgetType; import net.runelite.client.callback.ClientThread; import net.runelite.client.input.KeyListener; import net.runelite.client.input.MouseListener; -import net.runelite.client.util.MiscUtils; +import net.runelite.client.util.Text; @Slf4j public class ChatboxTextInput extends ChatboxInput implements KeyListener, MouseListener @@ -67,7 +67,7 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse private static final Pattern BREAK_MATCHER = Pattern.compile("[^a-zA-Z0-9']"); private final ChatboxPanelManager chatboxPanelManager; - final ClientThread clientThread; + protected final ClientThread clientThread; private static IntPredicate getDefaultCharValidator() { @@ -88,7 +88,7 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse @Getter private int lines; - private StringBuilder value = new StringBuilder(); + private StringBuffer value = new StringBuffer(); @Getter private int cursorStart = 0; @@ -135,7 +135,7 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse return this; } - protected ChatboxTextInput lines(int lines) + public ChatboxTextInput lines(int lines) { this.lines = lines; if (built) @@ -157,7 +157,7 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse public ChatboxTextInput value(String value) { - StringBuilder sb = new StringBuilder(); + StringBuffer sb = new StringBuffer(); for (char c : value.toCharArray()) { if (charValidator.test(c)) @@ -170,12 +170,12 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse return this; } - private ChatboxTextInput cursorAt(int index) + public ChatboxTextInput cursorAt(int index) { return cursorAt(index, index); } - private ChatboxTextInput cursorAt(int indexA, int indexB) + public ChatboxTextInput cursorAt(int indexA, int indexB) { if (indexA < 0) { @@ -377,8 +377,8 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse if (isStartLine || isEndLine || (cursorEnd > line.end && cursorStart < line.start)) { - final int cIdx = MiscUtils.clamp(cursorStart - line.start, 0, len); - final int ceIdx = MiscUtils.clamp(cursorEnd - line.start, 0, len); + final int cIdx = Ints.constrainToRange(cursorStart - line.start, 0, len); + final int ceIdx = Ints.constrainToRange(cursorEnd - line.start, 0, len); lt = Text.escapeJagex(text.substring(0, cIdx)); mt = Text.escapeJagex(text.substring(cIdx, ceIdx)); @@ -480,7 +480,7 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse int cx = p.x - ccl.getX() - ox; int cy = p.y - ccl.getY() - oy; - int currentLine = MiscUtils.clamp(cy / oh, 0, editLines.size() - 1); + int currentLine = Ints.constrainToRange(cy / oh, 0, editLines.size() - 1); final Line line = editLines.get(currentLine); final String tsValue = line.text; @@ -517,7 +517,7 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse break; } - charIndex = MiscUtils.clamp(charIndex, 0, tsValue.length()); + charIndex = Ints.constrainToRange(charIndex, 0, tsValue.length()); return line.start + charIndex; }; @@ -760,6 +760,9 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse newPos++; break; case KeyEvent.VK_UP: + ev.consume(); + newPos = getLineOffset.applyAsInt(code); + break; case KeyEvent.VK_DOWN: ev.consume(); newPos = getLineOffset.applyAsInt(code); diff --git a/runelite-client/src/main/java/net/runelite/client/game/chatbox/ChatboxTextMenuInput.java b/runelite-client/src/main/java/net/runelite/client/game/chatbox/ChatboxTextMenuInput.java index 177df30beb..bc637a6528 100644 --- a/runelite-client/src/main/java/net/runelite/client/game/chatbox/ChatboxTextMenuInput.java +++ b/runelite-client/src/main/java/net/runelite/client/game/chatbox/ChatboxTextMenuInput.java @@ -32,12 +32,12 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.Getter; import net.runelite.api.FontID; +import net.runelite.api.widgets.WidgetType; import net.runelite.api.widgets.JavaScriptCallback; import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetPositionMode; import net.runelite.api.widgets.WidgetSizeMode; import net.runelite.api.widgets.WidgetTextAlignment; -import net.runelite.api.widgets.WidgetType; import net.runelite.client.input.KeyListener; public class ChatboxTextMenuInput extends ChatboxInput implements KeyListener diff --git a/runelite-client/src/main/java/net/runelite/client/graphics/ModelOutlineRenderer.java b/runelite-client/src/main/java/net/runelite/client/graphics/ModelOutlineRenderer.java deleted file mode 100644 index d7605bc4f8..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/graphics/ModelOutlineRenderer.java +++ /dev/null @@ -1,1102 +0,0 @@ -/* - * Copyright (c) 2018, Woox - * 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.graphics; - -import com.google.inject.Inject; -import com.google.inject.Singleton; -import java.awt.Color; -import java.awt.image.BufferedImage; -import java.awt.image.DataBufferInt; -import java.time.temporal.ChronoUnit; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import net.runelite.api.Client; -import net.runelite.api.DecorativeObject; -import net.runelite.api.GameObject; -import net.runelite.api.GroundObject; -import net.runelite.api.MainBufferProvider; -import net.runelite.api.Model; -import net.runelite.api.NPC; -import net.runelite.api.NPCDefinition; -import net.runelite.api.Perspective; -import net.runelite.api.Player; -import net.runelite.api.TileItemPile; -import net.runelite.api.TileObject; -import net.runelite.api.WallObject; -import net.runelite.api.coords.LocalPoint; -import net.runelite.client.task.Schedule; - -@Singleton -public class ModelOutlineRenderer -{ - /* - * This class doesn't really "need" static variables, but they are - * static for performance reasons. Arrays are kept outside methods - * to avoid frequent big allocations. Arrays should mostly be seen - * as ArrayLists. The size of them is increased whenever they need - * to become bigger. - */ - - private final Client client; - - private boolean isReset; - private boolean usedSinceLastCheck; - - // Dimensions of the underlying image - private int imageWidth; - private int imageHeight; - - // Boundaries for the current rasterization - private int clipX1; - private int clipY1; - private int clipX2; - private int clipY2; - - // Pixel points that would be rendered to - private int[] visited; - private int currentVisitedNumber = 0; - - // Transformed vertex positions - private int[] projectedVerticesX; - private int[] projectedVerticesY; - private boolean[] projectedVerticesRenderable; - - // An array of pixel points to raster onto the image. These are checked against - // clip boundaries and the visited array to prevent drawing on top of the model - // and outside the scene area. They are grouped per distance to the closest pixel - // drawn on the model. - private int[][] outlinePixels; - private int[] outlinePixelsLengths; // outlinePixelsLength[i] is the used length of outlinePixels[i] - private int outlineArrayWidth; - - // A list of pixel distances ordered from shortest to longest distance for - // each outline width. These are calculated once upon first usage and then - // stored here to prevent reevaluation. - private List> precomputedDistancePriorities; - - @Inject - private ModelOutlineRenderer(Client client) - { - this.client = client; - - reset(); - } - - @Schedule(period = 5, unit = ChronoUnit.SECONDS) - public void checkUsage() - { - if (!isReset && !usedSinceLastCheck) - { - // Reset memory allocated when the rasterizer becomes inactive - reset(); - } - usedSinceLastCheck = false; - } - - /** - * Reset memory used by the rasterizer - */ - private void reset() - { - visited = new int[0]; - projectedVerticesX = new int[0]; - projectedVerticesY = new int[0]; - projectedVerticesRenderable = new boolean[0]; - outlinePixels = new int[0][]; - outlinePixelsLengths = new int[0]; - precomputedDistancePriorities = new ArrayList<>(0); - isReset = true; - } - - /** - * Calculate the next power of two of a value - * - * @param value The value to find the next power of two of - * @return Returns the next power of two - */ - private static int nextPowerOfTwo(int value) - { - value--; - value |= value >> 1; - value |= value >> 2; - value |= value >> 4; - value |= value >> 8; - value |= value >> 16; - value++; - return value; - } - - /** - * Determine if a triangle goes counter clockwise - * - * @return Returns true if the triangle goes counter clockwise and should be culled, otherwise false - */ - private static boolean cullFace(int x1, int y1, int x2, int y2, int x3, int y3) - { - return - (y2 - y1) * (x3 - x2) - - (x2 - x1) * (y3 - y2) < 0; - } - - /** - * Gets the list of pixel distances ordered by distance from closest pixel for a specific outline width. - * - * @param outlineWidth The outline width - * @return Returns the list of pixel distances - */ - private List getPriorityList(int outlineWidth) - { - while (precomputedDistancePriorities.size() <= outlineWidth) - { - precomputedDistancePriorities.add(null); - } - - // Grab the cached outline width if we have one - if (precomputedDistancePriorities.get(outlineWidth) != null) - { - return precomputedDistancePriorities.get(outlineWidth); - } - - List ps = new ArrayList<>(); - for (int x = 0; x <= outlineWidth; x++) - { - for (int y = 0; y <= outlineWidth; y++) - { - if (x == 0 && y == 0) - { - continue; - } - - double dist = Math.sqrt(x * x + y * y); - if (dist > outlineWidth) - { - continue; - } - - int outerAlpha = outlineWidth == 1 ? 255 // For preventing division by 0 - : (int) (255 * (dist - 1) / (outlineWidth - 1)); - ps.add(new PixelDistanceAlpha(outerAlpha, x + y * outlineArrayWidth)); - } - } - ps.sort(Comparator.comparingDouble(PixelDistanceAlpha::getOuterAlpha)); - precomputedDistancePriorities.set(outlineWidth, ps); - - return ps; - } - - /** - * Checks that the size of outlinePixels is big enough to hold a specific - * amount of elements. This is used to reduce the amount of if checks needed - * when adding elements to outlinePixels. - * - * @param distArrayPos The position in the array - * @param additionalMinimumSize The additional minimum size required - */ - private void ensureMinimumOutlineQueueSize(int distArrayPos, int additionalMinimumSize) - { - int minimumSize = outlinePixelsLengths[distArrayPos] + additionalMinimumSize; - while (outlinePixels[distArrayPos].length < minimumSize) - { - int[] newArr = new int[nextPowerOfTwo(minimumSize)]; - System.arraycopy(outlinePixels[distArrayPos], 0, newArr, 0, - outlinePixels[distArrayPos].length); - outlinePixels[distArrayPos] = newArr; - } - } - - /** - * Resets the visited flag for a specific amount of pixels - * - * @param pixelAmount The amount of pixels to reset - */ - private void resetVisited(int pixelAmount) - { - // The visited array is essentially a boolean array, but by - // making it an int array and checking if visited[i] == currentVisitedNumber - // and changing currentVisitedNumber for every new outline, we can essentially - // reset the whole array without having to iterate over every element - - if (visited.length < pixelAmount) - { - visited = new int[nextPowerOfTwo(pixelAmount)]; - currentVisitedNumber = 0; - } - - currentVisitedNumber++; - } - - /** - * Resets the pixels that are queued for outlining - * - * @param outlineWidth The width of the outline to reset pixels for - */ - private void resetOutline(int outlineWidth) - { - outlineArrayWidth = outlineWidth + 2; - - int arraySizes = outlineArrayWidth * outlineArrayWidth; - if (outlinePixels.length < arraySizes) - { - outlinePixels = new int[arraySizes][]; - outlinePixelsLengths = new int[arraySizes]; - for (int i = 0; i < arraySizes; i++) - { - outlinePixels[i] = new int[4]; - } - } - else - { - for (int i = 0; i < arraySizes; i++) - { - outlinePixelsLengths[i] = 0; - } - } - } - - /** - * Simulates a horizontal line rasterization and adds the pixels to the left - * and to the right to the outline queue if they are within the clip area. - * - * @param pixelPos The pixel position in the line where x == 0 - * @param x1 The starting x position - * @param x2 The ending x position - */ - private void simulateHorizontalLineRasterizationForOutline(int pixelPos, int x1, int x2) - { - if (x2 > clipX2) - { - x2 = clipX2; - } - if (x1 < clipX1) - { - x1 = clipX1; - } - if (x1 >= x2) - { - return; - } - - // Queue the pixel positions to the left and to the right of the line - ensureMinimumOutlineQueueSize(1, 2); - if (x2 < clipX2) - { - outlinePixels[1][outlinePixelsLengths[1]++] = pixelPos + x2; - } - if (x1 > clipX1) - { - outlinePixels[1][outlinePixelsLengths[1]++] = pixelPos + x1 - 1; - } - - // Divide by 4 to account for loop unrolling - int xDist = x2 - x1 >> 2; - pixelPos += x1; - - // This loop could run over 100m times per second without loop unrolling in some cases, - // so unrolling it can give a noticeable performance boost. - while (xDist-- > 0) - { - visited[pixelPos++] = currentVisitedNumber; - visited[pixelPos++] = currentVisitedNumber; - visited[pixelPos++] = currentVisitedNumber; - visited[pixelPos++] = currentVisitedNumber; - } - - // Draw up to 3 more pixels if there were any left - xDist = (x2 - x1) & 3; - while (xDist-- > 0) - { - visited[pixelPos++] = currentVisitedNumber; - } - } - - /** - * Queues the pixel positions above and below two horizontal lines, excluding those - * where the x positions of the lines intersect. - * - * @param pixelPos The pixel position at x == 0 of the second line - * @param x1 The starting x position of the first line - * @param x2 The ending x position of the first line - * @param x3 The starting x position of the second line - * @param x4 The ending x position of the second line - */ - private void outlineAroundHorizontalLine(int pixelPos, int x1, int x2, int x3, int x4) - { - if (x1 < clipX1) - { - x1 = clipX1; - } - if (x2 < clipX1) - { - x2 = clipX1; - } - if (x3 < clipX1) - { - x3 = clipX1; - } - if (x4 < clipX1) - { - x4 = clipX1; - } - - if (x1 > clipX2) - { - x1 = clipX2; - } - if (x2 > clipX2) - { - x2 = clipX2; - } - if (x3 > clipX2) - { - x3 = clipX2; - } - if (x4 > clipX2) - { - x4 = clipX2; - } - - if (x1 < x3) - { - ensureMinimumOutlineQueueSize(outlineArrayWidth, x3 - x1); - for (int x = x1; x < x3; x++) - { - outlinePixels[outlineArrayWidth][outlinePixelsLengths[outlineArrayWidth]++] = pixelPos - imageWidth + x; - } - } - else - { - ensureMinimumOutlineQueueSize(outlineArrayWidth, x1 - x3); - for (int x = x3; x < x1; x++) - { - outlinePixels[outlineArrayWidth][outlinePixelsLengths[outlineArrayWidth]++] = pixelPos + x; - } - } - - if (x2 < x4) - { - ensureMinimumOutlineQueueSize(outlineArrayWidth, x4 - x2); - for (int x = x2; x < x4; x++) - { - outlinePixels[outlineArrayWidth][outlinePixelsLengths[outlineArrayWidth]++] = pixelPos + x; - } - } - else - { - ensureMinimumOutlineQueueSize(outlineArrayWidth, x2 - x4); - for (int x = x4; x < x2; x++) - { - outlinePixels[outlineArrayWidth][outlinePixelsLengths[outlineArrayWidth]++] = pixelPos - imageWidth + x; - } - } - } - - /** - * Simulates rasterization of a triangle and adds every pixel outside the triangle - * to the outline queue. - * - * @param x1 The x position of the first vertex in the triangle - * @param y1 The y position of the first vertex in the triangle - * @param x2 The x position of the second vertex in the triangle - * @param y2 The y position of the second vertex in the triangle - * @param x3 The x position of the third vertex in the triangle - * @param y3 The y position of the third vertex in the triangle - */ - private void simulateTriangleRasterizationForOutline(int x1, int y1, int x2, int y2, int x3, int y3) - { - // Swap vertices so y1 <= y2 <= y3 using bubble sort - if (y1 > y2) - { - int yp = y1; - int xp = x1; - y1 = y2; - y2 = yp; - x1 = x2; - x2 = xp; - } - if (y2 > y3) - { - int yp = y2; - int xp = x2; - y2 = y3; - y3 = yp; - x2 = x3; - x3 = xp; - } - if (y1 > y2) - { - int yp = y1; - int xp = x1; - y1 = y2; - y2 = yp; - x1 = x2; - x2 = xp; - } - - if (y1 > clipY2) - { - // All points are outside clip boundaries - return; - } - - int slope1 = 0; - if (y1 != y2) - { - slope1 = (x2 - x1 << 14) / (y2 - y1); - } - - int slope2 = 0; - if (y3 != y2) - { - slope2 = (x3 - x2 << 14) / (y3 - y2); - } - - int slope3 = 0; - if (y1 != y3) - { - slope3 = (x1 - x3 << 14) / (y1 - y3); - } - - if (y2 > clipY2) - { - y2 = clipY2; - } - if (y3 > clipY2) - { - y3 = clipY2; - } - if (y1 == y3 || y3 < 0) - { - return; - } - - x1 <<= 14; - x2 <<= 14; - x3 = x1; - - if (y1 < 0) - { - x3 -= y1 * slope3; - x1 -= y1 * slope1; - y1 = 0; - } - if (y2 < 0) - { - x2 -= slope2 * y2; - y2 = 0; - } - - int pixelPos = y1 * imageWidth; - int currX1; - int currX2; - if (y1 != y2 && slope3 < slope1 || y1 == y2 && slope3 > slope2) - { - int height1 = y2 - y1; - int height2 = y3 - y2; - - int prevX1; - int prevX2; - if (height1 <= 0) - { - prevX1 = x3 >> 14; - prevX2 = x2 >> 14; - } - else - { - prevX1 = x3 >> 14; - prevX2 = x1 >> 14; - } - - outlineAroundHorizontalLine(pixelPos, prevX1, prevX2, prevX2, prevX2); - - while (height1-- > 0) - { - currX1 = x3 >> 14; - currX2 = x1 >> 14; - outlineAroundHorizontalLine(pixelPos, currX1, currX2, prevX1, prevX2); - simulateHorizontalLineRasterizationForOutline(pixelPos, currX1, currX2); - x3 += slope3; - x1 += slope1; - pixelPos += imageWidth; - prevX1 = currX1; - prevX2 = currX2; - } - - while (height2-- > 0) - { - currX1 = x3 >> 14; - currX2 = x2 >> 14; - outlineAroundHorizontalLine(pixelPos, currX1, currX2, prevX1, prevX2); - simulateHorizontalLineRasterizationForOutline(pixelPos, currX1, currX2); - x3 += slope3; - x2 += slope2; - pixelPos += imageWidth; - prevX1 = currX1; - prevX2 = currX2; - } - - outlineAroundHorizontalLine(pixelPos, prevX1, prevX1, prevX1, prevX2); - } - else - { - int height1 = y2 - y1; - int height2 = y3 - y2; - - int prevX1; - int prevX2; - if (height1 <= 0) - { - prevX1 = x2 >> 14; - prevX2 = x3 >> 14; - } - else - { - prevX1 = x1 >> 14; - prevX2 = x3 >> 14; - } - - outlineAroundHorizontalLine(pixelPos, prevX1, prevX2, prevX2, prevX2); - - while (height1-- > 0) - { - currX1 = x1 >> 14; - currX2 = x3 >> 14; - outlineAroundHorizontalLine(pixelPos, currX1, currX2, prevX1, prevX2); - simulateHorizontalLineRasterizationForOutline(pixelPos, currX1, currX2); - x1 += slope1; - x3 += slope3; - pixelPos += imageWidth; - prevX1 = currX1; - prevX2 = currX2; - } - - while (height2-- > 0) - { - currX1 = x2 >> 14; - currX2 = x3 >> 14; - outlineAroundHorizontalLine(pixelPos, currX1, currX2, prevX1, prevX2); - simulateHorizontalLineRasterizationForOutline(pixelPos, currX1, currX2); - x3 += slope3; - x2 += slope2; - pixelPos += imageWidth; - prevX1 = currX1; - prevX2 = currX2; - } - - outlineAroundHorizontalLine(pixelPos, prevX1, prevX1, prevX1, prevX2); - } - } - - /** - * Translates the vertices 3D points to the screen canvas 2D points - * - * @param localX The local x position of the vertices - * @param localY The local y position of the vertices - * @param localZ The local z position of the vertices - * @param vertexOrientation The orientation of the vertices - * @return Returns true if any of them are inside the clip area, otherwise false - */ - private boolean projectVertices(Model model, final int localX, final int localY, final int localZ, final int vertexOrientation) - { - final int cameraX = client.getCameraX(); - final int cameraY = client.getCameraY(); - final int cameraZ = client.getCameraZ(); - final int cameraYaw = client.getCameraYaw(); - final int cameraPitch = client.getCameraPitch(); - final int scale = client.getScale(); - final int orientationSin = Perspective.SINE[vertexOrientation]; - final int orientationCos = Perspective.COSINE[vertexOrientation]; - final int pitchSin = Perspective.SINE[cameraPitch]; - final int pitchCos = Perspective.COSINE[cameraPitch]; - final int yawSin = Perspective.SINE[cameraYaw]; - final int yawCos = Perspective.COSINE[cameraYaw]; - final int vertexCount = model.getVerticesCount(); - final int[] verticesX = model.getVerticesX(); - final int[] verticesY = model.getVerticesY(); - final int[] verticesZ = model.getVerticesZ(); - - boolean anyVisible = false; - - // Make sure the arrays are big enough - while (projectedVerticesX.length < vertexCount) - { - int newSize = nextPowerOfTwo(vertexCount); - projectedVerticesX = new int[newSize]; - projectedVerticesY = new int[newSize]; - projectedVerticesRenderable = new boolean[newSize]; - } - - for (int i = 0; i < vertexCount; i++) - { - int vx = verticesX[i]; - int vy = verticesZ[i]; - int vz = verticesY[i]; - int vh; // Value holder - - // Rotate based on orientation - vh = vx * orientationCos + vy * orientationSin >> 16; - vy = vy * orientationCos - vx * orientationSin >> 16; - vx = vh; - - // Translate to local coords - vx += localX; - vy += localY; - vz += localZ; - - // Translate to camera - vx -= cameraX; - vy -= cameraY; - vz -= cameraZ; - - // Transform to canvas - vh = vx * yawCos + vy * yawSin >> 16; - vy = vy * yawCos - vx * yawSin >> 16; - vx = vh; - vh = vz * pitchCos - vy * pitchSin >> 16; - vz = vz * pitchSin + vy * pitchCos >> 16; - vy = vh; - - if (vz >= 50) - { - projectedVerticesX[i] = (clipX1 + clipX2) / 2 + vx * scale / vz; - projectedVerticesY[i] = (clipY1 + clipY2) / 2 + vy * scale / vz; - - projectedVerticesRenderable[i] = true; - anyVisible |= - projectedVerticesX[i] >= clipX1 && projectedVerticesX[i] < clipX2 && - projectedVerticesY[i] >= clipY1 && projectedVerticesY[i] < clipY2; - } - else - { - projectedVerticesRenderable[i] = false; - } - } - - return anyVisible; - } - - /** - * Simulate rendering of the model and puts every pixel of the wireframe of - * the non-culled and non-transparent faces into the outline pixel queue. - */ - private void simulateModelRasterizationForOutline(Model model) - { - final int triangleCount = model.getTrianglesCount(); - final int[] indices1 = model.getTrianglesX(); - final int[] indices2 = model.getTrianglesY(); - final int[] indices3 = model.getTrianglesZ(); - final byte[] triangleTransparencies = model.getTriangleTransparencies(); - - for (int i = 0; i < triangleCount; i++) - { - if (projectedVerticesRenderable[indices1[i]] && - projectedVerticesRenderable[indices2[i]] && - projectedVerticesRenderable[indices3[i]] && - // 254 and 255 counts as fully transparent - (triangleTransparencies == null || (triangleTransparencies[i] & 255) < 254)) - { - final int index1 = indices1[i]; - final int index2 = indices2[i]; - final int index3 = indices3[i]; - final int v1x = projectedVerticesX[index1]; - final int v1y = projectedVerticesY[index1]; - final int v2x = projectedVerticesX[index2]; - final int v2y = projectedVerticesY[index2]; - final int v3x = projectedVerticesX[index3]; - final int v3y = projectedVerticesY[index3]; - - if (!cullFace(v1x, v1y, v2x, v2y, v3x, v3y)) - { - simulateTriangleRasterizationForOutline( - v1x, v1y, v2x, v2y, v3x, v3y); - } - } - } - } - - /** - * Draws an outline of the pixels in the outline queue to an image - * - * @param image The image to draw the outline to - * @param outlineWidth The width of the outline - * @param innerColor The color of the pixels of the outline closest to the model - * @param outerColor The color of the pixels of the outline furthest away from the model - */ - private void renderOutline(BufferedImage image, int outlineWidth, Color innerColor, Color outerColor) - { - int[] imageData = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); - List ps = getPriorityList(outlineWidth); - - for (PixelDistanceAlpha p : ps) - { - int color; - int alpha; - if (outlineWidth == 1) - { - color = - ((innerColor.getRed() + outerColor.getRed()) << 15) | - ((innerColor.getGreen() + outerColor.getGreen() << 7)) | - ((innerColor.getBlue() + outerColor.getBlue() >> 1)); - alpha = (innerColor.getAlpha() + outerColor.getAlpha()) >> 1; - } - else - { - int outerAlpha = p.getOuterAlpha(); - int innerAlpha = 255 - outerAlpha; - int innerAlphaFraction = (innerAlpha * innerColor.getAlpha()) / 255; - int outerAlphaFraction = (outerAlpha * outerColor.getAlpha()) / 255; - alpha = innerAlphaFraction + outerAlphaFraction; - if (alpha != 0) - { - color = - ((innerColor.getRed() * innerAlphaFraction + - outerColor.getRed() * outerAlphaFraction) / alpha << 16) | - ((innerColor.getGreen() * innerAlphaFraction + - outerColor.getGreen() * outerAlphaFraction) / alpha << 8) | - ((innerColor.getBlue() * innerAlphaFraction + - outerColor.getBlue() * outerAlphaFraction) / alpha); - } - else - { - color = 0; - } - } - - final int distArrayPos = p.getDistArrayPos(); - final int nextDistArrayPosY = distArrayPos + outlineArrayWidth; - final int nextDistArrayPosX = distArrayPos + 1; - ensureMinimumOutlineQueueSize(nextDistArrayPosX, outlinePixelsLengths[distArrayPos] * 2); - ensureMinimumOutlineQueueSize(nextDistArrayPosY, outlinePixelsLengths[distArrayPos] * 2); - - // The following 3 branches do the same thing, but when the requirements are simple, - // there are less checks needed which can give a performance boost. - if (alpha == 255) - { - if (outlineWidth == 1) - { - for (int i2 = 0; i2 < outlinePixelsLengths[distArrayPos]; i2++) - { - int pixelPos = outlinePixels[distArrayPos][i2]; - int x = pixelPos % imageWidth; - int y = pixelPos / imageWidth; - if (x < clipX1 || x >= clipX2 || - y < clipY1 || y >= clipY2 || - visited[pixelPos] == currentVisitedNumber) - { - continue; - } - - imageData[pixelPos] = color; - } - } - else - { - for (int i2 = 0; i2 < outlinePixelsLengths[distArrayPos]; i2++) - { - int pixelPos = outlinePixels[distArrayPos][i2]; - int x = pixelPos % imageWidth; - int y = pixelPos / imageWidth; - if (x < clipX1 || x >= clipX2 || - y < clipY1 || y >= clipY2 || - visited[pixelPos] == currentVisitedNumber) - { - continue; - } - visited[pixelPos] = currentVisitedNumber; - - imageData[pixelPos] = color; - - if (pixelPos % imageWidth != 0) - { - outlinePixels[nextDistArrayPosX][outlinePixelsLengths[nextDistArrayPosX]++] = pixelPos - 1; - } - if ((pixelPos + 1) % imageWidth != 0) - { - outlinePixels[nextDistArrayPosX][outlinePixelsLengths[nextDistArrayPosX]++] = pixelPos + 1; - } - outlinePixels[nextDistArrayPosY][outlinePixelsLengths[nextDistArrayPosY]++] = pixelPos - imageWidth; - outlinePixels[nextDistArrayPosY][outlinePixelsLengths[nextDistArrayPosY]++] = pixelPos + imageWidth; - } - } - } - else - { - for (int i2 = 0; i2 < outlinePixelsLengths[distArrayPos]; i2++) - { - int pixelPos = outlinePixels[distArrayPos][i2]; - int x = pixelPos % imageWidth; - int y = pixelPos / imageWidth; - if (x < clipX1 || x >= clipX2 || - y < clipY1 || y >= clipY2 || - visited[pixelPos] == currentVisitedNumber) - { - continue; - } - visited[pixelPos] = currentVisitedNumber; - - imageData[pixelPos] = - ((((color & 0xFF0000) * alpha + (imageData[pixelPos] & 0xFF0000) * (255 - alpha)) / 255) & 0xFF0000) + - ((((color & 0xFF00) * alpha + (imageData[pixelPos] & 0xFF00) * (255 - alpha)) / 255) & 0xFF00) + - ((((color & 0xFF) * alpha + (imageData[pixelPos] & 0xFF) * (255 - alpha)) / 255) & 0xFF); - - if (pixelPos % imageWidth != 0) - { - outlinePixels[nextDistArrayPosX][outlinePixelsLengths[nextDistArrayPosX]++] = pixelPos - 1; - } - if ((pixelPos + 1) % imageWidth != 0) - { - outlinePixels[nextDistArrayPosX][outlinePixelsLengths[nextDistArrayPosX]++] = pixelPos + 1; - } - outlinePixels[nextDistArrayPosY][outlinePixelsLengths[nextDistArrayPosY]++] = pixelPos - imageWidth; - outlinePixels[nextDistArrayPosY][outlinePixelsLengths[nextDistArrayPosY]++] = pixelPos + imageWidth; - } - } - } - } - - /** - * Draws an outline around a model to an image - * - * @param localX The local x position of the model - * @param localY The local y position of the model - * @param localZ The local z position of the model - * @param orientation The orientation of the model - * @param outlineWidth The width of the outline - * @param innerColor The color of the pixels of the outline closest to the model - * @param outerColor The color of the pixels of the outline furthest away from the model - */ - private void drawModelOutline(Model model, int localX, int localY, int localZ, int orientation, int outlineWidth, Color innerColor, Color outerColor) - { - if (outlineWidth <= 0) - { - return; - } - - isReset = false; - usedSinceLastCheck = true; - - MainBufferProvider bufferProvider = (MainBufferProvider) client.getBufferProvider(); - BufferedImage image = (BufferedImage) bufferProvider.getImage(); - - clipX1 = client.getViewportXOffset(); - clipY1 = client.getViewportYOffset(); - clipX2 = client.getViewportWidth() + clipX1; - clipY2 = client.getViewportHeight() + clipY1; - imageWidth = image.getWidth(); - imageHeight = image.getHeight(); - final int pixelAmount = imageWidth * imageHeight; - - resetVisited(pixelAmount); - resetOutline(outlineWidth); - - if (!projectVertices(model, - localX, localY, localZ, orientation)) - { - // No vertex of the model is visible on the screen, so we can - // assume there are no parts of the model to outline. - return; - } - - simulateModelRasterizationForOutline(model); - - renderOutline(image, outlineWidth, innerColor, outerColor); - } - - public void drawOutline(NPC npc, int outlineWidth, Color color) - { - drawOutline(npc, outlineWidth, color, color); - } - - public void drawOutline(NPC npc, int outlineWidth, Color innerColor, Color outerColor) - { - int size = 1; - NPCDefinition composition = npc.getTransformedDefinition(); - if (composition != null) - { - size = composition.getSize(); - } - - LocalPoint lp = npc.getLocalLocation(); - if (lp != null) - { - // NPCs z position are calculated based on the tile height of the northeastern tile - final int northEastX = lp.getX() + Perspective.LOCAL_TILE_SIZE * (size - 1) / 2; - final int northEastY = lp.getY() + Perspective.LOCAL_TILE_SIZE * (size - 1) / 2; - final LocalPoint northEastLp = new LocalPoint(northEastX, northEastY); - - drawModelOutline(npc.getModel(), lp.getX(), lp.getY(), - Perspective.getTileHeight(client, northEastLp, client.getPlane()), - npc.getCurrentOrientation(), outlineWidth, innerColor, outerColor); - } - } - - public void drawOutline(Player player, int outlineWidth, Color color) - { - drawOutline(player, outlineWidth, color, color); - } - - public void drawOutline(Player player, int outlineWidth, Color innerColor, Color outerColor) - { - LocalPoint lp = player.getLocalLocation(); - if (lp != null) - { - drawModelOutline(player.getModel(), lp.getX(), lp.getY(), - Perspective.getTileHeight(client, lp, client.getPlane()), - player.getCurrentOrientation(), outlineWidth, innerColor, outerColor); - } - } - - public void drawOutline(GameObject gameObject, int outlineWidth, Color innerColor, Color outerColor) - { - LocalPoint lp = gameObject.getLocalLocation(); - if (lp != null) - { - drawModelOutline(gameObject.getModel(), lp.getX(), lp.getY(), - Perspective.getTileHeight(client, lp, gameObject.getPlane()), - gameObject.getRsOrientation(), outlineWidth, innerColor, outerColor); - } - } - - public void drawOutline(GroundObject groundObject, int outlineWidth, Color innerColor, Color outerColor) - { - LocalPoint lp = groundObject.getLocalLocation(); - if (lp != null) - { - drawModelOutline(groundObject.getModel(), lp.getX(), lp.getY(), - Perspective.getTileHeight(client, lp, client.getPlane()), - 0, outlineWidth, innerColor, outerColor); - } - } - - private void drawOutline(TileItemPile tileItemPile, int outlineWidth, Color innerColor, Color outerColor) - { - LocalPoint lp = tileItemPile.getLocalLocation(); - if (lp != null) - { - Model model = tileItemPile.getModelBottom(); - if (model != null) - { - drawModelOutline(model, lp.getX(), lp.getY(), - Perspective.getTileHeight(client, lp, tileItemPile.getPlane()), - 0, outlineWidth, innerColor, outerColor); - } - - model = tileItemPile.getModelMiddle(); - if (model != null) - { - drawModelOutline(model, lp.getX(), lp.getY(), - Perspective.getTileHeight(client, lp, tileItemPile.getPlane()), - 0, outlineWidth, innerColor, outerColor); - } - - model = tileItemPile.getModelTop(); - if (model != null) - { - drawModelOutline(model, lp.getX(), lp.getY(), - Perspective.getTileHeight(client, lp, tileItemPile.getPlane()), - 0, outlineWidth, innerColor, outerColor); - } - } - } - - private void drawOutline(DecorativeObject decorativeObject, int outlineWidth, Color innerColor, Color outerColor) - { - LocalPoint lp = decorativeObject.getLocalLocation(); - if (lp != null) - { - Model model = decorativeObject.getModel1(); - if (model != null) - { - drawModelOutline(model, - lp.getX() + decorativeObject.getXOffset(), - lp.getY() + decorativeObject.getYOffset(), - Perspective.getTileHeight(client, lp, decorativeObject.getPlane()), - decorativeObject.getOrientation(), outlineWidth, innerColor, outerColor); - } - - model = decorativeObject.getModel2(); - if (model != null) - { - // Offset is not used for the second model - drawModelOutline(model, lp.getX(), lp.getY(), - Perspective.getTileHeight(client, lp, decorativeObject.getPlane()), - decorativeObject.getOrientation(), outlineWidth, innerColor, outerColor); - } - } - } - - private void drawOutline(WallObject wallObject, int outlineWidth, Color innerColor, Color outerColor) - { - LocalPoint lp = wallObject.getLocalLocation(); - if (lp != null) - { - Model model = wallObject.getModelA(); - if (model != null) - { - drawModelOutline(model, lp.getX(), lp.getY(), - Perspective.getTileHeight(client, lp, wallObject.getPlane()), - wallObject.getOrientationA(), outlineWidth, innerColor, outerColor); - } - - model = wallObject.getModelB(); - if (model != null) - { - drawModelOutline(model, lp.getX(), lp.getY(), - Perspective.getTileHeight(client, lp, wallObject.getPlane()), - wallObject.getOrientationB(), outlineWidth, innerColor, outerColor); - } - } - } - - public void drawOutline(TileObject tileObject, int outlineWidth, Color color) - { - drawOutline(tileObject, outlineWidth, color, color); - } - - public void drawOutline(TileObject tileObject, - int outlineWidth, Color innerColor, Color outerColor) - { - if (tileObject instanceof GameObject) - { - drawOutline((GameObject) tileObject, outlineWidth, innerColor, outerColor); - } - else if (tileObject instanceof GroundObject) - { - drawOutline((GroundObject) tileObject, outlineWidth, innerColor, outerColor); - } - else if (tileObject instanceof TileItemPile) - { - drawOutline((TileItemPile) tileObject, outlineWidth, innerColor, outerColor); - } - else if (tileObject instanceof DecorativeObject) - { - drawOutline((DecorativeObject) tileObject, outlineWidth, innerColor, outerColor); - } - else if (tileObject instanceof WallObject) - { - drawOutline((WallObject) tileObject, outlineWidth, innerColor, outerColor); - } - } -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/menus/AbstractComparableEntry.java b/runelite-client/src/main/java/net/runelite/client/menus/AbstractComparableEntry.java deleted file mode 100644 index eccc8a26d7..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/menus/AbstractComparableEntry.java +++ /dev/null @@ -1,29 +0,0 @@ -package net.runelite.client.menus; - -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.Setter; -import net.runelite.api.MenuEntry; - -@Getter -@Setter -@EqualsAndHashCode -public abstract class AbstractComparableEntry -{ - String option = null; - - String target = null; - - int id = -1; - - int type = -1; - - boolean strictOption = true; - - boolean strictTarget = true; - - @EqualsAndHashCode.Exclude - private int priority = 0; - - public abstract boolean matches(MenuEntry entry); -} diff --git a/runelite-client/src/main/java/net/runelite/client/menus/BankComparableEntry.java b/runelite-client/src/main/java/net/runelite/client/menus/BankComparableEntry.java deleted file mode 100644 index f65f1508fd..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/menus/BankComparableEntry.java +++ /dev/null @@ -1,49 +0,0 @@ -package net.runelite.client.menus; - -import lombok.EqualsAndHashCode; -import net.runelite.api.MenuEntry; -import net.runelite.api.util.Text; -import net.runelite.api.widgets.WidgetID; -import net.runelite.api.widgets.WidgetInfo; -import org.apache.commons.lang3.StringUtils; - -@EqualsAndHashCode(callSuper = true) -public class BankComparableEntry extends AbstractComparableEntry -{ - public BankComparableEntry(String option, String itemName, boolean strictTarget) - { - this.setOption(option); - this.setTarget(Text.standardize(itemName)); - this.setStrictTarget(strictTarget); - } - - public boolean matches(MenuEntry entry) - { - if (isNotBankWidget(entry.getParam1())) - { - return false; - } - - if (isStrictTarget() && !Text.standardize(entry.getTarget()).equals(this.getTarget())) - { - return false; - } - - return StringUtils.containsIgnoreCase(entry.getOption(), this.getOption()) && Text.standardize(entry.getTarget()).contains(this.getTarget()); - } - - @Override - public int getPriority() - { - return 100; - } - - static boolean isNotBankWidget(int widgetID) - { - final int groupId = WidgetInfo.TO_GROUP(widgetID); - - return groupId != WidgetID.BANK_GROUP_ID - && groupId != WidgetID.BANK_INVENTORY_GROUP_ID - && groupId != WidgetID.GRAND_EXCHANGE_GROUP_ID; - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/menus/BaseComparableEntry.java b/runelite-client/src/main/java/net/runelite/client/menus/BaseComparableEntry.java deleted file mode 100644 index 6609486bb6..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/menus/BaseComparableEntry.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2019, Lucas - * 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.menus; - -import javax.annotation.Nonnull; -import joptsimple.internal.Strings; -import lombok.EqualsAndHashCode; -import net.runelite.api.MenuEntry; -import net.runelite.api.util.Text; -import org.apache.commons.lang3.StringUtils; - -@EqualsAndHashCode(callSuper = true) -public class BaseComparableEntry extends AbstractComparableEntry -{ - /** - * If two entries are both suppose to be left click, - * the entry with the higher priority will be selected. - * This only effects left click priority entries. - */ - - public BaseComparableEntry(String option, String target, int id, int type, boolean strictOption, boolean strictTarget) - { - super.option = option.trim().toLowerCase(); - super.target = Text.standardize(target); - super.id = id; - super.type = type; - super.strictOption = strictOption; - super.strictTarget = strictTarget; - } - - public boolean matches(@Nonnull MenuEntry entry) - { - String opt = entry.getOption(); - - if (strictOption && !StringUtils.equalsIgnoreCase(opt, option) || !strictOption && !StringUtils.containsIgnoreCase(opt, option)) - { - return false; - } - - if (strictTarget || !Strings.isNullOrEmpty(target)) - { - String tgt = Text.standardize(entry.getTarget(), true); - - if (strictTarget && !tgt.equals(target) || !strictTarget && !tgt.contains(target)) - { - return false; - } - } - - if (id != -1) - { - int id = entry.getIdentifier(); - - if (this.id != id) - { - return false; - } - } - - if (type != -1) - { - int type = entry.getOpcode(); - - return this.type == type; - } - - return true; - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/menus/ComparableEntries.java b/runelite-client/src/main/java/net/runelite/client/menus/ComparableEntries.java deleted file mode 100644 index a20ee70020..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/menus/ComparableEntries.java +++ /dev/null @@ -1,72 +0,0 @@ -package net.runelite.client.menus; - -import net.runelite.api.Client; - -public interface ComparableEntries -{ - /** - * BaseComparableEntries should only be used if there's - * no better ComparableEntry available. - * - * @param option has to equal entry option - * @param target has to equal entry option - * @return a new BaseComparableEntry - */ - static BaseComparableEntry newBaseComparableEntry(String option, String target) - { - return new BaseComparableEntry(option, target, -1, -1, true, true); - } - - /** - * BaseComparableEntries should only be used if there's - * no better ComparableEntry available. - * - * @param option has to equal option - * @param target equal or contains depending on strictTarget - * @param strictTarget read up one line - * @return a new BaseComparableEntry - */ - static BaseComparableEntry newBaseComparableEntry(String option, String target, boolean strictTarget) - { - return new BaseComparableEntry(option, target, -1, -1, true, strictTarget); - } - - /** - * BaseComparableEntries should only be used if there's - * no better ComparableEntry available. - * - * @param option equal or contains depending on strictOption - * @param target equal or contains depending on strictTarget - * @param id has to be the same, or -1 to skip checking - * @param type has to be the same, or -1 to skip checking - * @param strictOption strict option or nah - * @param strictTarget strict target or nah - * @return a new BaseComparableEntry - */ - static BaseComparableEntry newBaseComparableEntry(String option, String target, int id, int type, boolean strictOption, boolean strictTarget) - { - return new BaseComparableEntry(option, target, id, type, strictOption, strictTarget); - } - - /** - * This comparable finds all items with itemName - * in their name. It then checks the ItemDefinition - * for each of them, to see if it's possible for - * the item to have option as one of their options. - *

- * This has to be ran on the clientthread! - */ - static ItemComparableEntry newInvItemComparableEntry(Client client, String option, String itemName) - { - return new ItemComparableEntry.InvItemComparableEntry(client, option, itemName); - } - - /** - * This will only match items in the bank or in - * your inventory if the bank is open. Withdraw-x anyone? - */ - static BankComparableEntry newBankComparableEntry(String option, String itemName) - { - return new BankComparableEntry(option, itemName, false); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/menus/EquipmentComparableEntry.java b/runelite-client/src/main/java/net/runelite/client/menus/EquipmentComparableEntry.java deleted file mode 100644 index 8d4118a6a8..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/menus/EquipmentComparableEntry.java +++ /dev/null @@ -1,31 +0,0 @@ -package net.runelite.client.menus; - -import lombok.EqualsAndHashCode; -import net.runelite.api.MenuEntry; -import net.runelite.api.util.Text; -import net.runelite.api.widgets.WidgetID; -import net.runelite.api.widgets.WidgetInfo; -import org.apache.commons.lang3.StringUtils; - -@EqualsAndHashCode(callSuper = true) -public class EquipmentComparableEntry extends AbstractComparableEntry -{ - public EquipmentComparableEntry(String option, String itemName) - { - this.setOption(option); - this.setTarget(Text.standardize(itemName)); - } - - public boolean matches(MenuEntry entry) - { - final int groupId = WidgetInfo.TO_GROUP(entry.getParam1()); - - if (groupId != WidgetID.EQUIPMENT_GROUP_ID) - { - return false; - } - - return StringUtils.equalsIgnoreCase(entry.getOption(), this.getOption()) - && Text.standardize(entry.getTarget()).contains(this.getTarget()); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/menus/InventoryComparableEntry.java b/runelite-client/src/main/java/net/runelite/client/menus/InventoryComparableEntry.java deleted file mode 100644 index f299afdd6e..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/menus/InventoryComparableEntry.java +++ /dev/null @@ -1,36 +0,0 @@ -package net.runelite.client.menus; - -import lombok.EqualsAndHashCode; -import net.runelite.api.MenuEntry; -import net.runelite.api.util.Text; -import net.runelite.api.widgets.WidgetID; -import net.runelite.api.widgets.WidgetInfo; -import org.apache.commons.lang3.StringUtils; - -@EqualsAndHashCode(callSuper = true) -public class InventoryComparableEntry extends AbstractComparableEntry -{ - public InventoryComparableEntry(String option, String itemName, boolean strictTarget) - { - this.setOption(option); - this.setTarget(Text.standardize(itemName)); - this.setStrictTarget(strictTarget); - } - - public boolean matches(MenuEntry entry) - { - final int groupId = WidgetInfo.TO_GROUP(entry.getParam1()); - - if (groupId != WidgetID.INVENTORY_GROUP_ID) - { - return false; - } - - if (isStrictTarget() && Text.standardize(entry.getTarget()).equals(this.getTarget())) - { - return false; - } - - return StringUtils.containsIgnoreCase(entry.getOption(), this.getOption()) && Text.standardize(entry.getTarget()).contains(this.getTarget()); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/menus/ItemComparableEntry.java b/runelite-client/src/main/java/net/runelite/client/menus/ItemComparableEntry.java deleted file mode 100644 index 66a608bcda..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/menus/ItemComparableEntry.java +++ /dev/null @@ -1,96 +0,0 @@ -package net.runelite.client.menus; - -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.Setter; -import net.runelite.api.Client; -import net.runelite.api.ItemDefinition; -import net.runelite.api.MenuEntry; -import net.runelite.api.util.Text; -import org.apache.commons.lang3.StringUtils; - -@EqualsAndHashCode(callSuper = true) -@Getter -@Setter -abstract class ItemComparableEntry extends AbstractComparableEntry -{ - @EqualsAndHashCode.Exclude - short[] itemIds; - @EqualsAndHashCode.Exclude - short itemCount; - - public abstract boolean matches(MenuEntry entry); - - @EqualsAndHashCode(callSuper = true) - static class InvItemComparableEntry extends ItemComparableEntry - { - public InvItemComparableEntry(Client client, String option, String itemName) - { - assert client.isClientThread() : "You can only create these on the clientthread"; - - this.target = Text.standardize(itemName); - this.option = option; - - short[] tmp = this.itemIds = new short[16]; - - final int itemCount = client.getItemCount(); - short found = 0; - - for (short i = 0; i < itemCount; i++) - { - ItemDefinition def = client.getItemDefinition(i); - if (def.getNote() != -1 || !StringUtils.containsIgnoreCase(def.getName(), target)) - { - continue; - } - - boolean notValid = true; - for (String opt : def.getInventoryActions()) - { - if (opt != null && StringUtils.containsIgnoreCase(opt, option)) - { - notValid = false; - break; - } - } - - if (notValid && !"use".equals(this.option)) - { - continue; - } - - if (found >= tmp.length) - { - short[] rlyTmp = new short[found * 2]; - System.arraycopy(tmp, 0, rlyTmp, 0, found); - tmp = rlyTmp; - } - - tmp[found++] = i; - } - - this.itemIds = new short[itemCount]; - this.itemCount = found; - System.arraycopy(tmp, 0, this.itemIds, 0, found); - } - - public boolean matches(MenuEntry entry) - { - if (!StringUtils.containsIgnoreCase(entry.getOption(), this.option)) - { - return false; - } - - int entryId = entry.getIdentifier(); - for (short i = 0; i < itemCount; i++) - { - if (entryId == itemIds[i]) - { - return true; - } - } - - return false; - } - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/menus/MenuManager.java b/runelite-client/src/main/java/net/runelite/client/menus/MenuManager.java index 96400b2cfa..67129b0ba5 100644 --- a/runelite-client/src/main/java/net/runelite/client/menus/MenuManager.java +++ b/runelite-client/src/main/java/net/runelite/client/menus/MenuManager.java @@ -1,6 +1,5 @@ /* * Copyright (c) 2017, Robin - * Copyright (c) 2019, Lucas * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,46 +24,35 @@ */ package net.runelite.client.menus; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; import com.google.common.collect.HashMultimap; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; -import java.util.stream.Collectors; -import javax.annotation.Nonnull; +import java.util.regex.Pattern; import javax.inject.Inject; import javax.inject.Singleton; -import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; +import net.runelite.api.IconID; +import net.runelite.api.MenuAction; import net.runelite.api.MenuEntry; -import net.runelite.api.MenuOpcode; -import static net.runelite.api.MenuOpcode.MENU_ACTION_DEPRIORITIZE_OFFSET; -import net.runelite.api.NPCDefinition; -import net.runelite.api.events.BeforeRender; -import net.runelite.api.events.ClientTick; +import net.runelite.api.NPCComposition; import net.runelite.api.events.MenuEntryAdded; -import net.runelite.api.events.MenuOpened; import net.runelite.api.events.MenuOptionClicked; import net.runelite.api.events.NpcActionChanged; import net.runelite.api.events.PlayerMenuOptionClicked; import net.runelite.api.events.PlayerMenuOptionsChanged; import net.runelite.api.events.WidgetMenuOptionClicked; -import net.runelite.api.events.WidgetPressed; -import net.runelite.api.util.Text; import net.runelite.api.widgets.WidgetInfo; import net.runelite.client.eventbus.EventBus; -import static net.runelite.client.menus.ComparableEntries.newBaseComparableEntry; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.util.Text; @Singleton @Slf4j @@ -76,6 +64,8 @@ public class MenuManager private static final int IDX_LOWER = 4; private static final int IDX_UPPER = 8; + private static final Pattern BOUNTY_EMBLEM_TAG_AND_TIER_REGEXP = Pattern.compile(String.format("%s[1-9]0?", IconID.BOUNTY_HUNTER_EMBLEM.toString())); + private final Client client; private final EventBus eventBus; @@ -84,33 +74,13 @@ public class MenuManager //Used to manage custom non-player menu options private final Multimap managedMenuOptions = HashMultimap.create(); private final Set npcMenuOptions = new HashSet<>(); - private final Set priorityEntries = new HashSet<>(); - private final Map currentPriorityEntries = new LinkedHashMap<>(); - private final Set hiddenEntries = new HashSet<>(); - private final Map swaps = new HashMap<>(); - - private MenuEntry leftClickEntry = null; - - private int playerAttackIdx = -1; @Inject - private MenuManager(Client client, EventBus eventBus) + @VisibleForTesting + MenuManager(Client client, EventBus eventBus) { this.client = client; this.eventBus = eventBus; - - - eventBus.subscribe(MenuOpened.class, this, this::onMenuOpened); - eventBus.subscribe(MenuEntryAdded.class, this, this::onMenuEntryAdded); - eventBus.subscribe(PlayerMenuOptionsChanged.class, this, this::onPlayerMenuOptionsChanged); - eventBus.subscribe(NpcActionChanged.class, this, this::onNpcActionChanged); - eventBus.subscribe(WidgetPressed.class, this, this::onWidgetPressed); - eventBus.subscribe(MenuOptionClicked.class, this, this::onMenuOptionClicked); - - // Make sure last tick's entry gets cleared - eventBus.subscribe(ClientTick.class, this, tick -> leftClickEntry = null); - // Rebuild left click menu for top left entry - eventBus.subscribe(BeforeRender.class, this, br -> rebuildLeftClickMenu()); } /** @@ -150,190 +120,38 @@ public class MenuManager return false; } - private void onMenuOpened(MenuOpened event) + @Subscribe + public void onMenuEntryAdded(MenuEntryAdded event) { - currentPriorityEntries.clear(); - - // Need to reorder the list to normal, then rebuild with swaps - MenuEntry[] oldEntries = event.getMenuEntries(); - - leftClickEntry = null; - - List newEntries = Lists.newArrayList(oldEntries); - - boolean shouldDeprioritize = false; - - prioritizer: - for (MenuEntry entry : oldEntries) - { - for (AbstractComparableEntry p : priorityEntries) - { - // Create list of priority entries, and remove from menus - if (p.matches(entry)) - { - // Other entries need to be deprioritized if their types are lower than 1000 - if (entry.getOpcode() >= 1000 && !shouldDeprioritize) - { - shouldDeprioritize = true; - } - currentPriorityEntries.put(entry, p); - newEntries.remove(entry); - continue prioritizer; - } - } - - if (newEntries.size() > 0) - { - // Swap first matching entry to top - for (AbstractComparableEntry src : swaps.keySet()) - { - if (!src.matches(entry)) - { - continue; - } - - MenuEntry swapFrom = null; - - AbstractComparableEntry from = swaps.get(src); - - for (MenuEntry e : newEntries) - { - if (from.matches(e)) - { - swapFrom = e; - break; - } - } - - // Do not need to swap with itself or if the swapFrom is already the first entry - if (swapFrom != null && swapFrom != entry && swapFrom != Iterables.getLast(newEntries)) - { - // Deprioritize entries if the swaps are not in similar opcode groups - if ((swapFrom.getOpcode() >= 1000 && entry.getOpcode() < 1000) || (entry.getOpcode() >= 1000 && swapFrom.getOpcode() < 1000) && !shouldDeprioritize) - { - shouldDeprioritize = true; - } - - int indexFrom = newEntries.indexOf(swapFrom); - int indexTo = newEntries.indexOf(entry); - - Collections.swap(newEntries, indexFrom, indexTo); - } - } - } - } - - if (shouldDeprioritize) - { - for (MenuEntry entry : newEntries) - { - if (entry.getOpcode() <= MENU_ACTION_DEPRIORITIZE_OFFSET) - { - entry.setOpcode(entry.getOpcode() + MENU_ACTION_DEPRIORITIZE_OFFSET); - } - } - } - - if (!currentPriorityEntries.isEmpty()) - { - newEntries.addAll(currentPriorityEntries.entrySet().stream() - .sorted(Comparator.comparingInt(e -> e.getValue().getPriority())) - .map(Map.Entry::getKey) - .collect(Collectors.toList())); - } - - MenuEntry[] arrayEntries = newEntries.toArray(new MenuEntry[0]); - - // Need to set the event entries to prevent conflicts - event.setMenuEntries(arrayEntries); - event.setModified(); - } - - private void onMenuEntryAdded(MenuEntryAdded event) - { - if (client.isSpellSelected()) + if (client.getSpellSelected()) { return; } - for (AbstractComparableEntry e : hiddenEntries) + int widgetId = event.getActionParam1(); + Collection options = managedMenuOptions.get(widgetId); + + for (WidgetMenuOption currentMenu : options) { - if (e.matches(event)) + if (!menuContainsCustomMenu(currentMenu))//Don't add if we have already added it to this widget { - client.setMenuOptionCount(client.getMenuOptionCount() - 1); - return; + MenuEntry[] menuEntries = client.getMenuEntries(); + menuEntries = Arrays.copyOf(menuEntries, menuEntries.length + 1); + + MenuEntry menuEntry = menuEntries[menuEntries.length - 1] = new MenuEntry(); + menuEntry.setOption(currentMenu.getMenuOption()); + menuEntry.setParam1(widgetId); + menuEntry.setTarget(currentMenu.getMenuTarget()); + menuEntry.setType(MenuAction.RUNELITE.getId()); + + client.setMenuEntries(menuEntries); } } - - int widgetId = event.getParam1(); - if (managedMenuOptions.containsKey(widgetId)) - { - Collection options = managedMenuOptions.get(widgetId); - - for (WidgetMenuOption currentMenu : options) - { - if (!menuContainsCustomMenu(currentMenu))//Don't add if we have already added it to this widget - { - client.insertMenuItem( - currentMenu.getMenuOption(), - currentMenu.getMenuTarget(), - MenuOpcode.RUNELITE.getId(), - 0, - 0, - widgetId, - false - ); - } - } - } - } - - private void rebuildLeftClickMenu() - { - leftClickEntry = null; - if (client.isMenuOpen()) - { - return; - } - - int menuOptionCount = client.getMenuOptionCount(); - if (menuOptionCount <= 2) - { - return; - } - - MenuEntry[] entries = new MenuEntry[menuOptionCount + priorityEntries.size()]; - System.arraycopy(client.getMenuEntries(), 0, entries, 0, menuOptionCount); - - if (!priorityEntries.isEmpty()) - { - indexPriorityEntries(entries, menuOptionCount); - } - - if (leftClickEntry == null && !swaps.isEmpty()) - { - indexSwapEntries(entries, menuOptionCount); - } - - - if (leftClickEntry == null) - { - Arrays.stream(entries) - .filter(Objects::nonNull) - .filter(MenuEntry::isForceLeftClick) - .findFirst() - .ifPresentOrElse( - (entry) -> leftClickEntry = entry, - () -> leftClickEntry = entries[menuOptionCount - 1] - ); - } - - client.setMenuEntries(entries); } public void addPlayerMenuItem(String menuText) { - Objects.requireNonNull(menuText); + Preconditions.checkNotNull(menuText); int playerMenuIndex = findEmptyPlayerMenuIndex(); if (playerMenuIndex == IDX_UPPER) @@ -346,8 +164,7 @@ public class MenuManager public void removePlayerMenuItem(String menuText) { - Objects.requireNonNull(menuText); - + Preconditions.checkNotNull(menuText); for (Map.Entry entry : playerMenuIndexMap.entrySet()) { if (entry.getValue().equalsIgnoreCase(menuText)) @@ -358,7 +175,8 @@ public class MenuManager } } - private void onPlayerMenuOptionsChanged(PlayerMenuOptionsChanged event) + @Subscribe + public void onPlayerMenuOptionsChanged(PlayerMenuOptionsChanged event) { int idx = event.getIndex(); @@ -382,16 +200,17 @@ public class MenuManager addPlayerMenuItem(newIdx, menuText); } - private void onNpcActionChanged(NpcActionChanged event) + @Subscribe + public void onNpcActionChanged(NpcActionChanged event) { - NPCDefinition composition = event.getNpcDefinition(); + NPCComposition composition = event.getNpcComposition(); for (String npcOption : npcMenuOptions) { addNpcOption(composition, npcOption); } } - private void addNpcOption(NPCDefinition composition, String npcOption) + private void addNpcOption(NPCComposition composition, String npcOption) { String[] actions = composition.getActions(); int unused = -1; @@ -413,84 +232,52 @@ public class MenuManager actions[unused] = npcOption; } - private void removeNpcOption(NPCDefinition composition, String npcOption) + @Subscribe + public void onMenuOptionClicked(MenuOptionClicked event) { - String[] actions = composition.getActions(); - - if (composition.getActions() == null) - { - return; - } - - for (int i = 0; i < actions.length; ++i) - { - if (actions[i] != null && actions[i].equals(npcOption)) - { - actions[i] = null; - } - } - } - - private void onWidgetPressed(WidgetPressed event) - { - rebuildLeftClickMenu(); - client.setTempMenuEntry(leftClickEntry); - } - - void onMenuOptionClicked(MenuOptionClicked event) - { - // option and target will be the same if this one came from "tempMenuAction" - if (!client.isMenuOpen() && !event.getOption().equals(event.getTarget()) && event.isAuthentic()) - { - if (!event.equals(leftClickEntry)) - { - rebuildLeftClickMenu(); - } - - if (leftClickEntry != null) - { - event.setMenuEntry(leftClickEntry); - leftClickEntry = null; - } - } - - if (event.getMenuOpcode() != MenuOpcode.RUNELITE - && event.getMenuOpcode() != MenuOpcode.RUNELITE_PLAYER) + if (event.getMenuAction() != MenuAction.RUNELITE + && event.getMenuAction() != MenuAction.RUNELITE_PLAYER) { return; // not a managed widget option or custom player option } - int widgetId = event.getParam1(); + int widgetId = event.getWidgetId(); Collection options = managedMenuOptions.get(widgetId); for (WidgetMenuOption curMenuOption : options) { - if (curMenuOption.getMenuTarget().equals(event.getTarget()) - && curMenuOption.getMenuOption().equals(event.getOption())) + if (curMenuOption.getMenuTarget().equals(event.getMenuTarget()) + && curMenuOption.getMenuOption().equals(event.getMenuOption())) { WidgetMenuOptionClicked customMenu = new WidgetMenuOptionClicked(); - customMenu.setMenuOption(event.getOption()); - customMenu.setMenuTarget(event.getTarget()); + customMenu.setMenuOption(event.getMenuOption()); + customMenu.setMenuTarget(event.getMenuTarget()); customMenu.setWidget(curMenuOption.getWidget()); - eventBus.post(WidgetMenuOptionClicked.class, customMenu); + eventBus.post(customMenu); return; // don't continue because it's not a player option } } - String username = Text.removeTags(event.getTarget(), true); + // removes bounty hunter emblem tag and tier from player name, e.g: + // "username5 (level-42)" -> "username (level-42)" + String target = BOUNTY_EMBLEM_TAG_AND_TIER_REGEXP.matcher(event.getMenuTarget()).replaceAll(""); + + // removes tags and level from player names for example: + // username (level-42) or username + String username = Text.removeTags(target).split("[(]")[0].trim(); PlayerMenuOptionClicked playerMenuOptionClicked = new PlayerMenuOptionClicked(); - playerMenuOptionClicked.setMenuOption(event.getOption()); + playerMenuOptionClicked.setMenuOption(event.getMenuOption()); playerMenuOptionClicked.setMenuTarget(username); - eventBus.post(PlayerMenuOptionClicked.class, playerMenuOptionClicked); + eventBus.post(playerMenuOptionClicked); } private void addPlayerMenuItem(int playerOptionIndex, String menuText) { client.getPlayerOptions()[playerOptionIndex] = menuText; client.getPlayerOptionsPriorities()[playerOptionIndex] = true; - client.getPlayerMenuTypes()[playerOptionIndex] = MenuOpcode.RUNELITE_PLAYER.getId(); + client.getPlayerMenuTypes()[playerOptionIndex] = MenuAction.RUNELITE_PLAYER.getId(); playerMenuIndexMap.put(playerOptionIndex, menuText); } @@ -516,438 +303,4 @@ public class MenuManager return index; } - - public int getPlayerAttackOpcode() - { - final String[] playerMenuOptions = client.getPlayerOptions(); - - if (playerAttackIdx != -1 && playerMenuOptions[playerAttackIdx].equals("Attack")) - { - return client.getPlayerMenuTypes()[playerAttackIdx]; - } - - playerAttackIdx = -1; - - for (int i = IDX_LOWER; i < IDX_UPPER; i++) - { - if ("Attack".equals(playerMenuOptions[i])) - { - playerAttackIdx = i; - break; - } - } - - return playerAttackIdx >= 0 ? client.getPlayerMenuTypes()[playerAttackIdx] : -1; - } - - /** - * Adds to the set of menu entries which when present, will remove all entries except for this one - */ - public AbstractComparableEntry addPriorityEntry(String option, String target) - { - option = option.trim().toLowerCase(); - target = Text.standardize(target); - - AbstractComparableEntry entry = newBaseComparableEntry(option, target); - - priorityEntries.add(entry); - - return entry; - } - - public void removePriorityEntry(String option, String target) - { - option = option.trim().toLowerCase(); - target = Text.standardize(target); - - AbstractComparableEntry entry = newBaseComparableEntry(option, target); - - priorityEntries.remove(entry); - } - - - /** - * Adds to the set of menu entries which when present, will remove all entries except for this one - * This method will add one with strict option, but not-strict target (contains for target, equals for option) - */ - public AbstractComparableEntry addPriorityEntry(String option) - { - option = option.trim().toLowerCase(); - - AbstractComparableEntry entry = newBaseComparableEntry(option, "", false); - - priorityEntries.add(entry); - - return entry; - } - - public AbstractComparableEntry addPriorityEntry(String option, boolean strictOption) - { - option = option.trim().toLowerCase(); - - AbstractComparableEntry entry = - newBaseComparableEntry(option, "", -1, -1, false, strictOption); - - priorityEntries.add(entry); - - return entry; - } - - public AbstractComparableEntry addPriorityEntry(AbstractComparableEntry entry) - { - priorityEntries.add(entry); - - return entry; - } - - public void removePriorityEntry(AbstractComparableEntry entry) - { - priorityEntries.remove(entry); - } - - public void removePriorityEntry(String option) - { - option = option.trim().toLowerCase(); - - AbstractComparableEntry entry = newBaseComparableEntry(option, "", false); - - priorityEntries.remove(entry); - } - - public void removePriorityEntry(String option, boolean strictOption) - { - option = option.trim().toLowerCase(); - - AbstractComparableEntry entry = - newBaseComparableEntry(option, "", -1, -1, false, strictOption); - - priorityEntries.remove(entry); - } - - public void addPriorityEntries(Collection entries) - { - priorityEntries.addAll(entries); - } - - public void removePriorityEntries(Collection entries) - { - priorityEntries.removeAll(entries); - } - - /** - * Adds to the map of swaps. Strict options, not strict target but target1=target2 - */ - public void addSwap(String option, String target, String option2) - { - addSwap(option, target, option2, target, true, false); - } - - public void removeSwap(String option, String target, String option2) - { - removeSwap(option, target, option2, target, true, false); - } - - /** - * Adds to the map of swaps. - */ - private void addSwap(String option, String target, String option2, String target2, boolean strictOption, boolean strictTarget) - { - option = option.trim().toLowerCase(); - target = Text.standardize(target); - - option2 = option2.trim().toLowerCase(); - target2 = Text.standardize(target2); - - AbstractComparableEntry swapFrom = newBaseComparableEntry(option, target, -1, -1, strictOption, strictTarget); - AbstractComparableEntry swapTo = newBaseComparableEntry(option2, target2, -1, -1, strictOption, strictTarget); - - if (swapTo.equals(swapFrom)) - { - log.warn("You shouldn't try swapping an entry for itself"); - return; - } - - swaps.put(swapFrom, swapTo); - } - - - private void removeSwap(String option, String target, String option2, String target2, boolean strictOption, boolean strictTarget) - { - option = option.trim().toLowerCase(); - target = Text.standardize(target); - - option2 = option2.trim().toLowerCase(); - target2 = Text.standardize(target2); - - AbstractComparableEntry swapFrom = newBaseComparableEntry(option, target, -1, -1, strictOption, strictTarget); - AbstractComparableEntry swapTo = newBaseComparableEntry(option2, target2, -1, -1, strictOption, strictTarget); - - removeSwap(swapFrom, swapTo); - } - - /** - * Adds to the map of swaps. - Strict option + target - */ - public void addSwap(String option, String target, String option2, String target2) - { - addSwap(option, target, option2, target2, false, false); - } - - public void removeSwap(String option, String target, String option2, String target2) - { - removeSwap(option, target, option2, target2, false, false); - } - - /** - * Adds to the map of swaps - Pre-baked entry - */ - public void addSwap(AbstractComparableEntry swapFrom, AbstractComparableEntry swapTo) - { - if (swapTo.equals(swapFrom)) - { - log.warn("You shouldn't try swapping an entry for itself"); - return; - } - - swaps.put(swapFrom, swapTo); - } - - /** - * Adds to the map of swaps - Non-strict option/target, but with opcode & id - * ID's of -1 are ignored in matches()! - */ - public void addSwap(String option, String target, int id, int type, String option2, String target2, int id2, int type2) - { - option = option.trim().toLowerCase(); - target = Text.standardize(target); - - option2 = option2.trim().toLowerCase(); - target2 = Text.standardize(target2); - - AbstractComparableEntry swapFrom = newBaseComparableEntry(option, target, id, type, false, false); - AbstractComparableEntry swapTo = newBaseComparableEntry(option2, target2, id2, type2, false, false); - - if (swapTo.equals(swapFrom)) - { - log.warn("You shouldn't try swapping an entry for itself"); - return; - } - - swaps.put(swapFrom, swapTo); - } - - public void removeSwap(String option, String target, int id, int type, String option2, String target2, int id2, int type2) - { - option = option.trim().toLowerCase(); - target = Text.standardize(target); - - option2 = option2.trim().toLowerCase(); - target2 = Text.standardize(target2); - - AbstractComparableEntry swapFrom = newBaseComparableEntry(option, target, id, type, false, false); - AbstractComparableEntry swapTo = newBaseComparableEntry(option2, target2, id2, type2, false, false); - - swaps.entrySet().removeIf(e -> e.getKey().equals(swapFrom) && e.getValue().equals(swapTo)); - } - - public void removeSwap(AbstractComparableEntry swapFrom, AbstractComparableEntry swapTo) - { - swaps.entrySet().removeIf(e -> e.getKey().equals(swapFrom) && e.getValue().equals(swapTo)); - } - - /** - * Removes all swaps with target - */ - public void removeSwaps(String... fromTarget) - { - for (String target : fromTarget) - { - final String s = Text.standardize(target); - swaps.keySet().removeIf(e -> e.getTarget() != null && e.getTarget().equals(s)); - priorityEntries.removeIf(e -> e.getTarget() != null && e.getTarget().equals(s)); - hiddenEntries.removeIf(e -> e.getTarget() != null && e.getTarget().equals(s)); - } - } - - /** - * Adds to the set of menu entries which when present, will be hidden from the menu - */ - public void addHiddenEntry(String option, String target) - { - option = option.trim().toLowerCase(); - target = Text.standardize(target); - - AbstractComparableEntry entry = newBaseComparableEntry(option, target); - - hiddenEntries.add(entry); - } - - public void removeHiddenEntry(String option, String target) - { - option = option.trim().toLowerCase(); - target = Text.standardize(target); - - AbstractComparableEntry entry = newBaseComparableEntry(option, target); - - hiddenEntries.removeIf(entry::equals); - } - - /** - * Adds to the set of menu entries which when present, will be hidden from the menu - * This method will add one with strict option, but not-strict target (contains for target, equals for option) - */ - public void addHiddenEntry(String option) - { - option = option.trim().toLowerCase(); - - AbstractComparableEntry entry = newBaseComparableEntry(option, "", false); - - hiddenEntries.add(entry); - } - - public void removeHiddenEntry(String option) - { - option = option.trim().toLowerCase(); - - AbstractComparableEntry entry = newBaseComparableEntry(option, "", false); - - hiddenEntries.removeIf(entry::equals); - } - - /** - * Adds to the set of hidden entries. - */ - public void addHiddenEntry(String option, String target, boolean strictOption, boolean strictTarget) - { - option = option.trim().toLowerCase(); - target = Text.standardize(target); - - AbstractComparableEntry entry = newBaseComparableEntry(option, target, -1, -1, strictOption, strictTarget); - - hiddenEntries.add(entry); - } - - public void removeHiddenEntry(String option, String target, boolean strictOption, boolean strictTarget) - { - option = option.trim().toLowerCase(); - target = Text.standardize(target); - - AbstractComparableEntry entry = newBaseComparableEntry(option, target, -1, -1, strictOption, strictTarget); - - hiddenEntries.remove(entry); - } - - /** - * Adds to the set of hidden entries - Pre-baked Comparable entry - */ - public void addHiddenEntry(AbstractComparableEntry entry) - { - hiddenEntries.add(entry); - } - - public void removeHiddenEntry(AbstractComparableEntry entry) - { - hiddenEntries.remove(entry); - } - - // This could use some optimization - private void indexPriorityEntries(MenuEntry[] entries, int menuOptionCount) - { - // create a array of priority entries so we can sort those - final SortMapping[] prios = new SortMapping[entries.length - menuOptionCount]; - - int prioAmt = 0; - for (int i = 0; i < menuOptionCount; i++) - { - final MenuEntry entry = entries[i]; - for (AbstractComparableEntry prio : priorityEntries) - { - if (!prio.matches(entry)) - { - continue; - } - - final SortMapping map = new SortMapping(prio.getPriority(), entry); - prios[prioAmt++] = map; - entries[i] = null; - break; - } - } - - if (prioAmt == 0) - { - return; - } - - // Sort em! - Arrays.sort(prios, 0, prioAmt); - int i; - - // Just place them after the standard entries. clientmixin ignores null entries - for (i = 0; i < prioAmt; i++) - { - entries[menuOptionCount + i] = prios[i].entry; - } - - leftClickEntry = entries[menuOptionCount + i - 1]; - - } - - private void indexSwapEntries(MenuEntry[] entries, int menuOptionCount) - { - // firstEntry was null, so it's the entry at count - 1 - final MenuEntry first = entries[menuOptionCount - 1]; - if (first == null) - { - log.debug("First entry is null"); - return; - } - - Set values = new HashSet<>(); - - for (Map.Entry pair : swaps.entrySet()) - { - if (pair.getKey().matches(first)) - { - values.add(pair.getValue()); - } - } - - if (values.isEmpty()) - { - return; - } - - // Backwards so we swap with the otherwise highest one - // Count - 2 so we don't compare the entry against itself - for (int i = menuOptionCount - 2; i > 0; i--) - { - final MenuEntry entry = entries[i]; - for (AbstractComparableEntry swap : values) - { - if (!swap.matches(entry)) - { - continue; - } - - entries[i] = first; - entries[menuOptionCount - 1] = entry; - leftClickEntry = entry; - return; - } - } - } - - @AllArgsConstructor - private static class SortMapping implements Comparable - { - private final int priority; - private final MenuEntry entry; - - @Override - public int compareTo(@Nonnull SortMapping mapping) - { - return Integer.compare(this.priority, mapping.priority); - } - } } diff --git a/runelite-client/src/main/java/net/runelite/client/menus/ShopComparableEntry.java b/runelite-client/src/main/java/net/runelite/client/menus/ShopComparableEntry.java deleted file mode 100644 index 6f022433c4..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/menus/ShopComparableEntry.java +++ /dev/null @@ -1,45 +0,0 @@ -package net.runelite.client.menus; - -import java.util.List; -import net.runelite.api.MenuEntry; -import net.runelite.api.util.Text; - -public class ShopComparableEntry extends AbstractComparableEntry -{ - private ShopComparableEntry(final boolean buy, final int amount, final String item) - { - assert amount == 1 || amount == 5 || amount == 10 || amount == 50 : "Only 1, 5, 10, or 50 are valid amounts"; - - this.setOption((buy ? "buy " : "sell ") + amount); - this.setTarget(Text.standardize(item)); - } - - @Override - public boolean matches(final MenuEntry entry) - { - return Text.standardize(entry.getOption()).equals(this.getOption()) && Text.standardize(entry.getTarget()).equals(this.getTarget()); - } - - @Override - public int getPriority() - { - return 100; - } - - @Override - public boolean equals(Object other) - { - return other instanceof ShopComparableEntry && super.equals(other); - } - - /** - * Fills the array with ShopComparableEntries, getting the items from the fed list - */ - public static void populateArray(final AbstractComparableEntry[] array, final List items, final boolean buy, final int amount) - { - for (int i = 0; i < array.length; i++) - { - array[i] = new ShopComparableEntry(buy, amount, items.get(i)); - } - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/menus/WidgetMenuOption.java b/runelite-client/src/main/java/net/runelite/client/menus/WidgetMenuOption.java index 23a08426d6..307551542c 100644 --- a/runelite-client/src/main/java/net/runelite/client/menus/WidgetMenuOption.java +++ b/runelite-client/src/main/java/net/runelite/client/menus/WidgetMenuOption.java @@ -24,23 +24,24 @@ */ package net.runelite.client.menus; -import java.awt.Color; import net.runelite.api.widgets.WidgetInfo; + +import java.awt.Color; import net.runelite.client.ui.JagexColors; import net.runelite.client.util.ColorUtil; public final class WidgetMenuOption { /** - * The left hand text to be displayed on the menu option. Ex. the option of "Drop Bones" is "Drop" + * The left hand text to be displayed on the menu option. Ex. the menuOption of "Drop Bones" is "Drop" */ private String menuOption; /** - * The right hand text to be displayed on the menu option Ex. the target of "Drop Bones" is "Bones" + * The right hand text to be displayed on the menu option Ex. the menuTarget of "Drop Bones" is "Bones" */ private String menuTarget; /** - * The color that the target should be. Defaults to the brownish color that most menu options have. + * The color that the menuTarget should be. Defaults to the brownish color that most menu options have. */ private Color color = JagexColors.MENU_TARGET; @@ -59,7 +60,7 @@ public final class WidgetMenuOption public WidgetMenuOption(String menuOption, String menuTarget, WidgetInfo widget) { this.menuOption = menuOption; - this.menuTarget = menuTarget; + setMenuTarget(menuTarget); this.widget = widget; } diff --git a/runelite-client/src/main/java/net/runelite/client/menus/WithdrawComparableEntry.java b/runelite-client/src/main/java/net/runelite/client/menus/WithdrawComparableEntry.java deleted file mode 100644 index 8c4cc8b98b..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/menus/WithdrawComparableEntry.java +++ /dev/null @@ -1,82 +0,0 @@ -package net.runelite.client.menus; - -import java.util.List; -import lombok.AllArgsConstructor; -import lombok.EqualsAndHashCode; -import net.runelite.api.MenuEntry; -import net.runelite.api.util.Text; - -@EqualsAndHashCode(callSuper = true) -public class WithdrawComparableEntry extends AbstractComparableEntry -{ - private static String x; - - private final Amount amount; - - private WithdrawComparableEntry(Amount amount, String item) - { - this.amount = amount; - this.setTarget(Text.standardize(item)); - } - - @Override - public boolean matches(MenuEntry entry) - { - if (BankComparableEntry.isNotBankWidget(entry.getParam1())) - { - return false; - } - - final String option = entry.getOption(); - - if (!option.startsWith("Withdraw") && !option.startsWith("Deposit")) - { - return false; - } - - if (amount == Amount.X) - { - if (!option.endsWith(x)) - { - return false; - } - } - else if (!option.endsWith(amount.suffix)) - { - return false; - } - - return Text.standardize(entry.getTarget()).contains(this.getTarget()); - } - - @Override - public int getPriority() - { - return 10; - } - - public static void setX(int amount) - { - x = String.valueOf(amount); - } - - public static void populateArray(AbstractComparableEntry[] array, List items, Amount amount) - { - for (int i = 0; i < array.length; i++) - { - array[i] = new WithdrawComparableEntry(amount, items.get(i)); - } - } - - @AllArgsConstructor - public enum Amount - { - ONE("1"), - FIVE("5"), - TEN("10"), - X(null), - ALL("All"); - - private String suffix; - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/ExternalPf4jPluginManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/ExternalPf4jPluginManager.java deleted file mode 100644 index f8043a3777..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/ExternalPf4jPluginManager.java +++ /dev/null @@ -1,381 +0,0 @@ -package net.runelite.client.plugins; - -import java.io.Closeable; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import javax.swing.JOptionPane; -import javax.swing.SwingUtilities; -import lombok.extern.slf4j.Slf4j; -import net.runelite.client.RuneLite; -import net.runelite.client.RuneLiteProperties; -import org.pf4j.BasePluginLoader; -import org.pf4j.CompoundPluginLoader; -import org.pf4j.CompoundPluginRepository; -import org.pf4j.DefaultPluginManager; -import org.pf4j.DependencyResolver; -import org.pf4j.DevelopmentPluginRepository; -import org.pf4j.JarPluginLoader; -import org.pf4j.JarPluginRepository; -import org.pf4j.ManifestPluginDescriptorFinder; -import org.pf4j.PluginAlreadyLoadedException; -import org.pf4j.PluginDescriptorFinder; -import org.pf4j.PluginLoader; -import org.pf4j.PluginRepository; -import org.pf4j.PluginRuntimeException; -import org.pf4j.PluginState; -import org.pf4j.PluginStateEvent; -import org.pf4j.PluginWrapper; -import org.pf4j.RuntimeMode; - -/** - * A pf4j {@link DefaultPluginManager} which can load openosrs external plugins, either from jar files in a directory, - * or from locally compiled classes depending on whether {@link ExternalPluginManager#isDevelopmentMode()} is true - */ -@Slf4j -class ExternalPf4jPluginManager extends DefaultPluginManager -{ - private final ExternalPluginManager externalPluginManager; - - public ExternalPf4jPluginManager(ExternalPluginManager externalPluginManager) - { - super(RuneLiteProperties.getPluginPath() != null ? Paths.get(RuneLiteProperties.getPluginPath()) - : RuneLite.EXTERNALPLUGIN_DIR.toPath()); - this.externalPluginManager = externalPluginManager; - } - - @Override - protected PluginDescriptorFinder createPluginDescriptorFinder() - { - return new ManifestPluginDescriptorFinder() - { - protected Path getManifestPath(Path pluginPath) - { - if (isDevelopment()) - { - // The superclass performs a find, which is slow in development mode since we're pointing - // at a sources directory, which can have a lot of files. The external plugin template - // will always output the manifest at the following location, so we can hardcode this path. - return pluginPath.resolve(ExternalPluginManager.DEVELOPMENT_MANIFEST_PATH); - } - - return super.getManifestPath(pluginPath); - } - }; - } - - @Override - protected PluginRepository createPluginRepository() - { - CompoundPluginRepository compoundPluginRepository = new CompoundPluginRepository(); - - if (isNotDevelopment()) - { - JarPluginRepository jarPluginRepository = new JarPluginRepository(getPluginsRoot()); - compoundPluginRepository.add(jarPluginRepository); - } - - if (isDevelopment()) - { - for (String developmentPluginPath : RuneLiteProperties.getPluginDevelopmentPath()) - { - DevelopmentPluginRepository developmentPluginRepository = new DevelopmentPluginRepository(Paths.get(developmentPluginPath)) - { - @Override - public boolean deletePluginPath(Path pluginPath) - { - // Do nothing, because we'd be deleting our sources! - return filter.accept(pluginPath.toFile()); - } - }; - - developmentPluginRepository.setFilter(new ExternalPluginFileFilter()); - compoundPluginRepository.add(developmentPluginRepository); - } - } - - return compoundPluginRepository; - } - - @Override - protected PluginLoader createPluginLoader() - { - return new CompoundPluginLoader() - .add(new BasePluginLoader(this, new ExternalPluginClasspath()), this::isDevelopment) - .add(new JarPluginLoader(this), this::isNotDevelopment); - } - - @Override - public void loadPlugins() - { - for (Path path : pluginsRoots) - { - if (Files.notExists(path) || !Files.isDirectory(path)) - { - log.warn("No '{}' root", path); - - return; - } - } - - List pluginPaths = pluginRepository.getPluginPaths(); - Collections.reverse(pluginPaths); - - if (pluginPaths.isEmpty()) - { - log.warn("No plugins"); - return; - } - - log.debug("Found {} possible plugins: {}", pluginPaths.size(), pluginPaths); - - Set duplicatePlugins = new HashSet<>(); - for (Path pluginPath : pluginPaths) - { - try - { - if (!isPluginEligibleForLoading(pluginPath) && isNotDevelopment()) - { - continue; - } - loadPluginFromPath(pluginPath); - } - catch (PluginRuntimeException e) - { - if (!(e instanceof PluginAlreadyLoadedException)) - { - if (!ExternalPluginManager.isDevelopmentMode()) - { - String plugin = pluginPath.toString().substring(pluginsRoots.get(0).toString().length() + 1); - duplicatePlugins.add(plugin); - } - log.error("Could not load plugin {}", pluginPath, e); - } - } - } - - if (!duplicatePlugins.isEmpty()) - { - log.error("Duplicate plugins detected: {}", String.join(", ", duplicatePlugins)); - - String formatted = String.join("\n", duplicatePlugins); - - SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(null, "You have duplicate plugins in your externalmanager.\n" + - "Having duplicate plugins will result in an unstable\n" + - "experience, It is highly recommended to delete any\n" + - "duplicates, here is a list of the plugins.\n\n" + - formatted, "Duplicate Plugins Detected", JOptionPane.WARNING_MESSAGE)); - } - - try - { - resolvePlugins(); - } - catch (PluginRuntimeException e) - { - if (e instanceof DependencyResolver.DependenciesNotFoundException) - { - throw e; - } - - log.error("Could not resolve plugins", e); - } - } - - @Override - protected void resolvePlugins() - { - // retrieves the plugins descriptors - List descriptors = new ArrayList<>(); - for (PluginWrapper plugin : plugins.values()) - { - descriptors.add(plugin.getDescriptor()); - } - - // retrieves the plugins descriptors from the resolvedPlugins list. This allows to load plugins that have already loaded dependencies. - for (PluginWrapper plugin : resolvedPlugins) - { - descriptors.add(plugin.getDescriptor()); - } - - DependencyResolver.Result result = dependencyResolver.resolve(descriptors); - - if (result.hasCyclicDependency()) - { - throw new DependencyResolver.CyclicDependencyException(); - } - - List notFoundDependencies = result.getNotFoundDependencies(); - if (!notFoundDependencies.isEmpty()) - { - throw new DependencyResolver.DependenciesNotFoundException(notFoundDependencies); - } - - List wrongVersionDependencies = result.getWrongVersionDependencies(); - if (!wrongVersionDependencies.isEmpty()) - { - throw new DependencyResolver.DependenciesWrongVersionException(wrongVersionDependencies); - } - - List sortedPlugins = result.getSortedPlugins(); - - // move plugins from "unresolved" to "resolved" - for (String pluginId : sortedPlugins) - { - PluginWrapper pluginWrapper = plugins.get(pluginId); - - //The plugin is already resolved. Don't put a copy in the resolvedPlugins. - if (resolvedPlugins.contains(pluginWrapper)) - { - continue; - } - - if (unresolvedPlugins.remove(pluginWrapper)) - { - PluginState pluginState = pluginWrapper.getPluginState(); - if (pluginState != PluginState.DISABLED) - { - pluginWrapper.setPluginState(PluginState.RESOLVED); - } - - resolvedPlugins.add(pluginWrapper); - - firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); - } - } - } - - @Override - public RuntimeMode getRuntimeMode() - { - return ExternalPluginManager.isDevelopmentMode() ? RuntimeMode.DEVELOPMENT : RuntimeMode.DEPLOYMENT; - } - - @Override - public PluginState stopPlugin(String pluginId) - { - if (!plugins.containsKey(pluginId)) - { - throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId)); - } - - PluginWrapper pluginWrapper = getPlugin(pluginId); - org.pf4j.PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); - PluginState pluginState = pluginWrapper.getPluginState(); - if (PluginState.STOPPED == pluginState) - { - log.debug("Already stopped plugin '{}'", getPluginLabel(pluginDescriptor)); - return PluginState.STOPPED; - } - - // test for disabled plugin - if (PluginState.DISABLED == pluginState) - { - // do nothing - return pluginState; - } - - pluginWrapper.getPlugin().stop(); - pluginWrapper.setPluginState(PluginState.STOPPED); - startedPlugins.remove(pluginWrapper); - - firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); - - return pluginWrapper.getPluginState(); - } - - @Override - public boolean unloadPlugin(String pluginId) - { - try - { - PluginState pluginState = stopPlugin(pluginId); - if (PluginState.STARTED == pluginState) - { - return false; - } - - PluginWrapper pluginWrapper = getPlugin(pluginId); - - // remove the plugin - plugins.remove(pluginId); - getResolvedPlugins().remove(pluginWrapper); - - firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); - - // remove the classloader - Map pluginClassLoaders = getPluginClassLoaders(); - if (pluginClassLoaders.containsKey(pluginId)) - { - ClassLoader classLoader = pluginClassLoaders.remove(pluginId); - if (classLoader instanceof Closeable) - { - try - { - ((Closeable) classLoader).close(); - } - catch (IOException e) - { - throw new PluginRuntimeException(e, "Cannot close classloader"); - } - } - } - - return true; - } - catch (IllegalArgumentException e) - { - // ignore not found exceptions because this method is recursive - } - - return false; - } - - @Override - public boolean deletePlugin(String pluginId) - { - if (!plugins.containsKey(pluginId)) - { - throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId)); - } - - PluginWrapper pluginWrapper = getPlugin(pluginId); - // stop the plugin if it's started - PluginState pluginState = stopPlugin(pluginId); - if (PluginState.STARTED == pluginState) - { - log.error("Failed to stop plugin '{}' on delete", pluginId); - return false; - } - - // get an instance of plugin before the plugin is unloaded - // for reason see https://github.com/pf4j/pf4j/issues/309 - - org.pf4j.Plugin plugin = pluginWrapper.getPlugin(); - - if (!unloadPlugin(pluginId)) - { - log.error("Failed to unload plugin '{}' on delete", pluginId); - return false; - } - - // notify the plugin as it's deleted - plugin.delete(); - - Path pluginPath = pluginWrapper.getPluginPath(); - - return pluginRepository.deletePluginPath(pluginPath); - } - - private boolean isPluginEligibleForLoading(Path path) - { - return path.toFile().getName().endsWith(".jar"); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/ExternalPluginClasspath.java b/runelite-client/src/main/java/net/runelite/client/plugins/ExternalPluginClasspath.java deleted file mode 100644 index 841f1b3be7..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/ExternalPluginClasspath.java +++ /dev/null @@ -1,13 +0,0 @@ -package net.runelite.client.plugins; - -import org.pf4j.DevelopmentPluginClasspath; - -class ExternalPluginClasspath extends DevelopmentPluginClasspath -{ - static final String GRADLE_DEPS_PATH = "build/deps"; - - ExternalPluginClasspath() - { - addJarsDirectories(GRADLE_DEPS_PATH); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/ExternalPluginFileFilter.java b/runelite-client/src/main/java/net/runelite/client/plugins/ExternalPluginFileFilter.java deleted file mode 100644 index c9cf9f9878..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/ExternalPluginFileFilter.java +++ /dev/null @@ -1,61 +0,0 @@ -package net.runelite.client.plugins; - -import java.io.File; -import java.io.FileFilter; -import java.util.Arrays; -import java.util.List; - -/** - * Determines whether a {@link File} is an external plugin folder. To be considered a plugin a folder must: - *

- * * Must not be a blacklisted name - * * Have a {@code .gradle.kts} file in the root named after the folder - * * Have a {@code MANIFEST.MF} located at {@code build/tmp/jar/MANIFEST.MF} - */ -public class ExternalPluginFileFilter implements FileFilter -{ - private static final List blacklist = Arrays.asList( - ".git", - "build", - "target", - "release" - ); - - private static final List buildFiles = Arrays.asList( - "%s.gradle.kts", - "%s.gradle" - ); - - @Override - public boolean accept(File pathName) - { - // Check if this path looks like a plugin development directory - if (!pathName.isDirectory()) - { - return false; - } - - String dirName = pathName.getName(); - if (blacklist.contains(dirName)) - { - return false; - } - - // Check if the plugin directory has a MANIFEST.MF which si required for loading - if (!new File(pathName, ExternalPluginManager.DEVELOPMENT_MANIFEST_PATH).exists()) - { - return false; - } - - // By convention plugins their directory is $name and they have a $name.gradle.kts or $name.gradle file in their root - for (String buildFile : buildFiles) - { - if (new File(pathName, String.format(buildFile, dirName)).exists()) - { - return true; - } - } - - return false; - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/ExternalPluginManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/ExternalPluginManager.java deleted file mode 100644 index a7630f9f31..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/ExternalPluginManager.java +++ /dev/null @@ -1,1093 +0,0 @@ -/* - * Copyright (c) 2020, Owain van Brakel - * 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.plugins; - -import com.google.common.collect.Lists; -import com.google.common.graph.GraphBuilder; -import com.google.common.graph.Graphs; -import com.google.common.graph.MutableGraph; -import com.google.inject.Binder; -import com.google.inject.CreationException; -import com.google.inject.Injector; -import com.google.inject.Key; -import com.google.inject.Module; -import java.lang.reflect.InvocationTargetException; -import java.net.MalformedURLException; -import java.net.URL; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; -import javax.swing.JOptionPane; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; -import net.runelite.client.RuneLite; -import static net.runelite.client.RuneLite.EXTERNALPLUGIN_DIR; -import static net.runelite.client.RuneLite.SYSTEM_VERSION; -import net.runelite.client.RuneLiteProperties; -import net.runelite.client.config.Config; -import net.runelite.client.config.ConfigManager; -import net.runelite.client.config.OpenOSRSConfig; -import net.runelite.client.config.RuneLiteConfig; -import net.runelite.client.eventbus.EventBus; -import net.runelite.client.events.ConfigChanged; -import net.runelite.client.events.ExternalPluginChanged; -import net.runelite.client.events.ExternalRepositoryChanged; -import net.runelite.client.ui.ClientUI; -import net.runelite.client.ui.RuneLiteSplashScreen; -import net.runelite.client.util.Groups; -import net.runelite.client.util.MiscUtils; -import net.runelite.client.util.SwingUtil; -import org.jgroups.Message; -import org.pf4j.DefaultPluginManager; -import org.pf4j.DependencyResolver; -import org.pf4j.PluginDependency; -import org.pf4j.PluginRuntimeException; -import org.pf4j.PluginWrapper; -import org.pf4j.update.DefaultUpdateRepository; -import org.pf4j.update.PluginInfo; -import org.pf4j.update.UpdateManager; -import org.pf4j.update.UpdateRepository; -import org.pf4j.update.VerifyException; - -@Slf4j -@Singleton -public class ExternalPluginManager -{ - public static final String DEFAULT_PLUGIN_REPOS = "OpenOSRS:https://raw.githubusercontent.com/open-osrs/plugin-hosting/master/;Plugin-Hub:https://raw.githubusercontent.com/owain94/OpenOSRS-RL-hub-hosting/master/"; - static final String DEVELOPMENT_MANIFEST_PATH = "build/tmp/jar/MANIFEST.MF"; - - public static ArrayList pluginClassLoaders = new ArrayList<>(); - private final PluginManager runelitePluginManager; - private org.pf4j.PluginManager externalPluginManager; - @Getter(AccessLevel.PUBLIC) - private final List repositories = new ArrayList<>(); - private final OpenOSRSConfig openOSRSConfig; - private final EventBus eventBus; - private final ExecutorService executorService; - private final ConfigManager configManager; - private final Map pluginsMap = new HashMap<>(); - @Getter(AccessLevel.PUBLIC) - private static final boolean developmentMode = RuneLiteProperties.getPluginDevelopmentPath().length > 0; - @Getter(AccessLevel.PUBLIC) - private final Map> pluginsInfoMap = new HashMap<>(); - private final Groups groups; - @Getter(AccessLevel.PUBLIC) - private UpdateManager updateManager; - private final Set pluginTypes = Set.of(PluginType.values()); - private final boolean safeMode; - - @Inject - public ExternalPluginManager( - @Named("safeMode") final boolean safeMode, - PluginManager pluginManager, - OpenOSRSConfig openOSRSConfig, - EventBus eventBus, - ExecutorService executorService, - ConfigManager configManager, - Groups groups) - { - this.safeMode = safeMode; - this.runelitePluginManager = pluginManager; - this.openOSRSConfig = openOSRSConfig; - this.eventBus = eventBus; - this.executorService = executorService; - this.configManager = configManager; - this.groups = groups; - - //noinspection ResultOfMethodCallIgnored - EXTERNALPLUGIN_DIR.mkdirs(); - - initPluginManager(); - - groups.getMessageStringSubject() - .subscribe(this::receive); - } - - private void initPluginManager() - { - externalPluginManager = new ExternalPf4jPluginManager(this); - externalPluginManager.setSystemVersion(SYSTEM_VERSION); - } - - public boolean doesGhRepoExist(String owner, String name) - { - return doesRepoExist("gh:" + owner + "/" + name); - } - - /** - * Note that {@link org.pf4j.update.UpdateManager#addRepository} checks if the repo exists, however it throws an exception which is bad - */ - public boolean doesRepoExist(String id) - { - return repositories.stream().anyMatch((repo) -> repo.getId().equals(id)); - } - - private static URL toRepositoryUrl(String owner, String name) throws MalformedURLException - { - return new URL("https://raw.githubusercontent.com/" + owner + "/" + name + "/master/"); - } - - public static boolean testGHRepository(String owner, String name) - { - try - { - return testRepository(toRepositoryUrl(owner, name)); - } - catch (MalformedURLException e) - { - e.printStackTrace(); - } - return false; - } - - public static boolean testRepository(URL url) - { - final List repositories = new ArrayList<>(); - repositories.add(new DefaultUpdateRepository("repository-testing", url)); - DefaultPluginManager testPluginManager = new DefaultPluginManager(EXTERNALPLUGIN_DIR.toPath()); - UpdateManager updateManager = new UpdateManager(testPluginManager, repositories); - - return updateManager.getPlugins().size() <= 0; - } - - public static Predicate not(Predicate t) - { - return t.negate(); - } - - public void startExternalPluginManager() - { - try - { - externalPluginManager.loadPlugins(); - } - catch (Exception ex) - { - if (ex instanceof DependencyResolver.DependenciesNotFoundException) - { - List deps = ((DependencyResolver.DependenciesNotFoundException) ex).getDependencies(); - - log.error("The following dependencies are missing: {}", deps); - - for (String dep : deps) - { - updateManager.installPlugin(dep, null); - } - - startExternalPluginManager(); - } - else - { - log.error("Could not load plugins", ex); - } - } - } - - public void startExternalUpdateManager() - { - if (!tryLoadNewFormat()) - { - log.debug("Load new format failed."); - loadOldFormat(); - } - - updateManager = new UpdateManager(externalPluginManager, repositories); - saveConfig(); - } - - public boolean tryLoadNewFormat() - { - try - { - duplicateCheck(); - log.debug("Trying to load new format: {}", openOSRSConfig.getExternalRepositories()); - repositories.clear(); - - for (String keyval : openOSRSConfig.getExternalRepositories().split(";")) - { - String[] split = keyval.split("\\|"); - if (split.length != 2) - { - log.debug("Split length invalid: {}", keyval); - repositories.clear(); - return false; - } - String id = split[0]; - String url = split[1]; - if (!url.endsWith("/")) - { - url = url.concat("/"); - } - - if (id.contains("https://raw.githubusercontent.com/")) - { - id = "gh:" + id.substring(id.indexOf("https://raw.githubusercontent.com/")).replace("/master", "") - .replace("https://raw.githubusercontent.com/", ""); - - if (id.endsWith("/")) - { - id = id.substring(0, id.lastIndexOf("/")); - } - } - - repositories.add(new DefaultUpdateRepository(id, new URL(url))); - } - } - catch (ArrayIndexOutOfBoundsException | MalformedURLException e) - { - log.error("Error in new format", e); - repositories.clear(); - return false; - } - return true; - } - - public void loadOldFormat() - { - try - { - log.debug("Loading old format."); - repositories.clear(); - - for (String keyval : openOSRSConfig.getExternalRepositories().split(";")) - { - log.debug("KeyVal: {}", keyval); - String id = keyval.substring(0, keyval.lastIndexOf(":https")); - String url = keyval.substring(keyval.lastIndexOf("https")); - - DefaultUpdateRepository defaultRepo = new DefaultUpdateRepository(id, new URL(url)); - repositories.add(defaultRepo); - log.debug("Added Repo: {}", defaultRepo.getUrl()); - } - } - catch (MalformedURLException e) - { - log.error("Old repository format contained malformed url", e); - } - catch (StringIndexOutOfBoundsException e) - { - log.error("Error loading external repositories. They have been reset."); - openOSRSConfig.setExternalRepositories(DEFAULT_PLUGIN_REPOS); - } - - updateManager = new UpdateManager(externalPluginManager, repositories); - } - - public void addGHRepository(String owner, String name) - { - try - { - addRepository("gh:" + owner + "/" + name, toRepositoryUrl(owner, name)); - } - catch (MalformedURLException e) - { - log.error("GitHub repostitory could not be added (owner={}, name={})", owner, name, e); - } - } - - public void addRepository(String key, URL url) - { - DefaultUpdateRepository respository = new DefaultUpdateRepository(key, url); - updateManager.addRepository(respository); - eventBus.post(ExternalRepositoryChanged.class, new ExternalRepositoryChanged(key, true)); - saveConfig(); - } - - public void removeRepository(String owner) - { - updateManager.removeRepository(owner); - eventBus.post(ExternalRepositoryChanged.class, new ExternalRepositoryChanged(owner, false)); - saveConfig(); - } - - private void saveConfig() - { - StringBuilder config = new StringBuilder(); - - for (UpdateRepository repository : updateManager.getRepositories()) - { - config.append(repository.getId()); - config.append("|"); - config.append(MiscUtils.urlToStringEncoded(repository.getUrl())); - config.append(";"); - } - config.deleteCharAt(config.lastIndexOf(";")); - openOSRSConfig.setExternalRepositories(config.toString()); - } - - public void setWarning(boolean val) - { - configManager.setConfiguration("openosrs", "warning", val); - } - - public boolean getWarning() - { - return openOSRSConfig.warning(); - } - - /** - * This method is a fail safe to ensure that no duplicate - * repositories end up getting saved to the config. - *

- * Configs that had duplicate repos prior to this should - * be updated and set correctly. - */ - private void duplicateCheck() - { - String[] split = openOSRSConfig.getExternalRepositories().split(";"); - - if (split.length <= 0) - { - return; - } - - Set strings = new HashSet<>(); - boolean duplicates = false; - - for (String s : split) - { - if (strings.contains(s)) - { - log.error("Duplicate Repo: {}", s); - duplicates = true; - continue; - } - strings.add(s); - } - - if (!duplicates) - { - log.info("No duplicates found."); - return; - } - - StringBuilder sb = new StringBuilder(); - - for (String string : strings) - { - sb.append(string); - sb.append(";"); - } - - sb.deleteCharAt(sb.lastIndexOf(";")); - String duplicateFix = sb.toString(); - - log.info("Duplicate Repos detected, setting them to: {}", duplicateFix); - openOSRSConfig.setExternalRepositories(duplicateFix); - } - - private void scanAndInstantiate(List plugins, boolean init, boolean initConfig) - { - RuneLiteSplashScreen.stage(.66, "Loading external plugins"); - MutableGraph> graph = GraphBuilder - .directed() - .build(); - - for (Plugin plugin : plugins) - { - Class clazz = plugin.getClass(); - PluginDescriptor pluginDescriptor = clazz.getAnnotation(PluginDescriptor.class); - - try - { - if (pluginDescriptor == null) - { - if (Plugin.class.isAssignableFrom(clazz)) - { - log.warn("Class {} is a plugin, but has no plugin descriptor", clazz); - } - continue; - } - else if (!Plugin.class.isAssignableFrom(clazz)) - { - log.warn("Class {} has plugin descriptor, but is not a plugin", clazz); - continue; - } - else if (!pluginTypes.contains(pluginDescriptor.type())) - { - continue; - } - } - catch (EnumConstantNotPresentException e) - { - log.warn("{} has an invalid plugin type of {}", clazz, e.getMessage()); - continue; - } - - if (safeMode && !pluginDescriptor.loadInSafeMode()) - { - log.debug("Disabling {} due to safe mode", clazz); - // also disable the plugin from autostarting later - configManager.unsetConfiguration(RuneLiteConfig.GROUP_NAME, clazz.getSimpleName().toLowerCase()); - continue; - } - - @SuppressWarnings("unchecked") Class pluginClass = (Class) clazz; - graph.addNode(pluginClass); - } - - // Build plugin graph - for (Class pluginClazz : graph.nodes()) - { - net.runelite.client.plugins.PluginDependency[] pluginDependencies = pluginClazz.getAnnotationsByType(net.runelite.client.plugins.PluginDependency.class); - - for (net.runelite.client.plugins.PluginDependency pluginDependency : pluginDependencies) - { - if (graph.nodes().contains(pluginDependency.value())) - { - graph.putEdge(pluginClazz, pluginDependency.value()); - } - } - } - - if (Graphs.hasCycle(graph)) - { - throw new RuntimeException("Plugin dependency graph contains a cycle!"); - } - - List>> sortedPlugins = PluginManager.topologicalGroupSort(graph); - sortedPlugins = Lists.reverse(sortedPlugins); - AtomicInteger loaded = new AtomicInteger(); - - final long start = System.currentTimeMillis(); - - List scannedPlugins = new CopyOnWriteArrayList<>(); - sortedPlugins.forEach(group -> - { - List> curGroup = new ArrayList<>(); - group.forEach(pluginClazz -> - curGroup.add(executorService.submit(() -> - { - Plugin plugininst; - try - { - //noinspection unchecked - plugininst = instantiate(scannedPlugins, (Class) pluginClazz, init, initConfig); - - if (plugininst == null) - { - return; - } - - scannedPlugins.add(plugininst); - } - catch (PluginInstantiationException e) - { - log.warn("Error instantiating plugin!", e); - return; - } - - loaded.getAndIncrement(); - - RuneLiteSplashScreen.stage(.67, .75, "Loading external plugins", loaded.get(), scannedPlugins.size()); - }))); - curGroup.forEach(future -> - { - try - { - future.get(); - } - catch (InterruptedException | ExecutionException e) - { - log.warn("Could not instantiate external plugin", e); - } - }); - }); - - log.info("External plugin instantiation took {}ms", System.currentTimeMillis() - start); - } - - @SuppressWarnings("unchecked") - private Plugin instantiate(List scannedPlugins, Class clazz, boolean init, boolean initConfig) - throws PluginInstantiationException - { - net.runelite.client.plugins.PluginDependency[] pluginDependencies = - clazz.getAnnotationsByType(net.runelite.client.plugins.PluginDependency.class); - List deps = new ArrayList<>(); - for (net.runelite.client.plugins.PluginDependency pluginDependency : pluginDependencies) - { - Optional dependency = - Stream.concat(runelitePluginManager.getPlugins().stream(), scannedPlugins.stream()) - .filter(p -> p.getClass() == pluginDependency.value()).findFirst(); - if (dependency.isEmpty()) - { - throw new PluginInstantiationException( - "Unmet dependency for " + clazz.getSimpleName() + ": " + pluginDependency.value().getSimpleName()); - } - deps.add(dependency.get()); - } - - log.info("Loading plugin {}", clazz.getSimpleName()); - Plugin plugin; - try - { - plugin = clazz.getDeclaredConstructor().newInstance(); - } - catch (ThreadDeath e) - { - throw e; - } - catch (Throwable ex) - { - throw new PluginInstantiationException(ex); - } - - try - { - Injector parent = RuneLite.getInjector(); - - if (deps.size() > 1) - { - List modules = new ArrayList<>(deps.size()); - for (Plugin p : deps) - { - // Create a module for each dependency - Module module = (Binder binder) -> - { - binder.bind((Class) p.getClass()).toInstance(p); - binder.install(p); - }; - modules.add(module); - } - - // Create a parent injector containing all of the dependencies - parent = parent.createChildInjector(modules); - } - else if (!deps.isEmpty()) - { - // With only one dependency we can simply use its injector - parent = deps.get(0).injector; - } - - // Create injector for the module - Module pluginModule = (Binder binder) -> - { - // Since the plugin itself is a module, it won't bind itself, so we'll bind it here - binder.bind(clazz).toInstance(plugin); - binder.install(plugin); - }; - Injector pluginInjector = parent.createChildInjector(pluginModule); - pluginInjector.injectMembers(plugin); - plugin.injector = pluginInjector; - - if (initConfig) - { - for (Key key : pluginInjector.getBindings().keySet()) - { - Class type = key.getTypeLiteral().getRawType(); - if (Config.class.isAssignableFrom(type)) - { - if (type.getPackageName().startsWith(plugin.getClass().getPackageName())) - { - Config config = (Config) pluginInjector.getInstance(key); - configManager.setDefaultConfiguration(config, false); - } - } - } - } - - if (init) - { - try - { - SwingUtil.syncExec(() -> - { - try - { - runelitePluginManager.add(plugin); - runelitePluginManager.startPlugin(plugin); - eventBus.post(ExternalPluginChanged.class, - new ExternalPluginChanged(pluginsMap.get(plugin.getClass().getSimpleName()), plugin, - true)); - } - catch (PluginInstantiationException e) - { - throw new RuntimeException(e); - } - }); - } - catch (Exception ex) - { - log.warn("unable to start plugin", ex); - } - } - else - { - runelitePluginManager.add(plugin); - } - } - catch (CreationException ex) - { - throw new PluginInstantiationException(ex); - } - catch (NoClassDefFoundError ex) - { - log.error("Plugin {} is outdated", clazz.getSimpleName()); - return null; - } - - log.debug("Loaded plugin {}", clazz.getSimpleName()); - return plugin; - } - - private void checkDepsAndStart(List startedPlugins, List scannedPlugins, PluginWrapper pluginWrapper) - { - boolean depsLoaded = true; - for (PluginDependency dependency : pluginWrapper.getDescriptor().getDependencies()) - { - if (startedPlugins.stream().noneMatch(pl -> pl.getPluginId().equals(dependency.getPluginId()))) - { - depsLoaded = false; - } - } - - if (!depsLoaded) - { - // This should never happen but can crash the client - return; - } - - scannedPlugins.addAll(loadPlugin(pluginWrapper.getPluginId())); - } - - public void loadPlugins() - { - externalPluginManager.startPlugins(); - List startedPlugins = getStartedPlugins(); - List scannedPlugins = new ArrayList<>(); - - for (PluginWrapper plugin : startedPlugins) - { - checkDepsAndStart(startedPlugins, scannedPlugins, plugin); - } - - scanAndInstantiate(scannedPlugins, false, false); - - if (groups.getInstanceCount() > 1) - { - for (String pluginId : getDisabledPlugins()) - { - groups.sendString("STOPEXTERNAL;" + pluginId); - } - } - else - { - for (String pluginId : getDisabledPlugins()) - { - externalPluginManager.enablePlugin(pluginId); - externalPluginManager.deletePlugin(pluginId); - } - } - } - - private List loadPlugin(String pluginId) - { - List scannedPlugins = new ArrayList<>(); - try - { - List extensions = externalPluginManager.getExtensions(Plugin.class, pluginId); - for (Plugin plugin : extensions) - { - pluginClassLoaders.add(plugin.getClass().getClassLoader()); - - pluginsMap.remove(plugin.getClass().getSimpleName()); - pluginsMap.put(plugin.getClass().getSimpleName(), pluginId); - - pluginsInfoMap.remove(plugin.getClass().getSimpleName()); - - AtomicReference support = new AtomicReference<>(""); - - updateManager.getRepositories().forEach(repository -> - repository.getPlugins().forEach((key, value) -> - { - if (key.equals(pluginId)) - { - support.set(value.projectUrl); - } - })); - - pluginsInfoMap.put( - plugin.getClass().getSimpleName(), - new HashMap<>() - {{ - put("version", externalPluginManager.getPlugin(pluginId).getDescriptor().getVersion()); - put("id", externalPluginManager.getPlugin(pluginId).getDescriptor().getPluginId()); - put("provider", externalPluginManager.getPlugin(pluginId).getDescriptor().getProvider()); - put("support", support.get()); - }} - ); - - scannedPlugins.add(plugin); - } - } - catch (Throwable ex) - { - log.error("Plugin {} could not be loaded.", pluginId, ex); - } - - return scannedPlugins; - } - - private Path stopPlugin(String pluginId) - { - List startedPlugins = List.copyOf(getStartedPlugins()); - - for (PluginWrapper pluginWrapper : startedPlugins) - { - if (!pluginId.equals(pluginWrapper.getDescriptor().getPluginId())) - { - continue; - } - - List extensions = externalPluginManager.getExtensions(Plugin.class, pluginId); - - for (Plugin plugin : runelitePluginManager.getPlugins()) - { - if (!extensions.get(0).getClass().getName().equals(plugin.getClass().getName())) - { - continue; - } - - try - { - SwingUtil.syncExec(() -> - { - try - { - runelitePluginManager.stopPlugin(plugin); - } - catch (Exception e2) - { - throw new RuntimeException(e2); - } - }); - runelitePluginManager.remove(plugin); - pluginClassLoaders.remove(plugin.getClass().getClassLoader()); - - eventBus.post(ExternalPluginChanged.class, new ExternalPluginChanged(pluginId, plugin, false)); - - return pluginWrapper.getPluginPath(); - } - catch (Exception ex) - { - log.warn("unable to stop plugin", ex); - return null; - } - } - } - - return null; - } - - public boolean install(String pluginId) throws VerifyException - { - if (getDisabledPlugins().contains(pluginId)) - { - externalPluginManager.enablePlugin(pluginId); - externalPluginManager.startPlugin(pluginId); - - groups.broadcastSring("STARTEXTERNAL;" + pluginId); - scanAndInstantiate(loadPlugin(pluginId), true, false); - - return true; - } - - if (getStartedPlugins().stream().anyMatch(ev -> ev.getPluginId().equals(pluginId))) - { - return true; - } - - try - { - if (!developmentMode) - { - PluginInfo.PluginRelease latest = updateManager.getLastPluginRelease(pluginId); - - // Null version returns the last release version of this plugin for given system version - if (latest == null) - { - try - { - SwingUtil.syncExec(() -> - JOptionPane.showMessageDialog(ClientUI.getFrame(), - pluginId + " is outdated and cannot be installed", - "Installation error", - JOptionPane.ERROR_MESSAGE)); - } - catch (InvocationTargetException | InterruptedException ignored) - { - return false; - } - - return true; - } - - updateManager.installPlugin(pluginId, null); - } - else - { - // In development mode our plugin will already be present in a repository, so we can just load it - externalPluginManager.loadPlugins(); - externalPluginManager.startPlugin(pluginId); - } - - scanAndInstantiate(loadPlugin(pluginId), true, true); - - groups.broadcastSring("STARTEXTERNAL;" + pluginId); - } - catch (DependencyResolver.DependenciesNotFoundException ex) - { - uninstall(pluginId); - - for (String dep : ex.getDependencies()) - { - install(dep); - } - - install(pluginId); - } - return false; - } - - public boolean uninstall(String pluginId) - { - return uninstall(pluginId, false); - } - - public boolean uninstall(String pluginId, boolean skip) - { - Path pluginPath = stopPlugin(pluginId); - - if (pluginPath == null) - { - return false; - } - - externalPluginManager.stopPlugin(pluginId); - - if (skip) - { - return true; - } - - if (groups.getInstanceCount() > 1) - { - groups.sendString("STOPEXTERNAL;" + pluginId); - } - else - { - externalPluginManager.deletePlugin(pluginId); - } - - return true; - } - - public void update() - { - if (groups.getInstanceCount() > 1) - { - // Do not update when there is more than one client open -> api might contain changes - log.info("Not updating external plugins since there is more than 1 client open"); - return; - } - else if (developmentMode) - { - log.info("Not updating because we're running in developer mode"); - return; - } - - RuneLiteSplashScreen.stage(.59, "Updating external plugins"); - - boolean error = false; - if (updateManager.hasUpdates()) - { - List updates = updateManager.getUpdates(); - for (PluginInfo plugin : updates) - { - PluginInfo.PluginRelease lastRelease = updateManager.getLastPluginRelease(plugin.id); - String lastVersion = lastRelease.version; - try - { - - RuneLiteSplashScreen.stage(.59, "Updating " + plugin.id + " to version " + lastVersion); - boolean updated = updateManager.updatePlugin(plugin.id, lastVersion); - - if (!updated) - { - log.warn("Cannot update plugin '{}'", plugin.id); - error = true; - } - } - catch (PluginRuntimeException ex) - { - // This should never happen but can crash the client - log.warn("Cannot update plugin '{}', the user probably has another client open", plugin.id); - error = true; - break; - } - } - } - - if (error) - { - initPluginManager(); - startExternalUpdateManager(); - startExternalPluginManager(); - } - } - - public Set getDependencies() - { - Set deps = new HashSet<>(); - List startedPlugins = getStartedPlugins(); - - for (PluginWrapper pluginWrapper : startedPlugins) - { - for (PluginDependency pluginDependency : pluginWrapper.getDescriptor().getDependencies()) - { - deps.add(pluginDependency.getPluginId()); - } - } - - return deps; - } - - public List getDisabledPlugins() - { - return externalPluginManager.getResolvedPlugins() - .stream() - .filter(not(externalPluginManager.getStartedPlugins()::contains)) - .map(PluginWrapper::getPluginId) - .collect(Collectors.toList()); - } - - public List getStartedPlugins() - { - return externalPluginManager.getStartedPlugins(); - } - - public Boolean reloadStart(String pluginId) - { - externalPluginManager.loadPlugins(); - externalPluginManager.startPlugin(pluginId); - - List startedPlugins = List.copyOf(getStartedPlugins()); - List scannedPlugins = new ArrayList<>(); - - for (PluginWrapper pluginWrapper : startedPlugins) - { - if (!pluginId.equals(pluginWrapper.getDescriptor().getPluginId())) - { - continue; - } - - checkDepsAndStart(startedPlugins, scannedPlugins, pluginWrapper); - } - - scanAndInstantiate(scannedPlugins, true, false); - - groups.broadcastSring("STARTEXTERNAL;" + pluginId); - - return true; - } - - public void receive(Message message) - { - if (message.getObject() instanceof ConfigChanged) - { - return; - } - - String[] messageObject = ((String) message.getObject()).split(";"); - - if (messageObject.length < 2) - { - return; - } - - String command = messageObject[0]; - String pluginId = messageObject[1]; - - switch (command) - { - case "STARTEXTERNAL": - externalPluginManager.loadPlugins(); - externalPluginManager.startPlugin(pluginId); - - List startedPlugins = List.copyOf(getStartedPlugins()); - List scannedPlugins = new ArrayList<>(); - - for (PluginWrapper pluginWrapper : startedPlugins) - { - if (!pluginId.equals(pluginWrapper.getDescriptor().getPluginId())) - { - continue; - } - - checkDepsAndStart(startedPlugins, scannedPlugins, pluginWrapper); - } - - scanAndInstantiate(scannedPlugins, true, false); - - break; - - case "STOPEXTERNAL": - uninstall(pluginId, true); - externalPluginManager.unloadPlugin(pluginId); - groups.send(message.getSrc(), "STOPPEDEXTERNAL;" + pluginId); - break; - - case "STOPPEDEXTERNAL": - groups.getMessageMap().get(pluginId).remove(message.getSrc()); - - if (groups.getMessageMap().get(pluginId).size() == 0) - { - groups.getMessageMap().remove(pluginId); - externalPluginManager.deletePlugin(pluginId); - } - - break; - } - } - -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/Plugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/Plugin.java index ddfdd9bb61..a36a1f40e6 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/Plugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/Plugin.java @@ -24,30 +24,12 @@ */ package net.runelite.client.plugins; -import com.google.common.collect.ImmutableSet; import com.google.inject.Binder; import com.google.inject.Injector; import com.google.inject.Module; -import io.reactivex.rxjava3.functions.Consumer; -import java.lang.reflect.Method; -import java.util.Set; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.Value; -import lombok.extern.slf4j.Slf4j; -import net.runelite.api.events.Event; -import net.runelite.client.eventbus.EventBus; -import net.runelite.client.eventbus.EventScheduler; -import net.runelite.client.eventbus.Subscribe; -import org.pf4j.ExtensionPoint; -@Slf4j -public abstract class Plugin implements Module, ExtensionPoint +public abstract class Plugin implements Module { - private final Set annotatedSubscriptions = findSubscriptions(); - private final Object annotatedSubsLock = new Object(); - - @Getter(AccessLevel.PROTECTED) protected Injector injector; @Override @@ -63,65 +45,15 @@ public abstract class Plugin implements Module, ExtensionPoint { } - @SuppressWarnings("unchecked") - final void addAnnotatedSubscriptions(EventBus eventBus) - { - annotatedSubscriptions.forEach(sub -> eventBus.subscribe(sub.type, annotatedSubsLock, sub.method, sub.takeUntil, sub.subscribe, sub.observe)); - } - - final void removeAnnotatedSubscriptions(EventBus eventBus) - { - eventBus.unregister(annotatedSubsLock); - } - - private Set findSubscriptions() - { - ImmutableSet.Builder builder = ImmutableSet.builder(); - - for (Method method : this.getClass().getDeclaredMethods()) - { - Subscribe annotation = method.getAnnotation(Subscribe.class); - if (annotation == null) - { - continue; - } - - assert method.getParameterCount() == 1 : "Methods annotated with @Subscribe should have only one parameter"; - - Class type = method.getParameterTypes()[0]; - - assert Event.class.isAssignableFrom(type) : "Parameters of methods annotated with @Subscribe should implement net.runelite.api.events.Event"; - assert method.getReturnType() == void.class : "Methods annotated with @Subscribe should have a void return type"; - if (method.getExceptionTypes().length != 0) - { - log.warn("Event handlers should handle all checked exceptions themselves " + method.toString()); - } - - method.setAccessible(true); - - Subscription sub = new Subscription(type.asSubclass(Event.class), event -> method.invoke(this, event), annotation.takeUntil(), annotation.subscribe(), annotation.observe()); - - builder.add(sub); - } - - return builder.build(); - } - - @Value - @SuppressWarnings("rawtypes") - private static class Subscription - { - Class type; - Consumer method; - int takeUntil; - EventScheduler subscribe; - EventScheduler observe; - } - public void resetConfiguration() { } + public final Injector getInjector() + { + return injector; + } + public String getName() { return getClass().getAnnotation(PluginDescriptor.class).name(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/PluginDescriptor.java b/runelite-client/src/main/java/net/runelite/client/plugins/PluginDescriptor.java index e5d208b7ea..57a7a4718f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/PluginDescriptor.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/PluginDescriptor.java @@ -48,16 +48,16 @@ public @interface PluginDescriptor */ String[] tags() default {}; + boolean enabledByDefault() default true; + /** * Whether or not plugin is hidden from configuration panel */ boolean hidden() default false; + boolean developerPlugin() default false; + boolean loadWhenOutdated() default false; - PluginType type() default PluginType.UNCATEGORIZED; - - boolean enabledByDefault() default true; - boolean loadInSafeMode() default true; } 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 3386965652..5b77ac6c12 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 @@ -25,8 +25,6 @@ package net.runelite.client.plugins; import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Stopwatch; -import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.graph.Graph; import com.google.common.graph.GraphBuilder; @@ -39,50 +37,45 @@ import com.google.inject.CreationException; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.Module; -import io.reactivex.rxjava3.schedulers.Schedulers; -import java.io.File; import java.io.IOException; +import java.lang.invoke.CallSite; +import java.lang.invoke.LambdaMetafactory; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; +import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.BiConsumer; import java.util.stream.Collectors; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Provider; import javax.inject.Singleton; import javax.swing.SwingUtilities; -import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import net.runelite.client.RuneLite; import net.runelite.client.config.Config; -import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigManager; import net.runelite.client.config.RuneLiteConfig; import net.runelite.client.eventbus.EventBus; -import net.runelite.client.events.ConfigChanged; +import net.runelite.client.eventbus.Subscribe; import net.runelite.client.events.PluginChanged; import net.runelite.client.events.SessionClose; import net.runelite.client.events.SessionOpen; -import net.runelite.client.plugins.config.PluginConfigurationDescriptor; +import net.runelite.client.task.Schedule; import net.runelite.client.task.ScheduledMethod; import net.runelite.client.task.Scheduler; -import net.runelite.client.ui.RuneLiteSplashScreen; +import net.runelite.client.ui.SplashScreen; import net.runelite.client.util.GameEventManager; -import net.runelite.client.util.Groups; -import net.runelite.client.util.SwingUtil; -import org.jgroups.Message; -import org.pf4j.Extension; +import net.runelite.client.util.ReflectUtil; @Singleton @Slf4j @@ -93,20 +86,14 @@ public class PluginManager */ private static final String PLUGIN_PACKAGE = "net.runelite.client.plugins"; + private final boolean developerMode; private final boolean safeMode; private final EventBus eventBus; private final Scheduler scheduler; - private final ExecutorService executorService; private final ConfigManager configManager; private final Provider sceneTileManager; private final List plugins = new CopyOnWriteArrayList<>(); private final List activePlugins = new CopyOnWriteArrayList<>(); - @Getter - private final List fakePlugins = new ArrayList<>(); - private final String runeliteGroupName = RuneLiteConfig.class - .getAnnotation(ConfigGroup.class).value(); - private final Groups groups; - private final File settingsFileInput; @Setter boolean isOutdated; @@ -114,51 +101,36 @@ public class PluginManager @Inject @VisibleForTesting PluginManager( + @Named("developerMode") final boolean developerMode, @Named("safeMode") final boolean safeMode, final EventBus eventBus, final Scheduler scheduler, - final ExecutorService executorService, final ConfigManager configManager, - final Provider sceneTileManager, - final Groups groups, - final @Named("config") File config) + final Provider sceneTileManager) { + this.developerMode = developerMode; this.safeMode = safeMode; this.eventBus = eventBus; this.scheduler = scheduler; - this.executorService = executorService; this.configManager = configManager; this.sceneTileManager = sceneTileManager; - this.groups = groups; - this.settingsFileInput = config; - - if (eventBus != null) - { - eventBus.subscribe(SessionOpen.class, this, this::onSessionOpen); - eventBus.subscribe(SessionClose.class, this, this::onSessionClose); - } - - if (groups != null) - { - groups.getMessageStringSubject() - .subscribeOn(Schedulers.from(SwingUtilities::invokeLater)) - .subscribe(this::receive); - } } - private void onSessionOpen(SessionOpen event) + @Subscribe + public void onSessionOpen(SessionOpen event) { refreshPlugins(); } - private void onSessionClose(SessionClose event) + @Subscribe + public void onSessionClose(SessionClose event) { refreshPlugins(); } private void refreshPlugins() { - loadDefaultPluginConfiguration(); + loadDefaultPluginConfiguration(null); SwingUtilities.invokeLater(() -> { for (Plugin plugin : getPlugins()) @@ -196,20 +168,6 @@ public class PluginManager Class type = key.getTypeLiteral().getRawType(); if (Config.class.isAssignableFrom(type)) { - Extension externalDescriptor = plugin.getClass().getAnnotation(Extension.class); - if (externalDescriptor != null) - { - // external - Config config = (Config) injector.getInstance(key); - Class actualClass = config.getClass().getInterfaces()[0]; - - if (actualClass.getPackageName().startsWith(plugin.getClass().getPackageName())) - { - return config; - } - continue; - } - return (Config) injector.getInstance(key); } } @@ -234,6 +192,7 @@ public class PluginManager plugins = getPlugins(); } plugins.forEach(pl -> injectors.add(pl.getInjector())); + List list = new ArrayList<>(); for (Injector injector : injectors) { @@ -247,10 +206,11 @@ public class PluginManager } } } + return list; } - public void loadDefaultPluginConfiguration() + public void loadDefaultPluginConfiguration(Collection plugins) { try { @@ -269,42 +229,10 @@ public class PluginManager } } - public void loadFakePluginConfiguration() - { - try - { - for (Object config : fakePlugins.stream().map(PluginConfigurationDescriptor::getConfig).toArray()) - { - if (config == null) - { - continue; - } - - configManager.setDefaultConfiguration(config, false); - } - } - catch (ThreadDeath e) - { - throw e; - } - catch (Throwable ex) - { - log.warn("Unable to reset plugin configuration", ex); - } - } - - public void loadCorePlugins() throws IOException - { - plugins.addAll(scanAndInstantiate(getClass().getClassLoader(), PLUGIN_PACKAGE, false)); - } - public void startPlugins() { List scannedPlugins = new ArrayList<>(plugins); int loaded = 0; - AtomicInteger started = new AtomicInteger(); - - final Stopwatch timer = Stopwatch.createStarted(); for (Plugin plugin : scannedPlugins) { try @@ -313,10 +241,7 @@ public class PluginManager { try { - if (startPlugin(plugin)) - { - started.incrementAndGet(); - } + startPlugin(plugin); } catch (PluginInstantiationException ex) { @@ -331,46 +256,43 @@ public class PluginManager } loaded++; - - RuneLiteSplashScreen.stage(.80, 1, "Starting plugins", loaded, scannedPlugins.size()); + SplashScreen.stage(.80, 1, null, "Starting plugins", loaded, scannedPlugins.size(), false); } - - log.debug("Started {}/{} plugins in {}", started, loaded, timer); } - @SuppressWarnings("unchecked") - List scanAndInstantiate(ClassLoader classLoader, String packageName, boolean external) throws IOException + public void loadCorePlugins() throws IOException, PluginInstantiationException + { + SplashScreen.stage(.59, null, "Loading Plugins"); + ClassPath classPath = ClassPath.from(getClass().getClassLoader()); + + List> plugins = classPath.getTopLevelClassesRecursive(PLUGIN_PACKAGE).stream() + .map(ClassInfo::load) + .collect(Collectors.toList()); + + loadPlugins(plugins, (loaded, total) -> + SplashScreen.stage(.60, .70, null, "Loading Plugins", loaded, total, false)); + } + + public List loadPlugins(List> plugins, BiConsumer onPluginLoaded) throws PluginInstantiationException { - RuneLiteSplashScreen.stage(.60, "Loading plugins"); MutableGraph> graph = GraphBuilder .directed() .build(); - ClassPath classPath = ClassPath.from(classLoader); - - ImmutableSet classes = packageName == null ? classPath.getAllClasses() - : classPath.getTopLevelClassesRecursive(packageName); - for (ClassInfo classInfo : classes) + for (Class clazz : plugins) { - Class clazz = classInfo.load(); - - if (clazz == null) - { - continue; - } - PluginDescriptor pluginDescriptor = clazz.getAnnotation(PluginDescriptor.class); if (pluginDescriptor == null) { - if (Plugin.class.isAssignableFrom(clazz) && clazz != Plugin.class) + if (clazz.getSuperclass() == Plugin.class) { log.warn("Class {} is a plugin, but has no plugin descriptor", clazz); } continue; } - if (!Plugin.class.isAssignableFrom(clazz)) + if (clazz.getSuperclass() != Plugin.class) { log.warn("Class {} has plugin descriptor, but is not a plugin", clazz); continue; @@ -381,6 +303,11 @@ public class PluginManager continue; } + if (pluginDescriptor.developerPlugin() && !developerMode) + { + continue; + } + if (safeMode && !pluginDescriptor.loadInSafeMode()) { log.debug("Disabling {} due to safe mode", clazz); @@ -389,7 +316,7 @@ public class PluginManager continue; } - @SuppressWarnings("unchecked") Class pluginClass = (Class) clazz; + Class pluginClass = (Class) clazz; graph.addNode(pluginClass); } @@ -409,53 +336,36 @@ public class PluginManager if (Graphs.hasCycle(graph)) { - throw new RuntimeException("Plugin dependency graph contains a cycle!"); + throw new PluginInstantiationException("Plugin dependency graph contains a cycle!"); } - List>> sortedPlugins = topologicalGroupSort(graph); + List> sortedPlugins = topologicalSort(graph); sortedPlugins = Lists.reverse(sortedPlugins); - AtomicInteger loaded = new AtomicInteger(); - final long start = System.currentTimeMillis(); - - List scannedPlugins = new CopyOnWriteArrayList<>(); - sortedPlugins.forEach(group -> + int loaded = 0; + List newPlugins = new ArrayList<>(); + for (Class pluginClazz : sortedPlugins) { - List> curGroup = new ArrayList<>(); - group.forEach(pluginClazz -> - curGroup.add(executorService.submit(() -> - { - Plugin plugin; - try - { - plugin = instantiate(scannedPlugins, (Class) pluginClazz); - scannedPlugins.add(plugin); - } - catch (PluginInstantiationException e) - { - log.warn("Error instantiating plugin!", e); - return; - } - - loaded.getAndIncrement(); - - RuneLiteSplashScreen.stage(.60, .65, "Loading internal plugins", loaded.get(), scannedPlugins.size()); - }))); - curGroup.forEach(future -> + Plugin plugin; + try { - try - { - future.get(); - } - catch (InterruptedException | ExecutionException e) - { - e.printStackTrace(); - } - }); - }); + plugin = instantiate(this.plugins, (Class) pluginClazz); + newPlugins.add(plugin); + this.plugins.add(plugin); + } + catch (PluginInstantiationException ex) + { + log.warn("Error instantiating plugin!", ex); + } - log.info("Plugin instantiation took {}ms", System.currentTimeMillis() - start); - return scannedPlugins; + loaded++; + if (onPluginLoaded != null) + { + onPluginLoaded.accept(loaded, sortedPlugins.size()); + } + } + + return newPlugins; } public boolean startPlugin(Plugin plugin) throws PluginInstantiationException @@ -473,7 +383,6 @@ public class PluginManager try { plugin.startUp(); - plugin.addAnnotatedSubscriptions(eventBus); log.debug("Plugin {} is now running", plugin.getClass().getSimpleName()); if (!isOutdated && sceneTileManager != null) @@ -485,22 +394,19 @@ public class PluginManager } } + eventBus.register(plugin); schedule(plugin); - eventBus.post(PluginChanged.class, new PluginChanged(plugin, true)); + eventBus.post(new PluginChanged(plugin, true)); } catch (ThreadDeath e) { - activePlugins.remove(plugin); throw e; } catch (Throwable ex) { - activePlugins.remove(plugin); throw new PluginInstantiationException(ex); } - groups.broadcastSring("STARTPLUGIN;" + plugin.getClass().getSimpleName() + ";" + settingsFileInput.getAbsolutePath()); - return true; } @@ -515,47 +421,43 @@ public class PluginManager } unschedule(plugin); + eventBus.unregister(plugin); try { plugin.shutDown(); - plugin.removeAnnotatedSubscriptions(eventBus); log.debug("Plugin {} is now stopped", plugin.getClass().getSimpleName()); - eventBus.post(PluginChanged.class, new PluginChanged(plugin, false)); - + eventBus.post(new PluginChanged(plugin, false)); } catch (Exception ex) { throw new PluginInstantiationException(ex); } - groups.broadcastSring("STOPPLUGIN;" + plugin.getClass().getSimpleName() + ";" + settingsFileInput.getAbsolutePath()); - return true; } public void setPluginEnabled(Plugin plugin, boolean enabled) { final String keyName = plugin.getClass().getSimpleName().toLowerCase(); - configManager.setConfiguration(runeliteGroupName, keyName, String.valueOf(enabled)); + configManager.setConfiguration(RuneLiteConfig.GROUP_NAME, keyName, String.valueOf(enabled)); } public boolean isPluginEnabled(Plugin plugin) { final String keyName = plugin.getClass().getSimpleName().toLowerCase(); - final String value = configManager.getConfiguration(runeliteGroupName, keyName); + final String value = configManager.getConfiguration(RuneLiteConfig.GROUP_NAME, keyName); if (value != null) { - return Boolean.parseBoolean(value); + return Boolean.valueOf(value); } final PluginDescriptor pluginDescriptor = plugin.getClass().getAnnotation(PluginDescriptor.class); return pluginDescriptor == null || pluginDescriptor.enabledByDefault(); } - @SuppressWarnings("unchecked") private Plugin instantiate(List scannedPlugins, Class clazz) throws PluginInstantiationException { PluginDependency[] pluginDependencies = clazz.getAnnotationsByType(PluginDependency.class); @@ -570,8 +472,6 @@ public class PluginManager deps.add(dependency.get()); } - log.info("Loading plugin {}", clazz.getSimpleName()); - Plugin plugin; try { @@ -632,12 +532,12 @@ public class PluginManager return plugin; } - void add(Plugin plugin) + public void add(Plugin plugin) { plugins.add(plugin); } - void remove(Plugin plugin) + public void remove(Plugin plugin) { plugins.remove(plugin); } @@ -647,10 +547,45 @@ public class PluginManager return plugins; } - public void schedule(Object plugin) + private void schedule(Plugin plugin) { - // note to devs: this method will almost certainly merge conflict in the future, just apply the changes in the scheduler instead - scheduler.registerObject(plugin); + for (Method method : plugin.getClass().getMethods()) + { + Schedule schedule = method.getAnnotation(Schedule.class); + + if (schedule == null) + { + continue; + } + + Runnable runnable = null; + try + { + final Class clazz = method.getDeclaringClass(); + final MethodHandles.Lookup caller = ReflectUtil.privateLookupIn(clazz); + final MethodType subscription = MethodType.methodType(method.getReturnType(), method.getParameterTypes()); + final MethodHandle target = caller.findVirtual(clazz, method.getName(), subscription); + final CallSite site = LambdaMetafactory.metafactory( + caller, + "run", + MethodType.methodType(Runnable.class, clazz), + subscription, + target, + subscription); + + final MethodHandle factory = site.getTarget(); + runnable = (Runnable) factory.bindTo(plugin).invokeExact(); + } + catch (Throwable e) + { + log.warn("Unable to create lambda for method {}", method, e); + } + + ScheduledMethod scheduledMethod = new ScheduledMethod(schedule, method, plugin, runnable); + log.debug("Scheduled task {}", scheduledMethod); + + scheduler.addScheduledMethod(scheduledMethod); + } } private void unschedule(Plugin plugin) @@ -670,146 +605,40 @@ public class PluginManager } /** - * Topologically sort a graph into separate groups. - * Each group represents the dependency level of the plugins. - * Plugins in group (index) 0 has no dependents. - * Plugins in group 1 has dependents in group 0. - * Plugins in group 2 has dependents in group 1, etc. - * This allows for loading dependent groups serially, starting from the last group, - * while loading plugins within each group in parallel. + * Topologically sort a graph. Uses Kahn's algorithm. * * @param graph * @param * @return */ - static List> topologicalGroupSort(Graph graph) + private List topologicalSort(Graph graph) { - final Set root = graph.nodes().stream() - .filter(node -> graph.inDegree(node) == 0) + MutableGraph graphCopy = Graphs.copyOf(graph); + List l = new ArrayList<>(); + Set s = graphCopy.nodes().stream() + .filter(node -> graphCopy.inDegree(node) == 0) .collect(Collectors.toSet()); - final Map dependencyCount = new HashMap<>(); + while (!s.isEmpty()) + { + Iterator it = s.iterator(); + T n = it.next(); + it.remove(); - root.forEach(n -> dependencyCount.put(n, 0)); - root.forEach(n -> graph.successors(n) - .forEach(m -> incrementChildren(graph, dependencyCount, m, dependencyCount.get(n) + 1))); + l.add(n); - // create list dependency grouping - final List> dependencyGroups = new ArrayList<>(); - final int[] curGroup = {-1}; - - dependencyCount.entrySet().stream() - .sorted(Map.Entry.comparingByValue()) - .forEach(entry -> + for (T m : graphCopy.successors(n)) { - if (entry.getValue() != curGroup[0]) + graphCopy.removeEdge(n, m); + if (graphCopy.inDegree(m) == 0) { - curGroup[0] = entry.getValue(); - dependencyGroups.add(new ArrayList<>()); + s.add(m); } - dependencyGroups.get(dependencyGroups.size() - 1).add(entry.getKey()); - }); - - return dependencyGroups; - } - - private static void incrementChildren(Graph graph, Map dependencyCount, T n, int val) - { - if (!dependencyCount.containsKey(n) || dependencyCount.get(n) < val) - { - dependencyCount.put(n, val); - graph.successors(n).forEach(m -> - incrementChildren(graph, dependencyCount, m, val + 1)); - } - } - - public void receive(Message message) - { - if (message.getObject() instanceof ConfigChanged) - { - return; - } - - String[] messageObject = ((String) message.getObject()).split(";"); - - if (messageObject.length < 3) - { - return; - } - - String command = messageObject[0]; - String pluginName = messageObject[1]; - String path = messageObject[2]; - Plugin plugin = null; - - if (!path.equals(settingsFileInput.getAbsolutePath())) - { - return; - } - - for (Plugin pl : getPlugins()) - { - if (pl.getClass().getSimpleName().equals(pluginName)) - { - plugin = pl; - - break; } } - - if (plugin == null) + if (!graphCopy.edges().isEmpty()) { - return; - } - - Plugin finalPlugin = plugin; - - switch (command) - { - case "STARTPLUGIN": - try - { - SwingUtil.syncExec(() -> - { - try - { - startPlugin(finalPlugin); - } - catch (PluginInstantiationException e) - { - log.warn("unable to start plugin", e); - throw new RuntimeException(e); - } - }); - } - catch (InvocationTargetException | InterruptedException e) - { - log.error("eh?"); - } - - break; - - case "STOPPLUGIN": - try - { - SwingUtil.syncExec(() -> - { - try - { - stopPlugin(finalPlugin); - } - catch (PluginInstantiationException e) - { - log.warn("unable to stop plugin", e); - throw new RuntimeException(e); - } - }); - } - catch (InvocationTargetException | InterruptedException e) - { - log.error("eh?"); - } - - break; + throw new RuntimeException("Graph has at least one cycle"); } + return l; } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/PluginType.java b/runelite-client/src/main/java/net/runelite/client/plugins/PluginType.java deleted file mode 100644 index d37d5404dd..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/PluginType.java +++ /dev/null @@ -1,28 +0,0 @@ -package net.runelite.client.plugins; - -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; - -@Getter(AccessLevel.PUBLIC) -@AllArgsConstructor -public enum PluginType -{ - PVM("PvM"), - PVP("PvP"), - SKILLING("Skilling"), - UTILITY("Utilities"), - MISCELLANEOUS("Miscellaneous"), - SYSTEM("System"), - MINIGAME("Minigame"), - GAMEMODE("Gamemode"), - UNCATEGORIZED("Uncategorized"); - - private final String name; - - @Override - public String toString() - { - return getName(); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/account/AccountPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/account/AccountPlugin.java new file mode 100644 index 0000000000..901987356c --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/account/AccountPlugin.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2017, Adam + * 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.plugins.account; + +import java.awt.image.BufferedImage; +import java.util.concurrent.ScheduledExecutorService; +import javax.inject.Inject; +import javax.swing.JOptionPane; +import lombok.extern.slf4j.Slf4j; +import net.runelite.client.account.AccountSession; +import net.runelite.client.account.SessionManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.SessionClose; +import net.runelite.client.events.SessionOpen; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.ClientToolbar; +import net.runelite.client.ui.NavigationButton; +import net.runelite.client.util.ImageUtil; + +@PluginDescriptor( + name = "Account", + description = "Sync RuneLite config settings with your Google account", + tags = {"external", "google", "integration"}, + loadWhenOutdated = true +) +@Slf4j +public class AccountPlugin extends Plugin +{ + @Inject + private SessionManager sessionManager; + + @Inject + private ClientToolbar clientToolbar; + + @Inject + private ScheduledExecutorService executor; + + private NavigationButton loginButton; + private NavigationButton logoutButton; + + private static final BufferedImage LOGIN_IMAGE, LOGOUT_IMAGE; + + static + { + LOGIN_IMAGE = ImageUtil.getResourceStreamFromClass(AccountPlugin.class, "login_icon.png"); + LOGOUT_IMAGE = ImageUtil.getResourceStreamFromClass(AccountPlugin.class, "logout_icon.png"); + } + + @Override + protected void startUp() throws Exception + { + loginButton = NavigationButton.builder() + .tab(false) + .icon(LOGIN_IMAGE) + .tooltip("Log in to RuneLite") + .onClick(this::loginClick) + .build(); + + logoutButton = NavigationButton.builder() + .tab(false) + .icon(LOGOUT_IMAGE) + .tooltip("Log out of RuneLite") + .onClick(this::logoutClick) + .build(); + + addAndRemoveButtons(); + } + + private void addAndRemoveButtons() + { + clientToolbar.removeNavigation(loginButton); + clientToolbar.removeNavigation(logoutButton); + clientToolbar.addNavigation(sessionManager.getAccountSession() == null + ? loginButton + : logoutButton); + } + + @Override + protected void shutDown() throws Exception + { + clientToolbar.removeNavigation(loginButton); + clientToolbar.removeNavigation(logoutButton); + } + + private void loginClick() + { + executor.execute(sessionManager::login); + } + + private void logoutClick() + { + if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(null, + "Are you sure you want to log out from RuneLite?", "Logout Confirmation", + JOptionPane.YES_NO_OPTION)) + { + executor.execute(sessionManager::logout); + } + } + + @Subscribe + public void onSessionClose(SessionClose e) + { + addAndRemoveButtons(); + } + + @Subscribe + public void onSessionOpen(SessionOpen sessionOpen) + { + AccountSession session = sessionManager.getAccountSession(); + + if (session.getUsername() == null) + { + return; // No username yet + } + + log.debug("Session opened as {}", session.getUsername()); + + addAndRemoveButtons(); + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cannonevents/CannonEventsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/cannonevents/CannonEventsPlugin.java deleted file mode 100644 index c729db2b6e..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cannonevents/CannonEventsPlugin.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * - * Copyright (c) 2019, Zeruth - * 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.plugins.cannonevents; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import javax.inject.Inject; -import javax.inject.Singleton; -import lombok.extern.slf4j.Slf4j; -import net.runelite.api.AnimationID; -import net.runelite.api.ChatMessageType; -import net.runelite.api.Client; -import net.runelite.api.GameObject; -import static net.runelite.api.ObjectID.CANNON_BASE; -import net.runelite.api.Player; -import net.runelite.api.Projectile; -import static net.runelite.api.ProjectileID.CANNONBALL; -import static net.runelite.api.ProjectileID.GRANITE_CANNONBALL; -import net.runelite.api.coords.WorldPoint; -import net.runelite.api.events.CannonChanged; -import net.runelite.api.events.CannonPlaced; -import net.runelite.api.events.ChatMessage; -import net.runelite.api.events.GameObjectSpawned; -import net.runelite.api.events.GameTick; -import net.runelite.api.events.ProjectileSpawned; -import net.runelite.client.eventbus.EventBus; -import net.runelite.client.eventbus.Subscribe; -import net.runelite.client.plugins.Plugin; -import net.runelite.client.plugins.PluginDescriptor; - -@PluginDescriptor( - loadWhenOutdated = true, // prevent users from disabling - hidden = true, // prevent users from disabling - name = "Cannon events" -) -@Singleton -@Slf4j -public class CannonEventsPlugin extends Plugin -{ - @Inject - private Client client; - - @Inject - private EventBus eventBus; - - private static final Pattern NUMBER_PATTERN = Pattern.compile("([0-9]+)"); - private static final int MAX_CBALLS = 30; - private int cballsLeft; - private WorldPoint cannonPosition; - private GameObject cannon; - private boolean cannonPlaced; - private boolean skipProjectileCheckThisTick; - - @Subscribe - private void onChatMessage(ChatMessage event) - { - if (event.getType() != ChatMessageType.SPAM && event.getType() != ChatMessageType.GAMEMESSAGE) - { - return; - } - - if (event.getMessage().equals("You add the furnace.")) - { - cballsLeft = 0; - eventBus.post(CannonPlaced.class, new CannonPlaced(true, cannonPosition, cannon)); - eventBus.post(CannonChanged.class, new CannonChanged(null, cballsLeft)); - cannonPlaced = true; - } - - if (event.getMessage().contains("You pick up the cannon") - || event.getMessage().contains("Your cannon has decayed. Speak to Nulodion to get a new one!")) - { - cballsLeft = 0; - eventBus.post(CannonPlaced.class, new CannonPlaced(false, null, null)); - eventBus.post(CannonChanged.class, new CannonChanged(null, cballsLeft)); - cannonPlaced = false; - } - - if (event.getMessage().startsWith("You load the cannon with")) - { - Matcher m = NUMBER_PATTERN.matcher(event.getMessage()); - if (m.find()) - { - // The cannon will usually refill to MAX_CBALLS, but if the - // player didn't have enough cannonballs in their inventory, - // it could fill up less than that. Filling the cannon to - // cballsLeft + amt is not always accurate though because our - // counter doesn't decrease if the player has been too far away - // from the cannon due to the projectiels not being in memory, - // so our counter can be higher than it is supposed to be. - int amt = Integer.parseInt(m.group()); - if (cballsLeft + amt >= MAX_CBALLS) - { - skipProjectileCheckThisTick = true; - cballsLeft = MAX_CBALLS; - } - else - { - cballsLeft += amt; - } - } - else if (event.getMessage().equals("You load the cannon with one cannonball.")) - { - if (cballsLeft + 1 >= MAX_CBALLS) - { - skipProjectileCheckThisTick = true; - cballsLeft = MAX_CBALLS; - } - else - { - cballsLeft++; - } - } - - eventBus.post(CannonChanged.class, new CannonChanged(null, cballsLeft)); - } - - if (event.getMessage().contains("Your cannon is out of ammo!")) - { - skipProjectileCheckThisTick = true; - - // If the player was out of range of the cannon, some cannonballs - // may have been used without the client knowing, so having this - // extra check is a good idea. - cballsLeft = 0; - - eventBus.post(CannonChanged.class, new CannonChanged(null, cballsLeft)); - } - - if (event.getMessage().startsWith("You unload your cannon and receive Cannonball") - || event.getMessage().startsWith("You unload your cannon and receive Granite cannonball")) - { - skipProjectileCheckThisTick = true; - - cballsLeft = 0; - - eventBus.post(CannonChanged.class, new CannonChanged(null, cballsLeft)); - } - } - - @Subscribe - private void onGameTick(GameTick event) - { - skipProjectileCheckThisTick = false; - } - - @Subscribe - private void onGameObjectSpawned(GameObjectSpawned event) - { - final GameObject gameObject = event.getGameObject(); - - final Player localPlayer = client.getLocalPlayer(); - if (gameObject.getId() == CANNON_BASE && !cannonPlaced && - localPlayer != null && localPlayer.getWorldLocation().distanceTo(gameObject.getWorldLocation()) <= 2 && - localPlayer.getAnimation() == AnimationID.BURYING_BONES) - { - cannonPosition = gameObject.getWorldLocation(); - cannon = gameObject; - } - } - - @Subscribe - private void onProjectileSpawned(ProjectileSpawned event) - { - if (!cannonPlaced) - { - return; - } - - final Projectile projectile = event.getProjectile(); - - if ((projectile.getId() == CANNONBALL || projectile.getId() == GRANITE_CANNONBALL) && cannonPosition != null) - { - final WorldPoint projectileLoc = WorldPoint.fromLocal(client, projectile.getX1(), projectile.getY1(), client.getPlane()); - - if (projectileLoc.equals(cannonPosition) && !skipProjectileCheckThisTick) - { - cballsLeft--; - eventBus.post(CannonChanged.class, new CannonChanged(projectile.getId(), cballsLeft)); - } - } - } -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chatboxperformance/ChatboxPerformancePlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/chatboxperformance/ChatboxPerformancePlugin.java deleted file mode 100644 index 76520a191d..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/chatboxperformance/ChatboxPerformancePlugin.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (c) 2018, Woox - * 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.plugins.chatboxperformance; - -import javax.inject.Inject; -import net.runelite.api.Client; -import net.runelite.api.GameState; -import net.runelite.api.ScriptID; -import net.runelite.api.events.ScriptCallbackEvent; -import net.runelite.api.widgets.Widget; -import net.runelite.api.widgets.WidgetInfo; -import net.runelite.api.widgets.WidgetPositionMode; -import net.runelite.api.widgets.WidgetSizeMode; -import net.runelite.api.widgets.WidgetType; -import net.runelite.client.callback.ClientThread; -import net.runelite.client.eventbus.Subscribe; -import net.runelite.client.plugins.Plugin; -import net.runelite.client.plugins.PluginDescriptor; - -@PluginDescriptor( - name = "Chatbox performance", - hidden = true -) -public class ChatboxPerformancePlugin extends Plugin -{ - @Inject - private Client client; - - @Inject - private ClientThread clientThread; - - @Override - public void startUp() - { - if (client.getGameState() == GameState.LOGGED_IN) - { - clientThread.invokeLater(() -> client.runScript(ScriptID.MESSAGE_LAYER_CLOSE, 0, 0)); - } - } - - @Override - public void shutDown() - { - if (client.getGameState() == GameState.LOGGED_IN) - { - clientThread.invokeLater(() -> client.runScript(ScriptID.MESSAGE_LAYER_CLOSE, 0, 0)); - } - } - - @Subscribe - private void onScriptCallbackEvent(ScriptCallbackEvent ev) - { - if (!"chatboxBackgroundBuilt".equals(ev.getEventName())) - { - return; - } - - fixDarkBackground(); - fixWhiteLines(true); - fixWhiteLines(false); - } - - private void fixDarkBackground() - { - int currOpacity = 256; - int prevY = 0; - Widget[] children = client.getWidget(WidgetInfo.CHATBOX_TRANSPARENT_BACKGROUND).getDynamicChildren(); - Widget prev = null; - for (Widget w : children) - { - if (w.getType() != WidgetType.RECTANGLE) - { - continue; - } - - if (prev != null) - { - int relY = w.getRelativeY(); - prev.setHeightMode(WidgetSizeMode.ABSOLUTE); - prev.setYPositionMode(WidgetPositionMode.ABSOLUTE_TOP); - prev.setRelativeY(prevY); - prev.setOriginalY(prev.getRelativeY()); - prev.setHeight(relY - prevY); - prev.setOriginalHeight(prev.getHeight()); - prev.setOpacity(currOpacity); - } - - prevY = w.getRelativeY(); - currOpacity -= 3; // Rough number, can't get exactly the same as Jagex because of rounding - prev = w; - } - if (prev != null) - { - prev.setOpacity(currOpacity); - } - } - - private void fixWhiteLines(boolean upperLine) - { - int currOpacity = 256; - int prevWidth = 0; - Widget[] children = client.getWidget(WidgetInfo.CHATBOX_TRANSPARENT_LINES).getDynamicChildren(); - Widget prev = null; - for (Widget w : children) - { - if (w.getType() != WidgetType.RECTANGLE) - { - continue; - } - - if ((w.getRelativeY() == 0 && !upperLine) || - (w.getRelativeY() != 0 && upperLine)) - { - continue; - } - - if (prev != null) - { - int width = w.getWidth(); - prev.setWidthMode(WidgetSizeMode.ABSOLUTE); - prev.setRelativeX(width); - prev.setOriginalX(width); - prev.setWidth(prevWidth - width); - prev.setOriginalWidth(prev.getWidth()); - prev.setOpacity(currOpacity); - } - - prevWidth = w.getWidth(); - - currOpacity -= upperLine ? 3 : 4; // Rough numbers, can't get exactly the same as Jagex because of rounding - prev = w; - } - if (prev != null) - { - prev.setOpacity(currOpacity); - } - } -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java index 592b0a07eb..f17d41a2cb 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java @@ -1,6 +1,5 @@ /* * Copyright (c) 2017, Adam - * Copyright (c) 2020, ThatGamerBlue * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,32 +24,22 @@ */ package net.runelite.client.plugins.config; -import com.google.common.base.Splitter; import com.google.common.base.Strings; -import java.awt.BasicStroke; +import com.google.common.collect.ComparisonChain; +import com.google.common.primitives.Ints; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; -import java.awt.Font; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.GridLayout; -import java.awt.Insets; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import java.awt.event.ItemEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.EnumSet; import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.function.Consumer; +import java.util.TreeMap; import javax.inject.Inject; import javax.swing.BorderFactory; import javax.swing.BoxLayout; @@ -60,76 +49,67 @@ import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JFormattedTextField; import javax.swing.JLabel; -import javax.swing.JList; +import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPasswordField; import javax.swing.JScrollPane; -import javax.swing.JSeparator; -import javax.swing.JSlider; import javax.swing.JSpinner; import javax.swing.JTextArea; -import javax.swing.JTextField; -import javax.swing.JToggleButton; import javax.swing.ScrollPaneConstants; import javax.swing.SpinnerModel; import javax.swing.SpinnerNumberModel; -import javax.swing.SwingConstants; import javax.swing.SwingUtilities; -import javax.swing.border.Border; import javax.swing.border.CompoundBorder; import javax.swing.border.EmptyBorder; +import javax.swing.border.MatteBorder; import javax.swing.event.ChangeListener; -import javax.swing.plaf.basic.BasicSpinnerUI; import javax.swing.text.JTextComponent; import lombok.extern.slf4j.Slf4j; -import net.runelite.api.events.ConfigButtonClicked; -import net.runelite.api.util.Text; -import net.runelite.client.RuneLite; -import net.runelite.client.config.Button; import net.runelite.client.config.ConfigDescriptor; +import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; import net.runelite.client.config.ConfigItemDescriptor; import net.runelite.client.config.ConfigManager; +import net.runelite.client.config.ConfigObject; import net.runelite.client.config.ConfigSection; -import net.runelite.client.config.ConfigTitleSection; +import net.runelite.client.config.ConfigSectionDescriptor; import net.runelite.client.config.Keybind; import net.runelite.client.config.ModifierlessKeybind; import net.runelite.client.config.Range; import net.runelite.client.config.Units; -import net.runelite.client.eventbus.EventBus; -import net.runelite.client.events.ConfigChanged; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.ExternalPluginsChanged; import net.runelite.client.events.PluginChanged; -import net.runelite.client.plugins.ExternalPluginManager; +import net.runelite.client.externalplugins.ExternalPluginManager; +import net.runelite.client.externalplugins.ExternalPluginManifest; +import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginManager; import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.DynamicGridLayout; import net.runelite.client.ui.FontManager; import net.runelite.client.ui.PluginPanel; import net.runelite.client.ui.components.ComboBoxListRenderer; -import net.runelite.client.ui.components.MinimumSizedPanel; import net.runelite.client.ui.components.colorpicker.ColorPickerManager; import net.runelite.client.ui.components.colorpicker.RuneliteColorPicker; import net.runelite.client.util.ColorUtil; import net.runelite.client.util.ImageUtil; -import net.runelite.client.util.LinkBrowser; -import net.runelite.client.util.MiscUtils; import net.runelite.client.util.SwingUtil; +import net.runelite.client.util.Text; @Slf4j class ConfigPanel extends PluginPanel { private static final int SPINNER_FIELD_WIDTH = 6; - private static final int OFFSET = 6; - - private static final ImageIcon BACK_ICON; - private static final ImageIcon BACK_ICON_HOVER; private static final ImageIcon SECTION_EXPAND_ICON; private static final ImageIcon SECTION_EXPAND_ICON_HOVER; private static final ImageIcon SECTION_RETRACT_ICON; private static final ImageIcon SECTION_RETRACT_ICON_HOVER; + static final ImageIcon BACK_ICON; + static final ImageIcon BACK_ICON_HOVER; + + private static final Map sectionExpandStates = new HashMap<>(); - private final JScrollPane scrollPane; private final FixedWidthPanel mainPanel; private final JLabel title; private final PluginToggleButton pluginToggle; @@ -149,12 +129,7 @@ class ConfigPanel extends PluginPanel @Inject private ColorPickerManager colorPickerManager; - @Inject - private EventBus eventBus; - private PluginConfigurationDescriptor pluginConfig = null; - private final Map sectionWidgets = new HashMap<>(); - private final Map titleSectionWidgets = new HashMap<>(); static { @@ -162,15 +137,13 @@ class ConfigPanel extends PluginPanel BACK_ICON = new ImageIcon(backIcon); BACK_ICON_HOVER = new ImageIcon(ImageUtil.alphaOffset(backIcon, -100)); - final BufferedImage blueBackIcon = ImageUtil.fillImage(backIcon, ColorScheme.BRAND_BLUE); - - final BufferedImage sectionRetractIcon = ImageUtil.rotateImage(blueBackIcon, Math.PI * 1.5); - SECTION_RETRACT_ICON = new ImageIcon(sectionRetractIcon); - SECTION_RETRACT_ICON_HOVER = new ImageIcon(ImageUtil.alphaOffset(sectionRetractIcon, -100)); - - final BufferedImage sectionExpandIcon = ImageUtil.rotateImage(blueBackIcon, Math.PI); - SECTION_EXPAND_ICON = new ImageIcon(sectionExpandIcon); - SECTION_EXPAND_ICON_HOVER = new ImageIcon(ImageUtil.alphaOffset(sectionExpandIcon, -100)); + BufferedImage sectionRetractIcon = ImageUtil.getResourceStreamFromClass(ConfigPanel.class, "/util/arrow_right.png"); + sectionRetractIcon = ImageUtil.luminanceOffset(sectionRetractIcon, -121); + SECTION_EXPAND_ICON = new ImageIcon(sectionRetractIcon); + SECTION_EXPAND_ICON_HOVER = new ImageIcon(ImageUtil.alphaOffset(sectionRetractIcon, -100)); + final BufferedImage sectionExpandIcon = ImageUtil.rotateImage(sectionRetractIcon, Math.PI / 2); + SECTION_RETRACT_ICON = new ImageIcon(sectionExpandIcon); + SECTION_RETRACT_ICON_HOVER = new ImageIcon(ImageUtil.alphaOffset(sectionExpandIcon, -100)); } public ConfigPanel() @@ -194,7 +167,7 @@ class ConfigPanel extends PluginPanel northPanel.setLayout(new BorderLayout()); northPanel.add(mainPanel, BorderLayout.NORTH); - scrollPane = new JScrollPane(northPanel); + JScrollPane scrollPane = new JScrollPane(northPanel); scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); add(scrollPane, BorderLayout.CENTER); @@ -225,6 +198,16 @@ class ConfigPanel extends PluginPanel title.setForeground(Color.WHITE); title.setToolTipText("" + name + ":
" + pluginConfig.getDescription() + ""); + ExternalPluginManifest mf = pluginConfig.getExternalPluginManifest(); + JMenuItem uninstallItem = null; + if (mf != null) + { + uninstallItem = new JMenuItem("Uninstall"); + uninstallItem.addActionListener(ev -> externalPluginManager.remove(mf.getInternalName())); + } + + PluginListItem.addLabelPopupMenu(title, pluginConfig.createSupportMenuItem(), uninstallItem); + if (pluginConfig.getPlugin() != null) { pluginToggle.setSelected(pluginManager.isPluginEnabled(pluginConfig.getPlugin())); @@ -245,323 +228,99 @@ class ConfigPanel extends PluginPanel pluginToggle.setVisible(false); } - rebuild(false); - eventBus.subscribe(PluginChanged.class, this, this::onPluginChanged); - eventBus.subscribe(ConfigChanged.class, this, this::onConfigChanged); + rebuild(); } - private void getSections(ConfigDescriptor cd) + private void toggleSection(ConfigSectionDescriptor csd, JButton button, JPanel contents) { - for (ConfigSection cs : cd.getSections()) + boolean newState = !contents.isVisible(); + contents.setVisible(newState); + button.setIcon(newState ? SECTION_RETRACT_ICON : SECTION_EXPAND_ICON); + button.setRolloverIcon(newState ? SECTION_RETRACT_ICON_HOVER : SECTION_EXPAND_ICON_HOVER); + button.setToolTipText(newState ? "Retract" : "Expand"); + sectionExpandStates.put(csd, newState); + SwingUtilities.invokeLater(contents::revalidate); + } + + private void rebuild() + { + mainPanel.removeAll(); + + ConfigDescriptor cd = pluginConfig.getConfigDescriptor(); + + final Map sectionWidgets = new HashMap<>(); + final Map topLevelPanels = new TreeMap<>((a, b) -> + ComparisonChain.start() + .compare(a.position(), b.position()) + .compare(a.name(), b.name()) + .result()); + + for (ConfigSectionDescriptor csd : cd.getSections()) { - final MinimumSizedPanel section = new MinimumSizedPanel(); + ConfigSection cs = csd.getSection(); + final boolean isOpen = sectionExpandStates.getOrDefault(csd, !cs.closedByDefault()); + + final JPanel section = new JPanel(); section.setLayout(new BoxLayout(section, BoxLayout.Y_AXIS)); section.setMinimumSize(new Dimension(PANEL_WIDTH, 0)); - JPanel item = new JPanel(); - item.setLayout(new BorderLayout()); - item.setMinimumSize(new Dimension(PANEL_WIDTH, 0)); + final JPanel sectionHeader = new JPanel(); + sectionHeader.setLayout(new BorderLayout()); + sectionHeader.setMinimumSize(new Dimension(PANEL_WIDTH, 0)); + // For whatever reason, the header extends out by a single pixel when closed. Adding a single pixel of + // border on the right only affects the width when closed, fixing the issue. + sectionHeader.setBorder(new CompoundBorder( + new MatteBorder(0, 0, 1, 0, ColorScheme.MEDIUM_GRAY_COLOR), + new EmptyBorder(0, 0, 3, 1))); + section.add(sectionHeader, BorderLayout.NORTH); + + final JButton sectionToggle = new JButton(isOpen ? SECTION_RETRACT_ICON : SECTION_EXPAND_ICON); + sectionToggle.setRolloverIcon(isOpen ? SECTION_RETRACT_ICON_HOVER : SECTION_EXPAND_ICON_HOVER); + sectionToggle.setPreferredSize(new Dimension(18, 0)); + sectionToggle.setBorder(new EmptyBorder(0, 0, 0, 5)); + sectionToggle.setToolTipText(isOpen ? "Retract" : "Expand"); + SwingUtil.removeButtonDecorations(sectionToggle); + sectionHeader.add(sectionToggle, BorderLayout.WEST); String name = cs.name(); - String description = cs.description(); - - JLabel headerLabel = new JLabel(cs.name()); - headerLabel.setFont(FontManager.getRunescapeFont()); - headerLabel.setForeground(ColorScheme.BRAND_BLUE); - if (!description.equals("")) - { - headerLabel.setToolTipText("" + name + ":
" + description + ""); - } - headerLabel.setPreferredSize(new Dimension(PANEL_WIDTH, (int) headerLabel.getPreferredSize().getHeight())); - - final boolean state = Boolean.parseBoolean(configManager.getConfiguration(cd.getGroup().value(), cs.keyName())); - - final JToggleButton collapse = new JToggleButton(SECTION_EXPAND_ICON, state); - - SwingUtil.removeButtonDecorations(collapse); - collapse.setRolloverIcon(SECTION_EXPAND_ICON_HOVER); - collapse.setSelectedIcon(SECTION_RETRACT_ICON); - collapse.setRolloverSelectedIcon(SECTION_RETRACT_ICON_HOVER); - collapse.setToolTipText(state ? "Retract" : "Expand"); - collapse.setPreferredSize(new Dimension(20, 20)); - collapse.setFont(collapse.getFont().deriveFont(16.0f)); - collapse.setBorder(null); - collapse.setMargin(new Insets(0, 0, 0, 0)); - - headerLabel.setBorder(new EmptyBorder(0, 10, 0, 0)); - - item.add(collapse, BorderLayout.WEST); - item.add(headerLabel, BorderLayout.CENTER); + final JLabel sectionName = new JLabel(name); + sectionName.setForeground(ColorScheme.BRAND_ORANGE); + sectionName.setFont(FontManager.getRunescapeBoldFont()); + sectionName.setToolTipText("" + name + ":
" + cs.description() + ""); + sectionHeader.add(sectionName, BorderLayout.CENTER); final JPanel sectionContents = new JPanel(); sectionContents.setLayout(new DynamicGridLayout(0, 1, 0, 5)); sectionContents.setMinimumSize(new Dimension(PANEL_WIDTH, 0)); - sectionContents.setBorder(new EmptyBorder(OFFSET, 5, 0, 0)); - section.add(item, BorderLayout.NORTH); + sectionContents.setBorder(new CompoundBorder( + new MatteBorder(0, 0, 1, 0, ColorScheme.MEDIUM_GRAY_COLOR), + new EmptyBorder(BORDER_OFFSET, 0, BORDER_OFFSET, 0))); + sectionContents.setVisible(isOpen); section.add(sectionContents, BorderLayout.SOUTH); - sectionContents.setVisible(state); - // Add listeners to each part of the header so that it's easier to toggle them final MouseAdapter adapter = new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { - toggleSection(cd, cs, collapse, sectionContents); + toggleSection(csd, sectionToggle, sectionContents); } }; - collapse.addActionListener(e -> toggleSection(cd, cs, collapse, sectionContents)); - headerLabel.addMouseListener(adapter); + sectionToggle.addActionListener(actionEvent -> toggleSection(csd, sectionToggle, sectionContents)); + sectionName.addMouseListener(adapter); + sectionHeader.addMouseListener(adapter); - sectionWidgets.put(cs.keyName(), sectionContents); + sectionWidgets.put(csd.getKey(), sectionContents); - // Allow for sub-sections - JPanel parentSection = sectionWidgets.get(cs.section()); - if (parentSection == null) - { - mainPanel.add(section); - } - else - { - parentSection.add(section); - } + topLevelPanels.put(csd, section); } - } - - private void getTitleSections(ConfigDescriptor cd) - { - for (ConfigTitleSection cs : cd.getTitleSections()) - { - final MinimumSizedPanel titleSection = new MinimumSizedPanel(); - titleSection.setLayout(new BoxLayout(titleSection, BoxLayout.Y_AXIS)); - titleSection.setMinimumSize(new Dimension(PANEL_WIDTH, 0)); - - JPanel item = new JPanel(); - item.setLayout(new BorderLayout()); - item.setMinimumSize(new Dimension(PANEL_WIDTH, 0)); - - Border border = item.getBorder(); - Border margin = new EmptyBorder(10, 0, 0, 0); - item.setBorder(new CompoundBorder(border, margin)); - - JLabel configEntryName = new JLabel(cs.name()); - configEntryName.setPreferredSize(new Dimension(PANEL_WIDTH, (int) configEntryName.getPreferredSize().getHeight())); - configEntryName.setForeground(ColorScheme.BRAND_BLUE); - item.add(configEntryName, BorderLayout.NORTH); - - final JPanel sectionContents = new JPanel(); - sectionContents.setLayout(new DynamicGridLayout(0, 1, 0, 5)); - sectionContents.setMinimumSize(new Dimension(0, 0)); - sectionContents.setBorder(new EmptyBorder(OFFSET, 5, 0, 0)); - - titleSection.add(item, BorderLayout.NORTH); - titleSection.add(sectionContents, BorderLayout.SOUTH); - - titleSectionWidgets.put(cs.keyName(), sectionContents); - - // Allow for sub-sections - JPanel parentTitleSection = titleSectionWidgets.get(cs.titleSection()); - JPanel parentSection = sectionWidgets.get(cs.section()); - - if (parentTitleSection != null) - { - parentTitleSection.add(titleSection); - } - else if (parentSection != null) - { - parentSection.add(titleSection); - } - else - { - mainPanel.add(titleSection); - } - } - } - - - private Boolean parse(ConfigItem item, String value) - { - try - { - Method parse = item.clazz().getMethod(item.method(), String.class); - - return (boolean) parse.invoke(null, value); - } - catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) - { - log.error("Parsing failed: {}", ex.getMessage()); - } - - return null; - } - - private void parseLabel(ConfigItem item, JLabel label, String value) - { - Boolean result = parse(item, value); - - if (result == null) - { - label.setForeground(Color.RED); - label.setText("Parsing failed"); - } - else if (result) - { - label.setForeground(Color.GREEN); - label.setText("Valid input"); - } - else - { - label.setForeground(Color.RED); - label.setText("Invalid input"); - } - } - - private void toggleSection(ConfigDescriptor cd, ConfigSection cs, JToggleButton button, JPanel contents) - { - boolean newState = !contents.isVisible(); - button.setSelected(newState); - contents.setVisible(newState); - configManager.setConfiguration(cd.getGroup().value(), cs.keyName(), newState); - button.setToolTipText(newState ? "Retract" : "Expand"); - SwingUtilities.invokeLater(() -> - { - contents.revalidate(); - contents.repaint(); - }); - } - - private void rebuild(boolean refresh) - { - int scrollBarPosition = scrollPane.getVerticalScrollBar().getValue(); - - mainPanel.removeAll(); - - ConfigDescriptor cd = pluginConfig.getConfigDescriptor(); - assert cd != null; - - Map> pluginsInfoMap = externalPluginManager.getPluginsInfoMap(); - - if (pluginConfig.getPlugin() != null && pluginsInfoMap.containsKey(pluginConfig.getPlugin().getClass().getSimpleName())) - { - - JPanel infoPanel = new JPanel(); - infoPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); - infoPanel.setBorder(new EmptyBorder(10, 10, 10, 10)); - infoPanel.setLayout(new GridLayout(0, 1)); - - final Font smallFont = FontManager.getRunescapeSmallFont(); - - Map pluginInfo = pluginsInfoMap.get(pluginConfig.getPlugin().getClass().getSimpleName()); - - JLabel idLabel = new JLabel(htmlLabel("id", pluginInfo.get("id"))); - idLabel.setFont(smallFont); - infoPanel.add(idLabel); - - JLabel versionLabel = new JLabel(htmlLabel("version", pluginInfo.get("version"))); - versionLabel.setFont(smallFont); - infoPanel.add(versionLabel); - - JLabel providerLabel = new JLabel(htmlLabel("provider", pluginInfo.get("provider"))); - providerLabel.setFont(smallFont); - infoPanel.add(providerLabel); - - JButton button = new JButton("Support"); - button.addActionListener(e -> LinkBrowser.browse(pluginInfo.get("support"))); - - JSeparator separator = new JSeparator() - { - @Override - protected void paintComponent(Graphics g) - { - int width = this.getSize().width; - Graphics2D g2 = (Graphics2D) g; - g2.setStroke(new BasicStroke(2)); - g2.setColor(ColorScheme.BRAND_BLUE); - g2.drawLine(0, 0, width, 0); - } - }; - - mainPanel.add(infoPanel); - mainPanel.add(button); - mainPanel.add(separator); - } - - getSections(cd); - getTitleSections(cd); for (ConfigItemDescriptor cid : cd.getItems()) { - if (cid == null) + if (cid.getItem().hidden()) { - continue; // Ignore main 'parent' - } - - Boolean unhide = cid.getItem().hidden(); - Boolean hide = !cid.getItem().hide().isEmpty(); - - if (unhide || hide) - { - boolean show = false; - - List itemHide = Splitter - .onPattern("\\|\\|") - .trimResults() - .omitEmptyStrings() - .splitToList(String.format("%s || %s", cid.getItem().unhide(), cid.getItem().hide())); - - for (ConfigItemDescriptor cid2 : cd.getItems()) - { - if (itemHide.contains(cid2.getItem().keyName())) - { - if (cid2.getType() == boolean.class) - { - show = Boolean.parseBoolean(configManager.getConfiguration(cd.getGroup().value(), cid2.getItem().keyName())); - } - else if (cid2.getType().isEnum()) - { - @SuppressWarnings("unchecked") Class type = (Class) cid2.getType(); - try - { - @SuppressWarnings("unchecked") Enum selectedItem = Enum.valueOf(type, configManager.getConfiguration(cd.getGroup().value(), cid2.getItem().keyName())); - if (!cid.getItem().unhideValue().equals("")) - { - List unhideValue = Splitter - .onPattern("\\|\\|") - .trimResults() - .omitEmptyStrings() - .splitToList(cid.getItem().unhideValue()); - - show = unhideValue.contains(selectedItem.toString()); - } - else if (!cid.getItem().hideValue().equals("")) - { - List hideValue = Splitter - .onPattern("\\|\\|") - .trimResults() - .omitEmptyStrings() - .splitToList(cid.getItem().hideValue()); - - show = !hideValue.contains(selectedItem.toString()); - } - } - catch (IllegalArgumentException ex) - { - log.info("So bad, so sad: {}", ex.toString()); - } - } - } - - if (show) - { - break; - } - } - - if ((unhide && !show) || (hide && show)) - { - continue; - } + continue; } JPanel item = new JPanel(); @@ -571,29 +330,8 @@ class ConfigPanel extends PluginPanel JLabel configEntryName = new JLabel(name); configEntryName.setForeground(Color.WHITE); configEntryName.setToolTipText("" + name + ":
" + cid.getItem().description() + ""); - item.add(configEntryName, cid.getType() != String.class ? BorderLayout.CENTER : BorderLayout.NORTH); - - if (cid.getType() == Button.class) - { - try - { - ConfigItem cidItem = cid.getItem(); - JButton button = new JButton(cidItem.name()); - button.addActionListener((e) -> - { - ConfigButtonClicked event = new ConfigButtonClicked(); - event.setGroup(cd.getGroup().value()); - event.setKey(cid.getItem().keyName()); - eventBus.post(ConfigButtonClicked.class, event); - }); - item.add(button); - } - catch (Exception ex) - { - log.error("Adding action listener failed: {}", ex.getMessage()); - ex.printStackTrace(); - } - } + PluginListItem.addLabelPopupMenu(configEntryName, createResetMenuItem(pluginConfig, cid)); + item.add(configEntryName, BorderLayout.CENTER); if (cid.getType() == boolean.class) { @@ -605,33 +343,10 @@ class ConfigPanel extends PluginPanel item.add(checkbox, BorderLayout.EAST); } - if (cid.getType().isAssignableFrom(Consumer.class)) - { - item.remove(configEntryName); - - JButton button = new JButton(cid.getItem().name()); - button.addActionListener((e) -> - { - log.debug("Running consumer: {}.{}", cd.getGroup().value(), cid.getItem().keyName()); - configManager.getConsumer(cd.getGroup().value(), cid.getItem().keyName()).accept(pluginConfig.getPlugin()); - }); - - item.add(button, BorderLayout.CENTER); - } - if (cid.getType() == int.class) { - int value; - try - { - value = Integer.parseInt(configManager.getConfiguration(cd.getGroup().value(), cid.getItem().keyName())); - } - catch (NumberFormatException ex) - { - return; - } + int value = Integer.parseInt(configManager.getConfiguration(cd.getGroup().value(), cid.getItem().keyName())); - Units units = cid.getUnits(); Range range = cid.getRange(); int min = 0, max = Integer.MAX_VALUE; if (range != null) @@ -641,124 +356,22 @@ class ConfigPanel extends PluginPanel } // Config may previously have been out of range - value = MiscUtils.clamp(value, min, max); + value = Ints.constrainToRange(value, min, max); - if (max < Integer.MAX_VALUE) + SpinnerModel model = new SpinnerNumberModel(value, min, max, 1); + JSpinner spinner = new JSpinner(model); + Component editor = spinner.getEditor(); + JFormattedTextField spinnerTextField = ((JSpinner.DefaultEditor) editor).getTextField(); + spinnerTextField.setColumns(SPINNER_FIELD_WIDTH); + spinner.addChangeListener(ce -> changeConfiguration(spinner, cd, cid)); + + Units units = cid.getUnits(); + if (units != null) { - JLabel sliderValueLabel = new JLabel(); - JSlider slider = new JSlider(min, max, value); - slider.setBackground(ColorScheme.DARK_GRAY_COLOR); - if (units != null) - { - sliderValueLabel.setText(slider.getValue() + units.value()); - } - else - { - sliderValueLabel.setText(String.valueOf(slider.getValue())); - } - slider.setPreferredSize(new Dimension(80, 25)); - slider.addChangeListener((l) -> - { - if (units != null) - { - sliderValueLabel.setText(slider.getValue() + units.value()); - } - else - { - sliderValueLabel.setText(String.valueOf(slider.getValue())); - } - - if (!slider.getValueIsAdjusting()) - { - changeConfiguration(slider, cd, cid); - } - } - ); - - SpinnerModel model = new SpinnerNumberModel(value, min, max, 1); - JSpinner spinner = new JSpinner(model); - Component editor = spinner.getEditor(); - JFormattedTextField spinnerTextField = ((JSpinner.DefaultEditor) editor).getTextField(); - spinnerTextField.setColumns(SPINNER_FIELD_WIDTH); - spinner.setUI(new BasicSpinnerUI() - { - protected Component createNextButton() - { - return null; - } - - protected Component createPreviousButton() - { - return null; - } - }); - - JPanel subPanel = new JPanel(); - subPanel.setPreferredSize(new Dimension(110, 25)); - subPanel.setLayout(new BorderLayout()); - - spinner.addChangeListener((ce) -> - { - changeConfiguration(spinner, cd, cid); - - if (units != null) - { - sliderValueLabel.setText(spinner.getValue() + units.value()); - } - else - { - sliderValueLabel.setText(String.valueOf(spinner.getValue())); - } - slider.setValue((Integer) spinner.getValue()); - - subPanel.add(sliderValueLabel, BorderLayout.WEST); - subPanel.add(slider, BorderLayout.EAST); - subPanel.remove(spinner); - - validate(); - repaint(); - }); - - sliderValueLabel.addMouseListener(new MouseAdapter() - { - public void mouseClicked(MouseEvent e) - { - spinner.setValue(slider.getValue()); - - subPanel.remove(sliderValueLabel); - subPanel.remove(slider); - subPanel.add(spinner, BorderLayout.EAST); - - validate(); - repaint(); - - final JTextField tf = ((JSpinner.DefaultEditor) spinner.getEditor()).getTextField(); - tf.requestFocusInWindow(); - SwingUtilities.invokeLater(tf::selectAll); - } - }); - - subPanel.add(sliderValueLabel, BorderLayout.WEST); - subPanel.add(slider, BorderLayout.EAST); - - item.add(subPanel, BorderLayout.EAST); + spinnerTextField.setFormatterFactory(new UnitFormatterFactory(units)); } - else - { - SpinnerModel model = new SpinnerNumberModel(value, min, max, 1); - JSpinner spinner = new JSpinner(model); - Component editor = spinner.getEditor(); - JFormattedTextField spinnerTextField = ((JSpinner.DefaultEditor) editor).getTextField(); - spinnerTextField.setColumns(SPINNER_FIELD_WIDTH); - spinner.addChangeListener(ce -> changeConfiguration(spinner, cd, cid)); - if (units != null) - { - spinnerTextField.setFormatterFactory(new UnitFormatterFactory(units)); - } - - item.add(spinner, BorderLayout.EAST); - } + item.add(spinner, BorderLayout.EAST); } if (cid.getType() == String.class) @@ -780,51 +393,16 @@ class ConfigPanel extends PluginPanel textField.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); textField.setText(configManager.getConfiguration(cd.getGroup().value(), cid.getItem().keyName())); - DeferredDocumentChangedListener listener = new DeferredDocumentChangedListener(); - listener.addChangeListener(e -> + textField.addFocusListener(new FocusAdapter() { - ConfigItem configItem = cid.getItem(); - if (configItem.parse()) - { - Boolean result = parse(configItem, textField.getText()); - - if (result != null && result) - { - changeConfiguration(textField, cd, cid); - } - } - else + @Override + public void focusLost(FocusEvent e) { changeConfiguration(textField, cd, cid); } }); - textField.getDocument().addDocumentListener(listener); - if (cid.getItem().parse()) - { - JLabel parsingLabel = new JLabel(); - parsingLabel.setHorizontalAlignment(SwingConstants.CENTER); - parsingLabel.setPreferredSize(new Dimension(PANEL_WIDTH, 15)); - - listener = new DeferredDocumentChangedListener(); - listener.addChangeListener(e -> - { - if (cid.getItem().parse()) - { - parseLabel(cid.getItem(), parsingLabel, textField.getText()); - } - }); - textField.getDocument().addDocumentListener(listener); - - item.add(textField, BorderLayout.CENTER); - - parseLabel(cid.getItem(), parsingLabel, textField.getText()); - item.add(parsingLabel, BorderLayout.SOUTH); - } - else - { - item.add(textField, BorderLayout.SOUTH); - } + item.add(textField, BorderLayout.SOUTH); } if (cid.getType() == Color.class) @@ -876,7 +454,6 @@ class ConfigPanel extends PluginPanel JPanel dimensionPanel = new JPanel(); dimensionPanel.setLayout(new BorderLayout()); - Units units = cid.getUnits(); String str = configManager.getConfiguration(cd.getGroup().value(), cid.getItem().keyName()); String[] splitStr = str.split("x"); int width = Integer.parseInt(splitStr[0]); @@ -888,22 +465,12 @@ class ConfigPanel extends PluginPanel JFormattedTextField widthSpinnerTextField = ((JSpinner.DefaultEditor) widthEditor).getTextField(); widthSpinnerTextField.setColumns(4); - if (units != null) - { - widthSpinnerTextField.setFormatterFactory(new UnitFormatterFactory(units)); - } - SpinnerModel heightModel = new SpinnerNumberModel(height, 0, Integer.MAX_VALUE, 1); JSpinner heightSpinner = new JSpinner(heightModel); Component heightEditor = heightSpinner.getEditor(); JFormattedTextField heightSpinnerTextField = ((JSpinner.DefaultEditor) heightEditor).getTextField(); heightSpinnerTextField.setColumns(4); - if (units != null) - { - heightSpinnerTextField.setFormatterFactory(new UnitFormatterFactory(units)); - } - ChangeListener listener = e -> configManager.setConfiguration(cd.getGroup().value(), cid.getItem().keyName(), widthSpinner.getValue() + "x" + heightSpinner.getValue()); @@ -966,54 +533,20 @@ class ConfigPanel extends PluginPanel item.add(button, BorderLayout.EAST); } - else if (cid.getType() == EnumSet.class) - { - Class enumType = cid.getItem().enumClass(); - EnumSet enumSet = configManager.getConfiguration(cd.getGroup().value(), - cid.getItem().keyName(), EnumSet.class); - if (enumSet == null || enumSet.contains(null)) - { - enumSet = EnumSet.noneOf(enumType); - } - - JPanel enumsetLayout = new JPanel(new GridLayout(0, 2)); - List jcheckboxes = new ArrayList<>(); - - for (Object obj : enumType.getEnumConstants()) - { - String option = String.valueOf(obj).toLowerCase().replace("_", " "); - - JCheckBox checkbox = new JCheckBox(option); - checkbox.setBackground(ColorScheme.LIGHT_GRAY_COLOR); - checkbox.setSelected(enumSet.contains(obj)); - jcheckboxes.add(checkbox); - - enumsetLayout.add(checkbox); - } - - jcheckboxes.forEach(checkbox -> checkbox.addActionListener(ae -> changeConfiguration(jcheckboxes, cd, cid))); - - item.add(enumsetLayout, BorderLayout.SOUTH); - } - - JPanel titleSection = titleSectionWidgets.get(cid.getItem().titleSection()); JPanel section = sectionWidgets.get(cid.getItem().section()); - - if (titleSection != null) + if (section == null) { - titleSection.add(item); - } - else if (section != null) - { - section.add(item); + topLevelPanels.put(cid, item); } else { - mainPanel.add(item); + section.add(item); } } + topLevelPanels.values().forEach(mainPanel::add); + JButton resetButton = new JButton("Reset"); resetButton.addActionListener((e) -> { @@ -1025,7 +558,14 @@ class ConfigPanel extends PluginPanel { configManager.setDefaultConfiguration(pluginConfig.getConfig(), true); - rebuild(false); + // Reset non-config panel keys + Plugin plugin = pluginConfig.getPlugin(); + if (plugin != null) + { + plugin.resetConfiguration(); + } + + rebuild(); } }); mainPanel.add(resetButton); @@ -1034,51 +574,24 @@ class ConfigPanel extends PluginPanel backButton.addActionListener(e -> pluginList.getMuxer().popState()); mainPanel.add(backButton); - if (refresh) - { - scrollPane.getVerticalScrollBar().setValue(scrollBarPosition); - } - else - { - scrollPane.getVerticalScrollBar().setValue(0); - } - revalidate(); } - private void changeConfiguration(List components, ConfigDescriptor cd, ConfigItemDescriptor cid) - { - Class enumType = cid.getItem().enumClass(); - EnumSet enumSet = configManager.getConfiguration(cd.getGroup().value(), - cid.getItem().keyName(), EnumSet.class); - if (enumSet == null) - { - //noinspection unchecked - enumSet = EnumSet.noneOf(enumType); - } - enumSet.clear(); - - EnumSet finalEnumSet = enumSet; - - //noinspection unchecked - components.forEach(value -> - { - if (value.isSelected()) - { - finalEnumSet.add(Enum.valueOf(cid.getItem().enumClass(), String.valueOf(value.getText()).toUpperCase().replace(" ", "_"))); - } - }); - - configManager.setConfiguration(cd.getGroup().value(), cid.getItem().keyName(), finalEnumSet); - } - private void changeConfiguration(Component component, ConfigDescriptor cd, ConfigItemDescriptor cid) { final ConfigItem configItem = cid.getItem(); if (!Strings.isNullOrEmpty(configItem.warning())) { - JOptionPane.showMessageDialog(component, configItem.warning(), "Warning", JOptionPane.WARNING_MESSAGE); + final int result = JOptionPane.showOptionDialog(component, configItem.warning(), + "Are you sure?", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, + null, new String[]{"Yes", "No"}, "No"); + + if (result != JOptionPane.YES_OPTION) + { + rebuild(); + return; + } } if (component instanceof JCheckBox) @@ -1091,11 +604,6 @@ class ConfigPanel extends PluginPanel JSpinner spinner = (JSpinner) component; configManager.setConfiguration(cd.getGroup().value(), cid.getItem().keyName(), "" + spinner.getValue()); } - else if (component instanceof JSlider) - { - JSlider slider = (JSlider) component; - configManager.setConfiguration(cd.getGroup().value(), cid.getItem().keyName(), slider.getValue()); - } else if (component instanceof JTextComponent) { JTextComponent textField = (JTextComponent) component; @@ -1116,115 +624,6 @@ class ConfigPanel extends PluginPanel HotkeyButton hotkeyButton = (HotkeyButton) component; configManager.setConfiguration(cd.getGroup().value(), cid.getItem().keyName(), hotkeyButton.getValue()); } - - hideUnhide(component, cd, cid); - } - - private void hideUnhide(Component component, ConfigDescriptor cd, ConfigItemDescriptor cid) - { - if (component instanceof JCheckBox) - { - JCheckBox checkbox = (JCheckBox) component; - - for (ConfigItemDescriptor cid2 : cd.getItems()) - { - if (cid2.getItem().hidden() || !cid2.getItem().hide().isEmpty()) - { - List itemHide = Splitter - .onPattern("\\|\\|") - .trimResults() - .omitEmptyStrings() - .splitToList(String.format("%s || %s", cid2.getItem().unhide(), cid2.getItem().hide())); - - if (itemHide.contains(cid.getItem().keyName())) - { - rebuild(true); - } - } - - if (checkbox.isSelected()) - { - if (cid2.getItem().enabledBy().contains(cid.getItem().keyName())) - { - configManager.setConfiguration(cd.getGroup().value(), cid2.getItem().keyName(), "true"); - rebuild(true); - } - else if (cid2.getItem().disabledBy().contains(cid.getItem().keyName())) - { - configManager.setConfiguration(cd.getGroup().value(), cid2.getItem().keyName(), "false"); - rebuild(true); - } - } - } - } - else if (component instanceof JComboBox) - { - JComboBox jComboBox = (JComboBox) component; - - for (ConfigItemDescriptor cid2 : cd.getItems()) - { - if (cid2.getItem().hidden() || !cid2.getItem().hide().isEmpty()) - { - List itemHide = Splitter - .onPattern("\\|\\|") - .trimResults() - .omitEmptyStrings() - .splitToList(String.format("%s || %s", cid2.getItem().unhide(), cid2.getItem().hide())); - - if (itemHide.contains(cid.getItem().keyName())) - { - rebuild(true); - } - - String changedVal = ((Enum) jComboBox.getSelectedItem()).name(); - - if (cid2.getItem().enabledBy().contains(cid.getItem().keyName()) && cid2.getItem().enabledByValue().equals(changedVal)) - { - configManager.setConfiguration(cd.getGroup().value(), cid2.getItem().keyName(), "true"); - rebuild(true); - } - else if (cid2.getItem().disabledBy().contains(cid.getItem().keyName()) && cid2.getItem().disabledByValue().equals(changedVal)) - { - configManager.setConfiguration(cd.getGroup().value(), cid2.getItem().keyName(), "false"); - rebuild(true); - } - } - } - } - else if (component instanceof JList) - { - JList jList = (JList) component; - - for (ConfigItemDescriptor cid2 : cd.getItems()) - { - if (cid2.getItem().hidden() || !cid2.getItem().hide().isEmpty()) - { - List itemHide = Splitter - .onPattern("\\|\\|") - .trimResults() - .omitEmptyStrings() - .splitToList(String.format("%s || %s", cid2.getItem().unhide(), cid2.getItem().hide())); - - if (itemHide.contains(cid.getItem().keyName())) - { - rebuild(true); - } - - String changedVal = String.valueOf((jList.getSelectedValues())); - - if (cid2.getItem().enabledBy().contains(cid.getItem().keyName()) && cid2.getItem().enabledByValue().equals(changedVal)) - { - configManager.setConfiguration(cd.getGroup().value(), cid2.getItem().keyName(), "true"); - rebuild(true); - } - else if (cid2.getItem().disabledBy().contains(cid.getItem().keyName()) && cid2.getItem().disabledByValue().equals(changedVal)) - { - configManager.setConfiguration(cd.getGroup().value(), cid2.getItem().keyName(), "false"); - rebuild(true); - } - } - } - } } @Override @@ -1233,33 +632,44 @@ class ConfigPanel extends PluginPanel return new Dimension(PANEL_WIDTH + SCROLLBAR_WIDTH, super.getPreferredSize().height); } + @Subscribe public void onPluginChanged(PluginChanged event) { if (event.getPlugin() == this.pluginConfig.getPlugin()) { SwingUtilities.invokeLater(() -> - pluginToggle.setSelected(event.isLoaded())); + { + pluginToggle.setSelected(event.isLoaded()); + }); } } - public void onConfigChanged(ConfigChanged event) + @Subscribe + private void onExternalPluginsChanged(ExternalPluginsChanged ev) { - if (event.getOrigin().equals(RuneLite.uuid)) - { - return; - } - - try - { - SwingUtilities.invokeAndWait(() -> rebuild(true)); - } - catch (InterruptedException | InvocationTargetException e) + if (pluginManager.getPlugins().stream() + .noneMatch(p -> p == this.pluginConfig.getPlugin())) { + pluginList.getMuxer().popState(); } + SwingUtilities.invokeLater(this::rebuild); } - private static String htmlLabel(String key, String value) + private JMenuItem createResetMenuItem(PluginConfigurationDescriptor pluginConfig, ConfigItemDescriptor configItemDescriptor) { - return "" + key + ": " + value + ""; + JMenuItem menuItem = new JMenuItem("Reset"); + menuItem.addActionListener(e -> + { + ConfigDescriptor configDescriptor = pluginConfig.getConfigDescriptor(); + ConfigGroup configGroup = configDescriptor.getGroup(); + ConfigItem configItem = configItemDescriptor.getItem(); + + // To reset one item we'll just unset it and then apply defaults over the whole group + configManager.unsetConfiguration(configGroup.value(), configItem.keyName()); + configManager.setDefaultConfiguration(pluginConfig.getConfig(), false); + + rebuild(); + }); + return menuItem; } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java index 71da3a5b0c..ac93dcf96b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java @@ -24,23 +24,18 @@ */ package net.runelite.client.plugins.config; -import com.github.zafarkhaja.semver.Version; import java.awt.image.BufferedImage; import javax.inject.Inject; import javax.inject.Provider; import javax.swing.SwingUtilities; -import net.runelite.api.MenuOpcode; -import net.runelite.client.RuneLiteProperties; +import net.runelite.api.MenuAction; import net.runelite.client.config.ChatColorConfig; import net.runelite.client.config.ConfigManager; -import net.runelite.client.config.LauncherConfig; -import net.runelite.client.config.OpenOSRSConfig; import net.runelite.client.config.RuneLiteConfig; import net.runelite.client.eventbus.Subscribe; import net.runelite.client.events.OverlayMenuClicked; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; -import net.runelite.client.plugins.PluginType; import net.runelite.client.ui.ClientToolbar; import net.runelite.client.ui.NavigationButton; import net.runelite.client.ui.overlay.Overlay; @@ -66,15 +61,9 @@ public class ConfigPlugin extends Plugin @Inject private RuneLiteConfig runeLiteConfig; - @Inject - private OpenOSRSConfig openOSRSConfig; - @Inject private ChatColorConfig chatColorConfig; - @Inject - private LauncherConfig launcherConfig; - private PluginListPanel pluginListPanel; private NavigationButton navButton; @@ -83,33 +72,15 @@ public class ConfigPlugin extends Plugin protected void startUp() throws Exception { pluginListPanel = pluginListPanelProvider.get(); - pluginListPanel.addFakePlugin( - new PluginConfigurationDescriptor( - "OpenOSRS", "OpenOSRS client settings", PluginType.SYSTEM, - new String[]{"sorting", "external", "logs", "categories", "colors", "opacity", "pin"}, - null, openOSRSConfig, configManager.getConfigDescriptor(openOSRSConfig) - ), - new PluginConfigurationDescriptor( - "RuneLite", "RuneLite client settings", PluginType.SYSTEM, + pluginListPanel.addFakePlugin(new PluginConfigurationDescriptor( + "RuneLite", "RuneLite client settings", new String[]{"client", "notification", "size", "position", "window", "chrome", "focus", "font", "overlay", "tooltip", "infobox"}, null, runeLiteConfig, configManager.getConfigDescriptor(runeLiteConfig) ), new PluginConfigurationDescriptor( - "Chat Color", "Recolor chat text", PluginType.MISCELLANEOUS, new String[]{"colour", "messages"}, + "Chat Color", "Recolor chat text", new String[]{"colour", "messages"}, null, chatColorConfig, configManager.getConfigDescriptor(chatColorConfig) )); - - // Support for this has been added in launcher version 2.2.0 - if (launcherVersion("2.2.0")) - { - pluginListPanel.addFakePlugin( - new PluginConfigurationDescriptor( - "Launcher", "Launcher settings", PluginType.SYSTEM, - new String[]{"hw", "nightly", "stable", "proxy", "bootstrap"}, - null, launcherConfig, configManager.getConfigDescriptor(launcherConfig) - )); - } - pluginListPanel.rebuildPluginList(); final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "config_icon.png"); @@ -134,7 +105,7 @@ public class ConfigPlugin extends Plugin public void onOverlayMenuClicked(OverlayMenuClicked overlayMenuClicked) { OverlayMenuEntry overlayMenuEntry = overlayMenuClicked.getEntry(); - if (overlayMenuEntry.getMenuOpcode() == MenuOpcode.RUNELITE_OVERLAY_CONFIG) + if (overlayMenuEntry.getMenuAction() == MenuAction.RUNELITE_OVERLAY_CONFIG) { Overlay overlay = overlayMenuClicked.getOverlay(); Plugin plugin = overlay.getPlugin(); @@ -154,16 +125,4 @@ public class ConfigPlugin extends Plugin }); } } - - private boolean launcherVersion(String version) - { - String launcherVersion = RuneLiteProperties.getLauncherVersion(); - - if (launcherVersion == null) - { - return false; - } - - return Version.valueOf(launcherVersion).greaterThanOrEqualTo(Version.valueOf(version)); - } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/DeferredDocumentChangedListener.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/DeferredDocumentChangedListener.java deleted file mode 100644 index 7dbd7d73b4..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/DeferredDocumentChangedListener.java +++ /dev/null @@ -1,58 +0,0 @@ -package net.runelite.client.plugins.config; - -import java.util.ArrayList; -import java.util.List; -import javax.swing.Timer; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; - -class DeferredDocumentChangedListener implements DocumentListener -{ - private final Timer timer; - private final List listeners; - - DeferredDocumentChangedListener() - { - listeners = new ArrayList<>(25); - timer = new Timer(350, e -> fireStateChanged()); - timer.setRepeats(false); - } - - void addChangeListener(ChangeListener listener) - { - listeners.add(listener); - } - - private void fireStateChanged() - { - if (!listeners.isEmpty()) - { - ChangeEvent evt = new ChangeEvent(this); - for (ChangeListener listener : listeners) - { - listener.stateChanged(evt); - } - } - } - - @Override - public void insertUpdate(DocumentEvent e) - { - timer.restart(); - } - - @Override - public void removeUpdate(DocumentEvent e) - { - timer.restart(); - } - - @Override - public void changedUpdate(DocumentEvent e) - { - timer.restart(); - } - -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/HotkeyButton.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/HotkeyButton.java index 1575d51cab..55b9160c5b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/HotkeyButton.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/HotkeyButton.java @@ -28,20 +28,17 @@ import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; -import javax.inject.Singleton; import javax.swing.JButton; -import lombok.AccessLevel; import lombok.Getter; import net.runelite.client.config.Keybind; import net.runelite.client.config.ModifierlessKeybind; -@Singleton class HotkeyButton extends JButton { - @Getter(AccessLevel.PACKAGE) + @Getter private Keybind value; - HotkeyButton(Keybind value, boolean modifierless) + public HotkeyButton(Keybind value, boolean modifierless) { setValue(value); addMouseListener(new MouseAdapter() @@ -53,6 +50,7 @@ class HotkeyButton extends JButton setValue(Keybind.NOT_SET); } }); + addKeyListener(new KeyAdapter() { @Override @@ -70,7 +68,7 @@ class HotkeyButton extends JButton }); } - private void setValue(Keybind value) + public void setValue(Keybind value) { if (value == null) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginConfigurationDescriptor.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginConfigurationDescriptor.java index 9098342d81..279991656b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginConfigurationDescriptor.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginConfigurationDescriptor.java @@ -25,18 +25,20 @@ package net.runelite.client.plugins.config; import javax.annotation.Nullable; +import javax.swing.JMenuItem; import lombok.Value; import net.runelite.client.config.Config; import net.runelite.client.config.ConfigDescriptor; +import net.runelite.client.externalplugins.ExternalPluginManager; +import net.runelite.client.externalplugins.ExternalPluginManifest; import net.runelite.client.plugins.Plugin; -import net.runelite.client.plugins.PluginType; +import net.runelite.client.util.LinkBrowser; @Value -public class PluginConfigurationDescriptor +class PluginConfigurationDescriptor { private final String name; private final String description; - private final PluginType pluginType; private final String[] tags; // Can be null if its not an actual plugin (RuneLite / ChatColors) @@ -54,4 +56,41 @@ public class PluginConfigurationDescriptor { return configDescriptor != null && !configDescriptor.getItems().stream().allMatch(item -> item.getItem().hidden()); } -} \ No newline at end of file + + /** + * Creates a menu item for linking to a support page for the plugin + * + * @return A {@link JMenuItem} which opens the plugin's wiki page URL in the browser when clicked + */ + @Nullable + JMenuItem createSupportMenuItem() + { + ExternalPluginManifest mf = getExternalPluginManifest(); + if (mf != null) + { + if (mf.getSupport() == null) + { + return null; + } + + JMenuItem menuItem = new JMenuItem("Support"); + menuItem.addActionListener(e -> LinkBrowser.browse(mf.getSupport().toString())); + return menuItem; + } + + JMenuItem menuItem = new JMenuItem("Wiki"); + menuItem.addActionListener(e -> LinkBrowser.browse("https://github.com/runelite/runelite/wiki/" + name.replace(' ', '-'))); + return menuItem; + } + + @Nullable + ExternalPluginManifest getExternalPluginManifest() + { + if (plugin == null) + { + return null; + } + + return ExternalPluginManager.getExternalPluginManifest(plugin.getClass()); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginHubPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginHubPanel.java new file mode 100644 index 0000000000..aa5dbd5074 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginHubPanel.java @@ -0,0 +1,587 @@ +/* + * 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.plugins.config; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; +import com.google.common.html.HtmlEscapers; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ScheduledExecutorService; +import java.util.function.Function; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.inject.Inject; +import javax.inject.Singleton; +import javax.swing.AbstractAction; +import javax.swing.BorderFactory; +import javax.swing.GroupLayout; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.KeyStroke; +import javax.swing.LayoutStyle; +import javax.swing.ScrollPaneConstants; +import javax.swing.SwingUtilities; +import javax.swing.border.EmptyBorder; +import javax.swing.border.LineBorder; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.client.config.Config; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.ExternalPluginsChanged; +import net.runelite.client.externalplugins.ExternalPluginClient; +import net.runelite.client.externalplugins.ExternalPluginManager; +import net.runelite.client.externalplugins.ExternalPluginManifest; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginManager; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.DynamicGridLayout; +import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.PluginPanel; +import net.runelite.client.ui.components.IconTextField; +import net.runelite.client.util.ImageUtil; +import net.runelite.client.util.LinkBrowser; +import net.runelite.client.util.SwingUtil; +import net.runelite.client.util.VerificationException; + +@Slf4j +@Singleton +class PluginHubPanel extends PluginPanel +{ + private static final ImageIcon MISSING_ICON; + private static final ImageIcon HELP_ICON; + private static final ImageIcon HELP_ICON_HOVER; + private static final ImageIcon CONFIGURE_ICON; + private static final ImageIcon CONFIGURE_ICON_HOVER; + private static final Pattern SPACES = Pattern.compile(" +"); + + static + { + BufferedImage missingIcon = ImageUtil.getResourceStreamFromClass(PluginHubPanel.class, "pluginhub_missingicon.png"); + MISSING_ICON = new ImageIcon(missingIcon); + + BufferedImage helpIcon = ImageUtil.getResourceStreamFromClass(PluginHubPanel.class, "pluginhub_help.png"); + HELP_ICON = new ImageIcon(helpIcon); + HELP_ICON_HOVER = new ImageIcon(ImageUtil.alphaOffset(helpIcon, -100)); + + BufferedImage configureIcon = ImageUtil.getResourceStreamFromClass(PluginHubPanel.class, "pluginhub_configure.png"); + CONFIGURE_ICON = new ImageIcon(configureIcon); + CONFIGURE_ICON_HOVER = new ImageIcon(ImageUtil.alphaOffset(configureIcon, -100)); + } + + private class PluginItem extends JPanel implements SearchablePlugin + { + private static final int HEIGHT = 70; + private static final int ICON_WIDTH = 48; + private static final int BOTTOM_LINE_HEIGHT = 16; + + private final ExternalPluginManifest manifest; + + @Getter + private final List keywords = new ArrayList<>(); + + @Getter + private final boolean installed; + + PluginItem(ExternalPluginManifest newManifest, Collection loadedPlugins, boolean installed) + { + ExternalPluginManifest loaded = null; + if (!loadedPlugins.isEmpty()) + { + loaded = ExternalPluginManager.getExternalPluginManifest(loadedPlugins.iterator().next().getClass()); + } + + manifest = newManifest == null ? loaded : newManifest; + this.installed = installed; + + if (manifest != null) + { + Collections.addAll(keywords, SPACES.split(manifest.getDisplayName().toLowerCase())); + + if (manifest.getDescription() != null) + { + Collections.addAll(keywords, SPACES.split(manifest.getDescription().toLowerCase())); + } + + Collections.addAll(keywords, manifest.getAuthor().toLowerCase()); + + if (manifest.getTags() != null) + { + Collections.addAll(keywords, manifest.getTags()); + } + } + + setBackground(ColorScheme.DARKER_GRAY_COLOR); + setOpaque(true); + + GroupLayout layout = new GroupLayout(this); + setLayout(layout); + + JLabel pluginName = new JLabel(manifest.getDisplayName()); + pluginName.setFont(FontManager.getRunescapeBoldFont()); + pluginName.setToolTipText(manifest.getDisplayName()); + + JLabel author = new JLabel(manifest.getAuthor()); + author.setFont(FontManager.getRunescapeSmallFont()); + author.setToolTipText(manifest.getAuthor()); + + JLabel version = new JLabel(manifest.getVersion()); + version.setFont(FontManager.getRunescapeSmallFont()); + version.setToolTipText(manifest.getVersion()); + + String descriptionText = manifest.getDescription(); + if (!descriptionText.startsWith("")) + { + descriptionText = "" + HtmlEscapers.htmlEscaper().escape(descriptionText) + ""; + } + JLabel description = new JLabel(descriptionText); + description.setVerticalAlignment(JLabel.TOP); + description.setToolTipText(descriptionText); + + JLabel icon = new JLabel(); + icon.setHorizontalAlignment(JLabel.CENTER); + icon.setIcon(MISSING_ICON); + if (manifest.hasIcon()) + { + executor.submit(() -> + { + try + { + BufferedImage img = externalPluginClient.downloadIcon(manifest); + + SwingUtilities.invokeLater(() -> + { + icon.setIcon(new ImageIcon(img)); + }); + } + catch (IOException e) + { + log.info("Cannot download icon for plugin \"{}\"", manifest.getInternalName(), e); + } + }); + } + + JButton help = new JButton(HELP_ICON); + help.setRolloverIcon(HELP_ICON_HOVER); + SwingUtil.removeButtonDecorations(help); + help.setBorder(null); + if (manifest.getSupport() == null) + { + help.setVisible(false); + } + else + { + help.setToolTipText("Open help: " + manifest.getSupport().toString()); + help.addActionListener(ev -> LinkBrowser.browse(manifest.getSupport().toString())); + } + + JButton configure = new JButton(CONFIGURE_ICON); + configure.setRolloverIcon(CONFIGURE_ICON_HOVER); + SwingUtil.removeButtonDecorations(configure); + configure.setToolTipText("Configure"); + help.setBorder(null); + if (loaded != null) + { + String search = null; + if (loadedPlugins.size() > 1) + { + search = loaded.getInternalName(); + } + else + { + Plugin plugin = loadedPlugins.iterator().next(); + Config cfg = pluginManager.getPluginConfigProxy(plugin); + if (cfg == null) + { + search = loaded.getInternalName(); + } + else + { + configure.addActionListener(l -> pluginListPanel.openConfigurationPanel(plugin)); + } + } + + if (search != null) + { + final String javaIsABadLanguage = search; + configure.addActionListener(l -> pluginListPanel.openWithFilter(javaIsABadLanguage)); + } + } + else + { + configure.setVisible(false); + } + + boolean install = !installed; + boolean update = loaded != null && newManifest != null && !newManifest.equals(loaded); + boolean remove = !install && !update; + JButton addrm = new JButton(); + if (install) + { + addrm.setText("Install"); + addrm.setBackground(new Color(0x28BE28)); + addrm.addActionListener(l -> + { + if (manifest.getWarning() != null) + { + int result = JOptionPane.showConfirmDialog( + this, + "

" + manifest.getWarning() + "

Are you sure you want to install this plugin?", + "Installing " + manifest.getDisplayName(), + JOptionPane.YES_NO_OPTION, + JOptionPane.WARNING_MESSAGE); + if (result != JOptionPane.OK_OPTION) + { + return; + } + } + externalPluginManager.install(manifest.getInternalName()); + }); + } + else if (remove) + { + addrm.setText("Remove"); + addrm.setBackground(new Color(0xBE2828)); + addrm.addActionListener(l -> externalPluginManager.remove(manifest.getInternalName())); + } + else + { + assert update; + addrm.setText("Update"); + addrm.setBackground(new Color(0x1F621F)); + addrm.addActionListener(l -> externalPluginManager.update()); + } + addrm.setBorder(new LineBorder(addrm.getBackground().darker())); + addrm.setFocusPainted(false); + + layout.setHorizontalGroup(layout.createSequentialGroup() + .addComponent(icon, ICON_WIDTH, ICON_WIDTH, ICON_WIDTH) + .addGap(5) + .addGroup(layout.createParallelGroup() + .addGroup(layout.createSequentialGroup() + .addComponent(pluginName, 0, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE) + .addComponent(author, 0, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE)) + .addComponent(description, 0, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addComponent(version, 0, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.PREFERRED_SIZE, 100) + .addComponent(help, 0, 24, 24) + .addComponent(configure, 0, 24, 24) + .addComponent(addrm, 0, 50, GroupLayout.PREFERRED_SIZE) + .addGap(5)))); + + int lineHeight = description.getFontMetrics(description.getFont()).getHeight(); + layout.setVerticalGroup(layout.createParallelGroup() + .addComponent(icon, HEIGHT, GroupLayout.DEFAULT_SIZE, HEIGHT + lineHeight) + .addGroup(layout.createSequentialGroup() + .addGap(5) + .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addComponent(pluginName) + .addComponent(author)) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE) + .addComponent(description, lineHeight, GroupLayout.PREFERRED_SIZE, lineHeight * 2) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addComponent(version, BOTTOM_LINE_HEIGHT, BOTTOM_LINE_HEIGHT, BOTTOM_LINE_HEIGHT) + .addComponent(help, BOTTOM_LINE_HEIGHT, BOTTOM_LINE_HEIGHT, BOTTOM_LINE_HEIGHT) + .addComponent(configure, BOTTOM_LINE_HEIGHT, BOTTOM_LINE_HEIGHT, BOTTOM_LINE_HEIGHT) + .addComponent(addrm, BOTTOM_LINE_HEIGHT, BOTTOM_LINE_HEIGHT, BOTTOM_LINE_HEIGHT)) + .addGap(5))); + } + + @Override + public String getSearchableName() + { + return manifest.getDisplayName(); + } + } + + private final PluginListPanel pluginListPanel; + private final ExternalPluginManager externalPluginManager; + private final PluginManager pluginManager; + private final ExternalPluginClient externalPluginClient; + private final ScheduledExecutorService executor; + + private final IconTextField searchBar; + private final JLabel refreshing; + private final JPanel mainPanel; + private List plugins = null; + + @Inject + PluginHubPanel( + PluginListPanel pluginListPanel, + ExternalPluginManager externalPluginManager, + PluginManager pluginManager, + ExternalPluginClient externalPluginClient, + ScheduledExecutorService executor) + { + super(false); + this.pluginListPanel = pluginListPanel; + this.externalPluginManager = externalPluginManager; + this.pluginManager = pluginManager; + this.externalPluginClient = externalPluginClient; + this.executor = executor; + + { + Object refresh = "this could just be a lambda, but no, it has to be abstracted"; + getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0), refresh); + getActionMap().put(refresh, new AbstractAction() + { + @Override + public void actionPerformed(ActionEvent e) + { + reloadPluginList(); + } + }); + } + + GroupLayout layout = new GroupLayout(this); + setLayout(layout); + setBackground(ColorScheme.DARK_GRAY_COLOR); + + searchBar = new IconTextField(); + searchBar.setIcon(IconTextField.Icon.SEARCH); + searchBar.setBackground(ColorScheme.DARKER_GRAY_COLOR); + searchBar.setHoverBackgroundColor(ColorScheme.DARK_GRAY_HOVER_COLOR); + searchBar.getDocument().addDocumentListener(new DocumentListener() + { + @Override + public void insertUpdate(DocumentEvent e) + { + filter(); + } + + @Override + public void removeUpdate(DocumentEvent e) + { + filter(); + } + + @Override + public void changedUpdate(DocumentEvent e) + { + filter(); + } + }); + + JLabel externalPluginWarning = new JLabel("External plugins are verified to not be " + + "malicious or rule-breaking, but are not " + + "maintained by the RuneLite developers. " + + "They may cause bugs or instability."); + externalPluginWarning.setBackground(new Color(0xFFBB33)); + externalPluginWarning.setForeground(Color.BLACK); + externalPluginWarning.setBorder(new EmptyBorder(5, 5, 5, 2)); + externalPluginWarning.setOpaque(true); + + JLabel externalPluginWarning2 = new JLabel("Use at your own risk!"); + externalPluginWarning2.setHorizontalAlignment(JLabel.CENTER); + externalPluginWarning2.setFont(FontManager.getRunescapeBoldFont()); + externalPluginWarning2.setBackground(externalPluginWarning.getBackground()); + externalPluginWarning2.setForeground(externalPluginWarning.getForeground()); + externalPluginWarning2.setBorder(new EmptyBorder(0, 5, 5, 5)); + externalPluginWarning2.setOpaque(true); + + JButton backButton = new JButton(ConfigPanel.BACK_ICON); + backButton.setRolloverIcon(ConfigPanel.BACK_ICON_HOVER); + SwingUtil.removeButtonDecorations(backButton); + backButton.setToolTipText("Back"); + backButton.addActionListener(l -> pluginListPanel.getMuxer().popState()); + + mainPanel = new JPanel(); + mainPanel.setBorder(BorderFactory.createEmptyBorder(0, 7, 7, 7)); + mainPanel.setLayout(new DynamicGridLayout(0, 1, 0, 5)); + mainPanel.setAlignmentX(Component.LEFT_ALIGNMENT); + + refreshing = new JLabel("Loading..."); + refreshing.setHorizontalAlignment(JLabel.CENTER); + + JPanel mainPanelWrapper = new FixedWidthPanel(); + mainPanelWrapper.setLayout(new BorderLayout()); + mainPanelWrapper.add(mainPanel, BorderLayout.NORTH); + mainPanelWrapper.add(refreshing, BorderLayout.CENTER); + + JScrollPane scrollPane = new JScrollPane(); + scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + // Can't use Short.MAX_VALUE like the docs say because of JDK-8079640 + scrollPane.setPreferredSize(new Dimension(0x7000, 0x7000)); + scrollPane.setViewportView(mainPanelWrapper); + + layout.setVerticalGroup(layout.createSequentialGroup() + .addComponent(externalPluginWarning) + .addComponent(externalPluginWarning2) + .addGap(10) + .addGroup(layout.createParallelGroup() + .addComponent(backButton) + .addComponent(searchBar)) + .addGap(10) + .addComponent(scrollPane)); + + layout.setHorizontalGroup(layout.createParallelGroup() + .addComponent(externalPluginWarning, 0, Short.MAX_VALUE, Short.MAX_VALUE) + .addComponent(externalPluginWarning2, 0, Short.MAX_VALUE, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addComponent(backButton) + .addComponent(searchBar) + .addGap(10)) + .addComponent(scrollPane)); + + revalidate(); + + refreshing.setVisible(false); + reloadPluginList(); + } + + private void reloadPluginList() + { + if (refreshing.isVisible()) + { + return; + } + + refreshing.setVisible(true); + mainPanel.removeAll(); + + executor.submit(() -> + { + List manifest; + try + { + manifest = externalPluginClient.downloadManifest(); + } + catch (IOException | VerificationException e) + { + log.error("", e); + SwingUtilities.invokeLater(() -> + { + refreshing.setVisible(false); + mainPanel.add(new JLabel("Downloading the plugin manifest failed")); + + JButton retry = new JButton("Retry"); + retry.addActionListener(l -> reloadPluginList()); + mainPanel.add(retry); + }); + return; + } + + reloadPluginList(manifest); + }); + } + + private void reloadPluginList(List manifest) + { + Map manifests = manifest.stream() + .collect(ImmutableMap.toImmutableMap(ExternalPluginManifest::getInternalName, Function.identity())); + + Multimap loadedPlugins = HashMultimap.create(); + for (Plugin p : pluginManager.getPlugins()) + { + Class clazz = p.getClass(); + ExternalPluginManifest mf = ExternalPluginManager.getExternalPluginManifest(clazz); + if (mf != null) + { + loadedPlugins.put(mf.getInternalName(), p); + } + } + + Set installed = new HashSet<>(externalPluginManager.getInstalledExternalPlugins()); + + SwingUtilities.invokeLater(() -> + { + plugins = Sets.union(manifests.keySet(), loadedPlugins.keySet()) + .stream() + .map(id -> new PluginItem(manifests.get(id), loadedPlugins.get(id), installed.contains(id))) + .collect(Collectors.toList()); + + refreshing.setVisible(false); + filter(); + }); + } + + void filter() + { + if (refreshing.isVisible()) + { + return; + } + + mainPanel.removeAll(); + + Stream stream = plugins.stream(); + + String query = searchBar.getText(); + boolean isSearching = query != null && !query.trim().isEmpty(); + if (isSearching) + { + PluginSearch.search(plugins, query).forEach(mainPanel::add); + } + else + { + stream + .sorted(Comparator.comparing(PluginItem::isInstalled).thenComparing(p -> p.manifest.getDisplayName())) + .forEach(mainPanel::add); + } + + mainPanel.revalidate(); + } + + @Override + public void onActivate() + { + revalidate(); + searchBar.setText(""); + reloadPluginList(); + searchBar.requestFocusInWindow(); + } + + @Subscribe + private void onExternalPluginsChanged(ExternalPluginsChanged ev) + { + SwingUtilities.invokeLater(() -> reloadPluginList(ev.getLoadedManifest())); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java index 17891ce08b..c71bcc8788 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java @@ -26,38 +26,37 @@ package net.runelite.client.plugins.config; import java.awt.BorderLayout; import java.awt.Color; +import java.awt.Component; import java.awt.Dimension; import java.awt.GridLayout; +import java.awt.MouseInfo; +import java.awt.Point; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Map; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JLabel; -import javax.swing.JOptionPane; +import javax.swing.JMenuItem; import javax.swing.JPanel; +import javax.swing.JPopupMenu; import javax.swing.JToggleButton; -import javax.swing.SwingWorker; +import javax.swing.SwingUtilities; +import javax.swing.border.EmptyBorder; import lombok.Getter; -import net.runelite.client.RuneLiteProperties; -import net.runelite.client.plugins.ExternalPluginManager; -import net.runelite.client.plugins.PluginType; -import net.runelite.client.ui.ClientUI; +import net.runelite.client.externalplugins.ExternalPluginManifest; import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.PluginPanel; import net.runelite.client.util.ImageUtil; import net.runelite.client.util.SwingUtil; -public class PluginListItem extends JPanel +class PluginListItem extends JPanel implements SearchablePlugin { private static final ImageIcon CONFIG_ICON; private static final ImageIcon CONFIG_ICON_HOVER; - private static final ImageIcon REFRESH_ICON; - private static final ImageIcon REFRESH_ICON_HOVER; private static final ImageIcon ON_STAR; private static final ImageIcon OFF_STAR; @@ -69,22 +68,16 @@ public class PluginListItem extends JPanel @Getter private final List keywords = new ArrayList<>(); - public JLabel nameLabel; private final JToggleButton pinButton; private final JToggleButton onOffToggle; - private Color color = null; - static { BufferedImage configIcon = ImageUtil.getResourceStreamFromClass(ConfigPanel.class, "config_edit_icon.png"); - BufferedImage refreshIcon = ImageUtil.getResourceStreamFromClass(ConfigPanel.class, "refresh.png"); BufferedImage onStar = ImageUtil.getResourceStreamFromClass(ConfigPanel.class, "star_on.png"); CONFIG_ICON = new ImageIcon(configIcon); - REFRESH_ICON = new ImageIcon(refreshIcon); - ON_STAR = new ImageIcon(ImageUtil.recolorImage(onStar, ColorScheme.BRAND_BLUE)); + ON_STAR = new ImageIcon(onStar); CONFIG_ICON_HOVER = new ImageIcon(ImageUtil.luminanceOffset(configIcon, -100)); - REFRESH_ICON_HOVER = new ImageIcon(ImageUtil.luminanceOffset(refreshIcon, -100)); BufferedImage offStar = ImageUtil.luminanceScale( ImageUtil.grayscaleImage(onStar), @@ -93,7 +86,7 @@ public class PluginListItem extends JPanel OFF_STAR = new ImageIcon(offStar); } - PluginListItem(PluginListPanel pluginListPanel, PluginConfigurationDescriptor pluginConfig, ExternalPluginManager externalPluginManager) + PluginListItem(PluginListPanel pluginListPanel, PluginConfigurationDescriptor pluginConfig) { this.pluginListPanel = pluginListPanel; this.pluginConfig = pluginConfig; @@ -101,11 +94,21 @@ public class PluginListItem extends JPanel Collections.addAll(keywords, pluginConfig.getName().toLowerCase().split(" ")); Collections.addAll(keywords, pluginConfig.getDescription().toLowerCase().split(" ")); Collections.addAll(keywords, pluginConfig.getTags()); + ExternalPluginManifest mf = pluginConfig.getExternalPluginManifest(); + if (mf != null) + { + keywords.add("pluginhub"); + keywords.add(mf.getInternalName()); + } + else + { + keywords.add("plugin"); // we don't want searching plugin to only show hub plugins + } setLayout(new BorderLayout(3, 0)); setPreferredSize(new Dimension(PluginPanel.PANEL_WIDTH, 20)); - nameLabel = new JLabel(pluginConfig.getName()); + JLabel nameLabel = new JLabel(pluginConfig.getName()); nameLabel.setForeground(Color.WHITE); if (!pluginConfig.getDescription().isEmpty()) @@ -130,61 +133,7 @@ public class PluginListItem extends JPanel buttonPanel.setLayout(new GridLayout(1, 2)); add(buttonPanel, BorderLayout.LINE_END); - Map> pluginsInfoMap = externalPluginManager.getPluginsInfoMap(); - - if (ExternalPluginManager.isDevelopmentMode() || - (RuneLiteProperties.getLauncherVersion() == null && pluginConfig.getPlugin() != null && pluginsInfoMap.containsKey(pluginConfig.getPlugin().getClass().getSimpleName()))) - { - JButton hotSwapButton = new JButton(REFRESH_ICON); - hotSwapButton.setRolloverIcon(REFRESH_ICON_HOVER); - SwingUtil.removeButtonDecorations(hotSwapButton); - hotSwapButton.setPreferredSize(new Dimension(25, 0)); - hotSwapButton.setVisible(false); - buttonPanel.add(hotSwapButton); - - hotSwapButton.addActionListener(e -> - { - Map pluginInfo = pluginsInfoMap.get(pluginConfig.getPlugin().getClass().getSimpleName()); - String pluginId = pluginInfo.get("id"); - - hotSwapButton.setIcon(REFRESH_ICON); - - new SwingWorker<>() - { - @Override - protected Boolean doInBackground() - { - return externalPluginManager.uninstall(pluginId); - } - - @Override - protected void done() - { - // In development mode our plugins will be loaded directly from sources, so we don't need to prompt - if (!ExternalPluginManager.isDevelopmentMode()) - { - JOptionPane.showMessageDialog(ClientUI.getFrame(), - pluginId + " is unloaded, put the new jar file in the externalmanager folder and click `ok`", - "Hotswap " + pluginId, - JOptionPane.INFORMATION_MESSAGE); - } - - new SwingWorker<>() - { - @Override - protected Boolean doInBackground() - { - return externalPluginManager.reloadStart(pluginId); - } - }.execute(); - } - }.execute(); - }); - - hotSwapButton.setVisible(true); - hotSwapButton.setToolTipText("Hotswap plugin"); - } - + JMenuItem configMenuItem = null; if (pluginConfig.hasConfigurables()) { JButton configButton = new JButton(CONFIG_ICON); @@ -202,9 +151,19 @@ public class PluginListItem extends JPanel configButton.setVisible(true); configButton.setToolTipText("Edit plugin configuration"); + + configMenuItem = new JMenuItem("Configure"); + configMenuItem.addActionListener(e -> openGroupConfigPanel()); } - addLabelMouseOver(nameLabel); + JMenuItem uninstallItem = null; + if (mf != null) + { + uninstallItem = new JMenuItem("Uninstall"); + uninstallItem.addActionListener(ev -> pluginListPanel.getExternalPluginManager().remove(mf.getInternalName())); + } + + addLabelPopupMenu(nameLabel, configMenuItem, pluginConfig.createSupportMenuItem(), uninstallItem); add(nameLabel, BorderLayout.CENTER); onOffToggle = new PluginToggleButton(); @@ -229,7 +188,14 @@ public class PluginListItem extends JPanel } } - boolean isPinned() + @Override + public String getSearchableName() + { + return pluginConfig.getName(); + } + + @Override + public boolean isPinned() { return pinButton.isSelected(); } @@ -239,27 +205,6 @@ public class PluginListItem extends JPanel pinButton.setSelected(pinned); } - public PluginType getPluginType() - { - return pluginConfig.getPluginType(); - } - - public Color getColor() - { - return this.color == null ? Color.WHITE : this.color; - } - - public void setColor(Color color) - { - if (color == null) - { - return; - } - - this.color = color; - this.nameLabel.setForeground(color); - } - void setPluginEnabled(boolean enabled) { onOffToggle.setSelected(enabled); @@ -271,21 +216,49 @@ public class PluginListItem extends JPanel } /** - * Adds a mouseover effect to change the text of the passed label to {@link ColorScheme#BRAND_BLUE} color + * Adds a mouseover effect to change the text of the passed label to {@link ColorScheme#BRAND_ORANGE} color, and + * adds the passed menu items to a popup menu shown when the label is clicked. * - * @param label The label to attach the mouseover and click effects to + * @param label The label to attach the mouseover and click effects to + * @param menuItems The menu items to be shown when the label is clicked */ - static void addLabelMouseOver(final JLabel label) + static void addLabelPopupMenu(JLabel label, JMenuItem... menuItems) { + final JPopupMenu menu = new JPopupMenu(); + final Color labelForeground = label.getForeground(); + menu.setBorder(new EmptyBorder(5, 5, 5, 5)); + + for (final JMenuItem menuItem : menuItems) + { + if (menuItem == null) + { + continue; + } + + // Some machines register mouseEntered through a popup menu, and do not register mouseExited when a popup + // menu item is clicked, so reset the label's color when we click one of these options. + menuItem.addActionListener(e -> label.setForeground(labelForeground)); + menu.add(menuItem); + } + label.addMouseListener(new MouseAdapter() { private Color lastForeground; + @Override + public void mouseClicked(MouseEvent mouseEvent) + { + Component source = (Component) mouseEvent.getSource(); + Point location = MouseInfo.getPointerInfo().getLocation(); + SwingUtilities.convertPointFromScreen(location, source); + menu.show(source, location.x, location.y); + } + @Override public void mouseEntered(MouseEvent mouseEvent) { lastForeground = label.getForeground(); - label.setForeground(ColorScheme.BRAND_BLUE); + label.setForeground(ColorScheme.BRAND_ORANGE); } @Override diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListPanel.java index e2b8d13568..8894b0fcc9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListPanel.java @@ -24,36 +24,23 @@ */ package net.runelite.client.plugins.config; -import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableList; import java.awt.BorderLayout; -import java.awt.Color; import java.awt.Component; -import java.awt.Container; import java.awt.Dimension; -import java.awt.Insets; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.image.BufferedImage; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.concurrent.ScheduledExecutorService; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.inject.Inject; import javax.inject.Provider; import javax.inject.Singleton; -import javax.swing.BoxLayout; -import javax.swing.ImageIcon; -import javax.swing.JLabel; +import javax.swing.JButton; import javax.swing.JPanel; import javax.swing.JScrollPane; -import javax.swing.JToggleButton; import javax.swing.ScrollPaneConstants; import javax.swing.SwingUtilities; import javax.swing.border.EmptyBorder; @@ -61,153 +48,91 @@ import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import net.runelite.api.util.Text; -import net.runelite.client.RuneLite; import net.runelite.client.config.Config; import net.runelite.client.config.ConfigDescriptor; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigManager; -import net.runelite.client.config.OpenOSRSConfig; import net.runelite.client.config.RuneLiteConfig; import net.runelite.client.eventbus.EventBus; -import net.runelite.client.events.ConfigChanged; -import net.runelite.client.events.ExternalPluginChanged; -import net.runelite.client.events.ExternalPluginsLoaded; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.ExternalPluginsChanged; import net.runelite.client.events.PluginChanged; -import net.runelite.client.plugins.ExternalPluginManager; +import net.runelite.client.externalplugins.ExternalPluginManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.plugins.PluginInstantiationException; import net.runelite.client.plugins.PluginManager; -import net.runelite.client.plugins.PluginType; import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.DynamicGridLayout; -import net.runelite.client.ui.FontManager; import net.runelite.client.ui.MultiplexingPluginPanel; import net.runelite.client.ui.PluginPanel; import net.runelite.client.ui.components.IconTextField; -import net.runelite.client.ui.components.MinimumSizedPanel; -import net.runelite.client.util.ImageUtil; -import net.runelite.client.util.SwingUtil; +import net.runelite.client.util.Text; @Slf4j @Singleton -public class PluginListPanel extends PluginPanel +class PluginListPanel extends PluginPanel { - private static final int OFFSET = 6; - - private static final ImageIcon SECTION_EXPAND_ICON; - private static final ImageIcon SECTION_EXPAND_ICON_HOVER; - private static final ImageIcon SECTION_RETRACT_ICON; - private static final ImageIcon SECTION_RETRACT_ICON_HOVER; - private static final String RUNELITE_GROUP_NAME = RuneLiteConfig.class.getAnnotation(ConfigGroup.class).value(); private static final String PINNED_PLUGINS_CONFIG_KEY = "pinnedPlugins"; - private static final Splitter SPLITTER = Splitter.on(" ").trimResults().omitEmptyStrings(); - private static final List CATEGORY_TAGS = List.of( + private static final ImmutableList CATEGORY_TAGS = ImmutableList.of( "Combat", "Chat", "Item", "Minigame", "Notification", + "Plugin Hub", "Skilling", "XP" ); - private static final List colorOptions = Arrays.asList("enabledColors", "pvmColor", "pvpColor", "skillingColor", "utilityColor", "minigameColor", "miscellaneousColor", "gamemodeColor"); - private static final List definedOrder = List.of(PluginType.SYSTEM, PluginType.PVM, PluginType.SKILLING, PluginType.PVP, PluginType.UTILITY, PluginType.MINIGAME, PluginType.MISCELLANEOUS, PluginType.GAMEMODE, PluginType.UNCATEGORIZED); - private static final Comparator categoryComparator = Comparator.comparing(plugin -> definedOrder.indexOf(plugin.getPluginType())); - private final ConfigManager configManager; private final PluginManager pluginManager; private final Provider configPanelProvider; - private final OpenOSRSConfig openOSRSConfig; + private final List fakePlugins = new ArrayList<>(); + + @Getter private final ExternalPluginManager externalPluginManager; @Getter private final MultiplexingPluginPanel muxer; + private final IconTextField searchBar; private final JScrollPane scrollPane; private final FixedWidthPanel mainPanel; - private final Set sectionsCopy = new HashSet<>(); - - public static List pluginList; - - static - { - final BufferedImage backIcon = ImageUtil.getResourceStreamFromClass(ConfigPanel.class, "config_back_icon.png"); - final BufferedImage orangeBackIcon = ImageUtil.fillImage(backIcon, ColorScheme.BRAND_BLUE); - - final BufferedImage sectionRetractIcon = ImageUtil.rotateImage(orangeBackIcon, Math.PI * 1.5); - SECTION_RETRACT_ICON = new ImageIcon(sectionRetractIcon); - SECTION_RETRACT_ICON_HOVER = new ImageIcon(ImageUtil.alphaOffset(sectionRetractIcon, -100)); - - final BufferedImage sectionExpandIcon = ImageUtil.rotateImage(orangeBackIcon, Math.PI); - SECTION_EXPAND_ICON = new ImageIcon(sectionExpandIcon); - SECTION_EXPAND_ICON_HOVER = new ImageIcon(ImageUtil.alphaOffset(sectionExpandIcon, -100)); - } + private List pluginList; @Inject public PluginListPanel( ConfigManager configManager, PluginManager pluginManager, - Provider configPanelProvider, - OpenOSRSConfig openOSRSConfig, ExternalPluginManager externalPluginManager, - EventBus eventBus) + ScheduledExecutorService executorService, + EventBus eventBus, + Provider configPanelProvider, + Provider pluginHubPanelProvider) { super(false); this.configManager = configManager; this.pluginManager = pluginManager; - this.configPanelProvider = configPanelProvider; - this.openOSRSConfig = openOSRSConfig; this.externalPluginManager = externalPluginManager; + this.configPanelProvider = configPanelProvider; - eventBus.subscribe(ConfigChanged.class, this, ev -> { - if (ev.getGroup().equals("runelite") && ev.getKey().equals("pinnedPlugins") && !ev.getOrigin().equals(RuneLite.uuid)) + muxer = new MultiplexingPluginPanel(this) + { + @Override + protected void onAdd(PluginPanel p) { - SwingUtilities.invokeLater(this::rebuildPluginList); + eventBus.register(p); } - if (!ev.getGroup().equals("openosrs")) + @Override + protected void onRemove(PluginPanel p) { - return; + eventBus.unregister(p); } - - if (ev.getKey().equals("enableCategories") || ev.getKey().equals("pluginSortMode")) - { - SwingUtilities.invokeLater(this::rebuildPluginList); - } - - if (ev.getKey().equals("pluginSortMode")) - { - sortPluginList(null); - } - - if (pluginList != null && colorOptions.stream().anyMatch(option -> option.equals(ev.getKey()))) - { - pluginList.forEach(listItem -> - { - if (listItem.getPluginType() == PluginType.SYSTEM) - { - return; - } - - listItem.setColor(getColorByCategory(listItem.getPluginType())); - }); - } - }); - - eventBus.subscribe(ExternalPluginsLoaded.class, this, ignored -> { - eventBus.subscribe(ExternalPluginChanged.class, this, ev -> SwingUtilities.invokeLater(this::rebuildPluginList)); - - SwingUtilities.invokeLater(this::rebuildPluginList); - }); - - eventBus.subscribe(PluginChanged.class, this, this::onPluginChanged); - - muxer = new MultiplexingPluginPanel(this); + }; searchBar = new IconTextField(); searchBar.setIcon(IconTextField.Icon.SEARCH); @@ -250,6 +175,12 @@ public class PluginListPanel extends PluginPanel mainPanel.setLayout(new DynamicGridLayout(0, 1, 0, 5)); mainPanel.setAlignmentX(Component.LEFT_ALIGNMENT); + JButton externalPluginButton = new JButton("Plugin Hub"); + externalPluginButton.setBorder(new EmptyBorder(5, 5, 5, 5)); + externalPluginButton.setLayout(new BorderLayout(0, BORDER_OFFSET)); + externalPluginButton.addActionListener(l -> muxer.pushState(pluginHubPanelProvider.get())); + add(externalPluginButton, BorderLayout.SOUTH); + JPanel northPanel = new FixedWidthPanel(); northPanel.setLayout(new BorderLayout()); northPanel.add(mainPanel, BorderLayout.NORTH); @@ -265,7 +196,7 @@ public class PluginListPanel extends PluginPanel // populate pluginList with all non-hidden plugins pluginList = Stream.concat( - pluginManager.getFakePlugins().stream(), + fakePlugins.stream(), pluginManager.getPlugins().stream() .filter(plugin -> !plugin.getClass().getAnnotation(PluginDescriptor.class).hidden()) .map(plugin -> @@ -277,30 +208,28 @@ public class PluginListPanel extends PluginPanel return new PluginConfigurationDescriptor( descriptor.name(), descriptor.description(), - descriptor.type(), descriptor.tags(), plugin, config, configDescriptor); }) - ).map(desc -> - { - PluginListItem listItem = new PluginListItem(this, desc, externalPluginManager); - listItem.setPinned(pinnedPlugins.contains(desc.getName())); - listItem.setColor(getColorByCategory(listItem.getPluginType())); + ) + .map(desc -> + { + PluginListItem listItem = new PluginListItem(this, desc); + listItem.setPinned(pinnedPlugins.contains(desc.getName())); + return listItem; + }) + .sorted(Comparator.comparing(p -> p.getPluginConfig().getName())) + .collect(Collectors.toList()); - return listItem; - }).collect(Collectors.toList()); - - sortPluginList(null); mainPanel.removeAll(); refresh(); } void addFakePlugin(PluginConfigurationDescriptor... descriptor) { - Collections.addAll(pluginManager.getFakePlugins(), descriptor); - pluginManager.loadFakePluginConfiguration(); + Collections.addAll(fakePlugins, descriptor); } void refresh() @@ -324,154 +253,21 @@ public class PluginListPanel extends PluginPanel scrollPane.getVerticalScrollBar().setValue(scrollBarPosition); } + void openWithFilter(String filter) + { + searchBar.setText(filter); + onSearchBarChanged(); + muxer.pushState(this); + } + private void onSearchBarChanged() { final String text = searchBar.getText(); - pluginList.forEach(mainPanel::remove); - - sectionsCopy.forEach(mainPanel::remove); - sectionsCopy.clear(); - - showMatchingPlugins(true, text); - showMatchingPlugins(false, text); - + PluginSearch.search(pluginList, text).forEach(mainPanel::add); revalidate(); } - private void generatePluginList(List pluginListItems) - { - final Map sections = new HashMap<>(); - - for (PluginListItem pluginListItem : pluginListItems) - { - if (pluginListItem.isPinned()) - { - if (!sections.containsKey("Pinned")) - { - sections.put("Pinned", addSection("Pinned")); - } - - sections.get("Pinned").add(pluginListItem); - continue; - } - - String sectionName = pluginListItem.getPluginConfig().getPluginType().getName(); - - if (!sections.containsKey(sectionName)) - { - sections.put(sectionName, addSection(sectionName)); - } - - sections.get(sectionName).add(pluginListItem); - } - - populateSections(sections); - } - - private void generatePluginListByRepo(List pluginListItems) - { - final Map sections = new HashMap<>(); - - for (PluginListItem pluginListItem : pluginListItems) - { - if (pluginListItem.isPinned()) - { - if (!sections.containsKey("Pinned")) - { - sections.put("Pinned", addSection("Pinned")); - } - - sections.get("Pinned").add(pluginListItem); - continue; - } - - Plugin plugin = pluginListItem.getPluginConfig().getPlugin(); - String sectionName; - - try - { - Map pluginInfoMap = externalPluginManager.getPluginsInfoMap().get(plugin.getClass().getSimpleName()); - sectionName = pluginInfoMap.get("provider"); - } - catch (NullPointerException e) - { - sectionName = "System"; - } - - if (!sections.containsKey(sectionName)) - { - sections.put(sectionName, addSection(sectionName)); - } - - sections.get(sectionName).add(pluginListItem); - } - - populateSections(sections); - } - - private void populateSections(Map sections) - { - sections.forEach((key, value) -> - { - Container parent = value.getParent(); - JToggleButton collapseButton = (JToggleButton) ((JPanel) parent.getComponent(0)).getComponent(0); - - if (searchBar.getText().equals("")) - { - resetSection(key, collapseButton, value); - } - else - { - forceExpandSection(collapseButton, value); - } - }); - } - - private void showMatchingPlugins(boolean pinned, String text) - { - final List plugins = new ArrayList<>(); - - if (text.isEmpty()) - { - if (openOSRSConfig.pluginSortMode() == OpenOSRSConfig.SortStyle.ALPHABETICALLY || (!openOSRSConfig.enableCategories() && (openOSRSConfig.pluginSortMode() != OpenOSRSConfig.SortStyle.REPOSITORY))) - { - pluginList.stream().filter(item -> pinned == item.isPinned()).forEach(mainPanel::add); - } - else - { - pluginList.stream().filter(item -> pinned == item.isPinned()).forEach(plugins::add); - } - } - else - { - pluginList.forEach(listItem -> - { - if (pinned == listItem.isPinned() && Text.matchesSearchTerms(SPLITTER.split(text.toLowerCase()), listItem.getKeywords())) - { - if (openOSRSConfig.pluginSortMode() == OpenOSRSConfig.SortStyle.ALPHABETICALLY || (!openOSRSConfig.enableCategories() && (openOSRSConfig.pluginSortMode() != OpenOSRSConfig.SortStyle.REPOSITORY))) - { - mainPanel.add(listItem); - } - else - { - plugins.add(listItem); - } - } - }); - } - - if (openOSRSConfig.pluginSortMode() == OpenOSRSConfig.SortStyle.CATEGORY && openOSRSConfig.enableCategories()) - { - generatePluginList(plugins); - } - - if (openOSRSConfig.pluginSortMode() == OpenOSRSConfig.SortStyle.REPOSITORY) - { - generatePluginListByRepo(plugins); - } - } - void openConfigurationPanel(String configGroup) { for (PluginListItem pluginListItem : pluginList) @@ -484,6 +280,18 @@ public class PluginListPanel extends PluginPanel } } + void openConfigurationPanel(Plugin plugin) + { + for (PluginListItem pluginListItem : pluginList) + { + if (pluginListItem.getPluginConfig().getPlugin() == plugin) + { + openConfigurationPanel(pluginListItem.getPluginConfig()); + break; + } + } + } + void openConfigurationPanel(PluginConfigurationDescriptor plugin) { ConfigPanel panel = configPanelProvider.get(); @@ -541,6 +349,7 @@ public class PluginListPanel extends PluginPanel configManager.setConfiguration(RUNELITE_GROUP_NAME, PINNED_PLUGINS_CONFIG_KEY, value); } + @Subscribe public void onPluginChanged(PluginChanged event) { SwingUtilities.invokeLater(this::refresh); @@ -563,161 +372,9 @@ public class PluginListPanel extends PluginPanel } } - private JPanel addSection(String name) + @Subscribe + private void onExternalPluginsChanged(ExternalPluginsChanged ev) { - final MinimumSizedPanel section = new MinimumSizedPanel(); - section.setLayout(new BoxLayout(section, BoxLayout.Y_AXIS)); - section.setMinimumSize(new Dimension(PANEL_WIDTH, 0)); - - JPanel item = new JPanel(); - item.setLayout(new BorderLayout()); - item.setMinimumSize(new Dimension(PANEL_WIDTH, 0)); - - JLabel headerLabel = new JLabel(name); - headerLabel.setFont(FontManager.getRunescapeFont()); - headerLabel.setForeground(ColorScheme.BRAND_BLUE); - headerLabel.setPreferredSize(new Dimension(PANEL_WIDTH, (int) headerLabel.getPreferredSize().getHeight())); - - final boolean state = Boolean.parseBoolean(configManager.getConfiguration("pluginlist", name)); - final JToggleButton collapse = new JToggleButton(SECTION_EXPAND_ICON, state); - - SwingUtil.removeButtonDecorations(collapse); - collapse.setRolloverIcon(SECTION_EXPAND_ICON_HOVER); - collapse.setSelectedIcon(SECTION_RETRACT_ICON); - collapse.setRolloverSelectedIcon(SECTION_RETRACT_ICON_HOVER); - collapse.setToolTipText(state ? "Retract" : "Expand"); - collapse.setPreferredSize(new Dimension(20, 20)); - collapse.setFont(collapse.getFont().deriveFont(16.0f)); - collapse.setBorder(null); - collapse.setMargin(new Insets(0, 0, 0, 0)); - - headerLabel.setBorder(new EmptyBorder(0, 10, 0, 0)); - - item.add(collapse, BorderLayout.WEST); - item.add(headerLabel, BorderLayout.CENTER); - - final JPanel sectionContents = new JPanel(); - sectionContents.setLayout(new DynamicGridLayout(0, 1, 0, 5)); - sectionContents.setMinimumSize(new Dimension(PANEL_WIDTH, 0)); - sectionContents.setBorder(new EmptyBorder(OFFSET, 5, 0, 0)); - section.add(item, BorderLayout.NORTH); - section.add(sectionContents, BorderLayout.SOUTH); - - sectionContents.setVisible(state); - - // Add listeners to each part of the header so that it's easier to toggle them - final MouseAdapter adapter = new MouseAdapter() - { - @Override - public void mouseClicked(MouseEvent e) - { - toggleSection(name, collapse, sectionContents); - } - }; - collapse.addActionListener(e -> toggleSection(name, collapse, sectionContents)); - headerLabel.addMouseListener(adapter); - - // Allow for sub-sections - mainPanel.add(section); - sectionsCopy.add(section); - - return sectionContents; - } - - private void toggleSection(String key, JToggleButton button, JPanel contents) - { - if (!button.isEnabled()) - { - return; - } - - boolean newState = !contents.isVisible(); - button.setSelected(newState); - button.setToolTipText(newState ? "Retract" : "Expand"); - contents.setVisible(newState); - configManager.setConfiguration("pluginlist", key, newState); - SwingUtilities.invokeLater(() -> - { - contents.revalidate(); - contents.repaint(); - }); - } - - private void forceExpandSection(JToggleButton button, JPanel contents) - { - button.setSelected(true); - button.setToolTipText(null); - button.setEnabled(false); - contents.setVisible(true); - - SwingUtilities.invokeLater(() -> - { - contents.revalidate(); - contents.repaint(); - }); - } - - private void resetSection(String key, JToggleButton button, JPanel contents) - { - boolean newState = Boolean.parseBoolean(configManager.getConfiguration("pluginlist", key)); - button.setSelected(newState); - button.setToolTipText(newState ? "Retract" : "Expand"); - contents.setVisible(newState); - SwingUtilities.invokeLater(() -> - { - contents.revalidate(); - contents.repaint(); - }); - } - - private Color getColorByCategory(PluginType pluginType) - { - if (!openOSRSConfig.enabledColors()) - { - return Color.LIGHT_GRAY; - } - - switch (pluginType) - { - case PVM: - return openOSRSConfig.pvmColor(); - case PVP: - return openOSRSConfig.pvpColor(); - case SKILLING: - return openOSRSConfig.skillingColor(); - case UTILITY: - return openOSRSConfig.utilityColor(); - case MISCELLANEOUS: - return openOSRSConfig.miscellaneousColor(); - case MINIGAME: - return openOSRSConfig.minigameColor(); - case GAMEMODE: - return openOSRSConfig.gamemodeColor(); - case SYSTEM: - return Color.WHITE; - } - - return Color.LIGHT_GRAY; - } - - public void sortPluginList(Comparator comparator) - { - if (pluginList == null) - return; - - if (comparator != null) - { - pluginList.sort(comparator.thenComparing(ev -> ev.getPluginConfig().getName())); - return; - } - - if (openOSRSConfig.pluginSortMode() == OpenOSRSConfig.SortStyle.CATEGORY) - { - pluginList.sort(categoryComparator.thenComparing(ev -> ev.getPluginConfig().getName())); - } - else - { - pluginList.sort(Comparator.comparing(ev -> ev.getPluginConfig().getName())); - } + SwingUtilities.invokeLater(this::rebuildPluginList); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginSearch.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginSearch.java new file mode 100644 index 0000000000..3cd3dad6cc --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginSearch.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2020, Jack Hodkinson + * 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.plugins.config; + +import com.google.common.base.Splitter; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; +import net.runelite.client.util.Text; +import org.apache.commons.lang3.StringUtils; + +public class PluginSearch +{ + private static final Splitter SPLITTER = Splitter.on(" ").trimResults().omitEmptyStrings(); + + public static List search(Collection searchablePlugins, String query) + { + return searchablePlugins.stream() + .filter(plugin -> Text.matchesSearchTerms(SPLITTER.split(query.toLowerCase()), plugin.getKeywords())) + .sorted(comparator(query)) + .collect(Collectors.toList()); + } + + private static Comparator comparator(String query) + { + if (StringUtils.isBlank(query)) + { + return Comparator.nullsLast(Comparator.comparing(SearchablePlugin::isPinned, Comparator.nullsLast(Comparator.reverseOrder()))) + .thenComparing(SearchablePlugin::getSearchableName, Comparator.nullsLast(Comparator.naturalOrder())); + } + Iterable queryPieces = SPLITTER.split(query.toLowerCase()); + return Comparator.nullsLast(Comparator.comparing((SearchablePlugin sp) -> query.equalsIgnoreCase(sp.getSearchableName()), Comparator.reverseOrder())) + .thenComparing(sp -> + { + if (sp.getSearchableName() == null) + { + return 0L; + } + return stream(SPLITTER.split(sp.getSearchableName())) + .filter(piece -> stream(queryPieces).anyMatch(qp -> containsOrIsContainedBy(piece.toLowerCase(), qp))) + .count(); + }, Comparator.reverseOrder()) + .thenComparing(sp -> + { + if (sp.getKeywords() == null) + { + return 0L; + } + return stream(sp.getKeywords()) + .filter(piece -> stream(queryPieces).anyMatch(qp -> containsOrIsContainedBy(piece.toLowerCase(), qp))) + .count(); + }, Comparator.reverseOrder()) + .thenComparing(SearchablePlugin::isPinned, Comparator.nullsLast(Comparator.reverseOrder())) + .thenComparing(SearchablePlugin::getSearchableName, Comparator.nullsLast(Comparator.naturalOrder())); + } + + private static Stream stream(Iterable iterable) + { + return StreamSupport.stream(iterable.spliterator(), false); + } + + private static boolean containsOrIsContainedBy(String a, String b) + { + return a.contains(b) || b.contains(a); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginToggleButton.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginToggleButton.java index 1134e8d41f..a2eb72719f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginToggleButton.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginToggleButton.java @@ -29,7 +29,6 @@ import java.awt.Dimension; import java.awt.image.BufferedImage; import javax.swing.ImageIcon; import javax.swing.JToggleButton; -import net.runelite.client.ui.ColorScheme; import net.runelite.client.util.ImageUtil; import net.runelite.client.util.SwingUtil; @@ -41,7 +40,7 @@ class PluginToggleButton extends JToggleButton static { BufferedImage onSwitcher = ImageUtil.getResourceStreamFromClass(ConfigPanel.class, "switcher_on.png"); - ON_SWITCHER = new ImageIcon(ImageUtil.recolorImage(onSwitcher, ColorScheme.BRAND_BLUE)); + ON_SWITCHER = new ImageIcon(onSwitcher); OFF_SWITCHER = new ImageIcon(ImageUtil.flipImage( ImageUtil.luminanceScale( ImageUtil.grayscaleImage(onSwitcher), @@ -60,4 +59,4 @@ class PluginToggleButton extends JToggleButton setPreferredSize(new Dimension(25, 0)); SwingUtil.addModalTooltip(this, "Disable plugin", "Enable plugin"); } -} \ No newline at end of file +} diff --git a/runelite-client/src/main/java/net/runelite/client/events/DrawFinished.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/SearchablePlugin.java similarity index 82% rename from runelite-client/src/main/java/net/runelite/client/events/DrawFinished.java rename to runelite-client/src/main/java/net/runelite/client/plugins/config/SearchablePlugin.java index 32308f7558..c8656787fa 100644 --- a/runelite-client/src/main/java/net/runelite/client/events/DrawFinished.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/SearchablePlugin.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Adam + * Copyright (c) 2020, Jack Hodkinson * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,12 +22,18 @@ * (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.events; +package net.runelite.client.plugins.config; -import net.runelite.api.events.Event; -import java.awt.Image; +import java.util.List; -public class DrawFinished implements Event +public interface SearchablePlugin { - public Image image; -} \ No newline at end of file + String getSearchableName(); + + List getKeywords(); + + default boolean isPinned() + { + return false; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/UnitFormatter.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/UnitFormatter.java index 4945a4a65e..efe2181d9b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/UnitFormatter.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/UnitFormatter.java @@ -61,7 +61,7 @@ final class UnitFormatter extends JFormattedTextField.AbstractFormatter } catch (NumberFormatException e) { - throw new ParseException(trimmedText + " is not an integer.", 0); + throw new ParseException(trimmedText + " is not an integer.", 0); // NOPMD: PreserveStackTrace } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPanel.java deleted file mode 100644 index c367c17701..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPanel.java +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Copyright (c) 2018 Abex - * Copyright (c) 2018, Psikoi - * 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.plugins.info; - -import com.google.inject.Inject; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Cursor; -import java.awt.Font; -import java.awt.GridLayout; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.io.File; -import javax.annotation.Nullable; -import javax.inject.Singleton; -import javax.swing.ImageIcon; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.border.EmptyBorder; -import net.runelite.api.Client; -import static net.runelite.client.RuneLite.LOGS_DIR; -import static net.runelite.client.RuneLite.RUNELITE_DIR; -import static net.runelite.client.RuneLite.SCREENSHOT_DIR; -import static net.runelite.client.RuneLite.EXTERNALPLUGIN_DIR; -import net.runelite.client.RuneLiteProperties; -import net.runelite.client.config.ConfigManager; -import net.runelite.client.ui.ColorScheme; -import net.runelite.client.ui.FontManager; -import net.runelite.client.ui.PluginPanel; -import net.runelite.client.util.ImageUtil; -import net.runelite.client.util.LinkBrowser; - -@Singleton -class InfoPanel extends PluginPanel -{ - private static final ImageIcon ARROW_RIGHT_ICON; - private static final ImageIcon GITHUB_ICON; - private static final ImageIcon FOLDER_ICON; - private static final ImageIcon DISCORD_ICON; - private static final ImageIcon PATREON_ICON; - private static final ImageIcon IMPORT_ICON; - private static final String RUNELITE_DIRECTORY = System.getProperty("user.home") + "\\.runelite"; - private static final String LOG_DIRECTORY = RUNELITE_DIRECTORY + "\\logs"; - private static final String PLUGINS_DIRECTORY = RUNELITE_DIRECTORY + "\\externalmanager"; - private static final String SCREENSHOT_DIRECTORY = RUNELITE_DIRECTORY + "\\screenshots"; - - static - { - ARROW_RIGHT_ICON = new ImageIcon(ImageUtil.getResourceStreamFromClass(InfoPanel.class, "/util/arrow_right.png")); - GITHUB_ICON = new ImageIcon(ImageUtil.getResourceStreamFromClass(InfoPanel.class, "github_icon.png")); - FOLDER_ICON = new ImageIcon(ImageUtil.getResourceStreamFromClass(InfoPanel.class, "folder_icon.png")); - DISCORD_ICON = new ImageIcon(ImageUtil.getResourceStreamFromClass(InfoPanel.class, "discord_icon.png")); - PATREON_ICON = new ImageIcon(ImageUtil.getResourceStreamFromClass(InfoPanel.class, "patreon_icon.png")); - IMPORT_ICON = new ImageIcon(ImageUtil.getResourceStreamFromClass(InfoPanel.class, "import_icon.png")); - } - - private JPanel syncPanel; - - @Inject - @Nullable - private Client client; - - @Inject - private ConfigManager configManager; - - @Inject - private InfoPlugin plugin; - - @Inject - public InfoPanel(final InfoPlugin plugin, final Client client) - { - setLayout(new BorderLayout()); - setBackground(ColorScheme.DARK_GRAY_COLOR); - setBorder(new EmptyBorder(10, 10, 10, 10)); - - JPanel versionPanel = new JPanel(); - versionPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); - versionPanel.setBorder(new EmptyBorder(10, 10, 10, 10)); - versionPanel.setLayout(new GridLayout(0, 1)); - - final Font smallFont = FontManager.getRunescapeSmallFont(); - - JLabel version = new JLabel(htmlLabel("RuneLite version: ", RuneLiteProperties.getVersion())); - version.setFont(smallFont); - - JLabel openOsrsVersion = new JLabel(htmlLabel("OpenOSRS version: ", RuneLiteProperties.getPlusVersion())); - openOsrsVersion.setFont(smallFont); - - String launcher = RuneLiteProperties.getLauncherVersion(); - JLabel launcherVersion = new JLabel(htmlLabel("Launcher version: ", launcher)); - launcherVersion.setFont(smallFont); - - JLabel revision = new JLabel(); - revision.setFont(smallFont); - - String engineVer = "Unknown"; - if (client != null) - { - engineVer = String.format("Rev %d", client.getRevision()); - } - - revision.setText(htmlLabel("Oldschool revision: ", engineVer)); - - versionPanel.add(version); - versionPanel.add(openOsrsVersion); - if (launcher != null) - { - versionPanel.add(launcherVersion); - } - versionPanel.add(revision); - - JPanel actionsContainer = new JPanel(); - actionsContainer.setBorder(new EmptyBorder(10, 0, 10, 0)); - actionsContainer.setLayout(new GridLayout(9, 1, 0, 10)); - - syncPanel = buildLinkPanel(IMPORT_ICON, "Import local settings", "to remote RuneLite account", () -> - { - final int result = JOptionPane.showOptionDialog(syncPanel, - "This will replace your current RuneLite account settings with settings from your local profile.", - "Are you sure?", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, - null, new String[]{"Yes", "No"}, "No"); - - if (result == JOptionPane.YES_OPTION) - { - configManager.importLocal(); - } - }); - - actionsContainer.add(buildLinkPanel(GITHUB_ICON, "License info", "for distribution", "https://github.com/open-osrs/runelite/blob/master/LICENSE")); - actionsContainer.add(buildLinkPanel(PATREON_ICON, "Patreon to support", "the OpenOSRS Devs", RuneLiteProperties.getPatreonLink())); - actionsContainer.add(buildLinkPanel(DISCORD_ICON, "Talk to us on our", "Discord Server", "https://discord.gg/OpenOSRS")); - actionsContainer.add(buildLinkPanel(GITHUB_ICON, "OpenOSRS Github", "", "https://github.com/open-osrs")); - actionsContainer.add(buildLinkPanel(IMPORT_ICON, "Launcher Download", "for the latest launcher", "https://github.com/open-osrs/launcher/releases")); - actionsContainer.add(buildLinkPanel(FOLDER_ICON, "Open Runelite Directory", "for your .properties file", RUNELITE_DIR)); - actionsContainer.add(buildLinkPanel(FOLDER_ICON, "Open Logs Directory", "for bug reports", LOGS_DIR)); - actionsContainer.add(buildLinkPanel(FOLDER_ICON, "Open ExternalManager Directory", "to see your plugins", EXTERNALPLUGIN_DIR)); - actionsContainer.add(buildLinkPanel(FOLDER_ICON, "Open Screenshots Directory", "for your screenshots", SCREENSHOT_DIR)); - - JPanel pathPanel = new JPanel(); - pathPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); - pathPanel.setBorder(new EmptyBorder(10, 10, 10, 10)); - pathPanel.setLayout(new GridLayout(0, 1)); - - JLabel rldirectory = new JLabel(htmlLabel("Runelite Directory: ", RUNELITE_DIRECTORY)); - rldirectory.setFont(smallFont); - - JLabel logdirectory = new JLabel(htmlLabel("Log Directory: ", LOG_DIRECTORY)); - logdirectory.setFont(smallFont); - - JLabel pluginsdirectory = new JLabel(htmlLabel("Plugins Directory: ", PLUGINS_DIRECTORY)); - pluginsdirectory.setFont(smallFont); - - JLabel screenshotsdirectory = new JLabel(htmlLabel("Screenshot Directory: ", SCREENSHOT_DIRECTORY)); - screenshotsdirectory.setFont(smallFont); - - pathPanel.add(rldirectory); - pathPanel.add(logdirectory); - pathPanel.add(pluginsdirectory); - pathPanel.add(screenshotsdirectory); - - add(pathPanel, BorderLayout.SOUTH); - add(versionPanel, BorderLayout.NORTH); - add(actionsContainer, BorderLayout.CENTER); - - } - - /** - * Builds a link panel with a given icon, text and url to redirect to. - */ - private static JPanel buildLinkPanel(ImageIcon icon, String topText, String bottomText, String url) - { - return buildLinkPanel(icon, topText, bottomText, () -> LinkBrowser.browse(url)); - } - - /** - * Builds a link panel with a given icon, text and callable to call. - */ - private static JPanel buildLinkPanel(ImageIcon icon, String topText, String bottomText, Runnable callback) - { - JPanel container = new JPanel(); - container.setBackground(ColorScheme.DARKER_GRAY_COLOR); - container.setLayout(new BorderLayout()); - container.setBorder(new EmptyBorder(10, 10, 10, 10)); - - final Color hoverColor = ColorScheme.DARKER_GRAY_HOVER_COLOR; - final Color pressedColor = ColorScheme.DARKER_GRAY_COLOR.brighter(); - - JLabel iconLabel = new JLabel(icon); - container.add(iconLabel, BorderLayout.WEST); - - JPanel textContainer = new JPanel(); - textContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR); - textContainer.setLayout(new GridLayout(2, 1)); - textContainer.setBorder(new EmptyBorder(5, 10, 5, 10)); - - container.addMouseListener(new MouseAdapter() - { - @Override - public void mousePressed(MouseEvent mouseEvent) - { - container.setBackground(pressedColor); - textContainer.setBackground(pressedColor); - } - - @Override - public void mouseReleased(MouseEvent e) - { - callback.run(); - container.setBackground(hoverColor); - textContainer.setBackground(hoverColor); - } - - @Override - public void mouseEntered(MouseEvent e) - { - container.setBackground(hoverColor); - textContainer.setBackground(hoverColor); - container.setCursor(new Cursor(Cursor.HAND_CURSOR)); - } - - @Override - public void mouseExited(MouseEvent e) - { - container.setBackground(ColorScheme.DARKER_GRAY_COLOR); - textContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR); - container.setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); - } - }); - - JLabel topLine = new JLabel(topText); - topLine.setForeground(Color.WHITE); - topLine.setFont(FontManager.getRunescapeSmallFont()); - - JLabel bottomLine = new JLabel(bottomText); - bottomLine.setForeground(Color.WHITE); - bottomLine.setFont(FontManager.getRunescapeSmallFont()); - - textContainer.add(topLine); - textContainer.add(bottomLine); - - container.add(textContainer, BorderLayout.CENTER); - - JLabel arrowLabel = new JLabel(ARROW_RIGHT_ICON); - container.add(arrowLabel, BorderLayout.EAST); - - return container; - } - - private static String htmlLabel(String key, String value) - { - return "" + key + "" + value + ""; - } - - /** - * Builds a link panel with a given icon, text and directory to open. - */ - private JPanel buildLinkPanel(ImageIcon icon, String topText, String bottomText, File dir) - { - return buildLinkPanel(icon, topText, bottomText, () -> LinkBrowser.openLocalFile(dir)); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPlugin.java deleted file mode 100644 index 3c1780ac2e..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPlugin.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2018 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.plugins.info; - -import java.awt.image.BufferedImage; -import javax.inject.Inject; -import javax.inject.Singleton; -import net.runelite.client.eventbus.Subscribe; -import net.runelite.client.events.ConfigChanged; -import net.runelite.client.plugins.Plugin; -import net.runelite.client.plugins.PluginDescriptor; -import net.runelite.client.plugins.PluginType; -import net.runelite.client.ui.ClientToolbar; -import net.runelite.client.ui.NavigationButton; -import net.runelite.client.util.ImageUtil; - -@PluginDescriptor( - name = "Info Panel", - description = "Enable the Info panel", - tags = {"info", "github", "patreon", "dir", "discord"}, - loadWhenOutdated = true, - type = PluginType.MISCELLANEOUS -) -@Singleton -public class InfoPlugin extends Plugin -{ - @Inject - private ClientToolbar clientToolbar; - - private NavigationButton navButton; - - @Subscribe - private void onConfigChanged(ConfigChanged event) - { - if (!event.getGroup().equals("info")) - { - return; - } - } - - @Override - protected void startUp() - { - InfoPanel panel = injector.getInstance(InfoPanel.class); - - final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "info_icon.png"); - - navButton = NavigationButton.builder() - .tooltip("Info") - .icon(icon) - .priority(9) - .panel(panel) - .build(); - - clientToolbar.addNavigation(navButton); - } - - @Override - protected void shutDown() - { - clientToolbar.removeNavigation(navButton); - } -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/info/JRichTextPane.java b/runelite-client/src/main/java/net/runelite/client/plugins/info/JRichTextPane.java deleted file mode 100644 index 1edf78ca6d..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/info/JRichTextPane.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2018 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.plugins.info; - -import java.awt.Desktop; -import java.io.IOException; -import java.net.URISyntaxException; -import javax.inject.Singleton; -import javax.swing.BorderFactory; -import javax.swing.JEditorPane; -import javax.swing.event.HyperlinkEvent; -import javax.swing.event.HyperlinkListener; -import javax.swing.text.html.HTMLEditorKit; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -@Singleton -public class JRichTextPane extends JEditorPane -{ - private HyperlinkListener linkHandler; - - public JRichTextPane() - { - super(); - setHighlighter(null); - setEditable(false); - setOpaque(false); - enableAutoLinkHandler(true); - setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - HTMLEditorKit ek = (HTMLEditorKit) getEditorKitForContentType("text/html"); - ek.getStyleSheet().addRule("a {color: #DDDDDD }"); - } - - private JRichTextPane(String type, String text) - { - this(); - setContentType(type); - setText(text); - } - - private void enableAutoLinkHandler(boolean enable) - { - if (enable == (linkHandler == null)) - { - if (enable) - { - linkHandler = e -> - { - if (HyperlinkEvent.EventType.ACTIVATED.equals(e.getEventType()) && e.getURL() != null && Desktop.isDesktopSupported()) - { - try - { - Desktop.getDesktop().browse(e.getURL().toURI()); - } - catch (URISyntaxException | IOException ex) - { - log.warn("Error opening link", ex); - } - } - }; - addHyperlinkListener(linkHandler); - } - else - { - removeHyperlinkListener(linkHandler); - linkHandler = null; - } - } - } - - public boolean getAutoLinkHandlerEnabled() - { - return linkHandler != null; - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/OpenOSRSPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/OpenOSRSPlugin.java deleted file mode 100644 index 9c1d7dd621..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/OpenOSRSPlugin.java +++ /dev/null @@ -1,273 +0,0 @@ -/* - * - * Copyright (c) 2019, Zeruth - * 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.plugins.openosrs; - -import ch.qos.logback.classic.Logger; -import java.awt.event.KeyEvent; -import java.awt.image.BufferedImage; -import javax.inject.Inject; -import javax.inject.Singleton; -import lombok.extern.slf4j.Slf4j; -import net.runelite.api.Client; -import static net.runelite.api.ScriptID.BANK_PIN_OP; -import net.runelite.api.events.ScriptCallbackEvent; -import net.runelite.api.widgets.WidgetID; -import static net.runelite.api.widgets.WidgetInfo.*; -import net.runelite.client.callback.ClientThread; -import net.runelite.client.config.Keybind; -import net.runelite.client.config.OpenOSRSConfig; -import net.runelite.client.eventbus.Subscribe; -import net.runelite.client.events.ConfigChanged; -import net.runelite.client.input.KeyListener; -import net.runelite.client.input.KeyManager; -import net.runelite.client.plugins.Plugin; -import net.runelite.client.plugins.PluginDescriptor; -import net.runelite.client.plugins.openosrs.externals.ExternalPluginManagerPanel; -import net.runelite.client.ui.ClientToolbar; -import net.runelite.client.ui.NavigationButton; -import net.runelite.client.util.HotkeyListener; -import net.runelite.client.util.ImageUtil; -import org.slf4j.LoggerFactory; - -@PluginDescriptor( - loadWhenOutdated = true, // prevent users from disabling - hidden = true, // prevent users from disabling - name = "OpenOSRS" -) -@Singleton -@Slf4j -public class OpenOSRSPlugin extends Plugin -{ - private final openosrsKeyListener keyListener = new openosrsKeyListener(); - - @Inject - private OpenOSRSConfig config; - - @Inject - private KeyManager keyManager; - - @Inject - private Client client; - - @Inject - private ClientThread clientThread; - - @Inject - private ClientToolbar clientToolbar; - - private NavigationButton navButton; - - private final HotkeyListener hotkeyListener = new HotkeyListener(() -> this.keybind) - { - @Override - public void hotkeyPressed() - { - detach = !detach; - client.setOculusOrbState(detach ? 1 : 0); - client.setOculusOrbNormalSpeed(detach ? 36 : 12); - } - }; - private int entered = -1; - private int enterIdx; - private boolean expectInput; - private boolean detach; - private Keybind keybind; - - @Override - protected void startUp() - { - ExternalPluginManagerPanel panel = injector.getInstance(ExternalPluginManagerPanel.class); - - final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "externalmanager_icon.png"); - - navButton = NavigationButton.builder() - .tooltip("External Plugin Manager") - .icon(icon) - .priority(1) - .panel(panel) - .build(); - clientToolbar.addNavigation(navButton); - - entered = -1; - enterIdx = 0; - expectInput = false; - this.keybind = config.detachHotkey(); - keyManager.registerKeyListener(hotkeyListener); - } - - @Override - protected void shutDown() - { - clientToolbar.removeNavigation(navButton); - - entered = 0; - enterIdx = 0; - expectInput = false; - keyManager.unregisterKeyListener(keyListener); - keyManager.unregisterKeyListener(hotkeyListener); - } - - @Subscribe - private void onConfigChanged(ConfigChanged event) - { - if (!event.getGroup().equals("openosrs")) - { - return; - } - - this.keybind = config.detachHotkey(); - - if (!config.keyboardPin()) - { - entered = 0; - enterIdx = 0; - expectInput = false; - keyManager.unregisterKeyListener(keyListener); - } - - if (event.getKey().equals("shareLogs") && !config.shareLogs()) - { - final Logger logger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); - logger.detachAppender("Sentry"); - } - } - - @Subscribe - private void onScriptCallbackEvent(ScriptCallbackEvent e) - { - if (!config.keyboardPin()) - { - return; - } - - if (e.getEventName().equals("bankpin")) - { - int[] intStack = client.getIntStack(); - int intStackSize = client.getIntStackSize(); - - // This'll be anywhere from -1 to 3 - // 0 = first number, 1 second, etc - // Anything other than 0123 means the bankpin interface closes - int enterIdx = intStack[intStackSize - 1]; - - if (enterIdx < 0 || enterIdx > 3) - { - keyManager.unregisterKeyListener(keyListener); - this.enterIdx = 0; - this.entered = 0; - expectInput = false; - return; - } - else if (enterIdx == 0) - { - keyManager.registerKeyListener(keyListener); - } - - this.enterIdx = enterIdx; - expectInput = true; - } - } - - private void handleKey(char c) - { - if (client.getWidget(WidgetID.BANK_PIN_GROUP_ID, BANK_PIN_INSTRUCTION_TEXT.getChildId()) == null - || !client.getWidget(BANK_PIN_INSTRUCTION_TEXT).getText().equals("First click the FIRST digit.") - && !client.getWidget(BANK_PIN_INSTRUCTION_TEXT).getText().equals("Now click the SECOND digit.") - && !client.getWidget(BANK_PIN_INSTRUCTION_TEXT).getText().equals("Time for the THIRD digit.") - && !client.getWidget(BANK_PIN_INSTRUCTION_TEXT).getText().equals("Finally, the FOURTH digit.")) - - { - entered = 0; - enterIdx = 0; - expectInput = false; - keyManager.unregisterKeyListener(keyListener); - return; - } - - if (!expectInput) - { - return; - } - - int num = Character.getNumericValue(c); - - // We gotta copy this cause enteridx changes while the script is executing - int oldEnterIdx = enterIdx; - - // Script 685 will call 653, which in turn will set expectInput to true - expectInput = false; - client.runScript(BANK_PIN_OP, num, enterIdx, entered, BANK_PIN_EXIT_BUTTON.getId(), BANK_PIN_FORGOT_BUTTON.getId(), BANK_PIN_1.getId(), BANK_PIN_2.getId(), BANK_PIN_3.getId(), BANK_PIN_4.getId(), BANK_PIN_5.getId(), BANK_PIN_6.getId(), BANK_PIN_7.getId(), BANK_PIN_8.getId(), BANK_PIN_9.getId(), BANK_PIN_10.getId(), BANK_PIN_FIRST_ENTERED.getId(), BANK_PIN_SECOND_ENTERED.getId(), BANK_PIN_THIRD_ENTERED.getId(), BANK_PIN_FOURTH_ENTERED.getId(), BANK_PIN_INSTRUCTION_TEXT.getId()); - - if (oldEnterIdx == 0) - { - entered = num * 1000; - } - else if (oldEnterIdx == 1) - { - entered += num * 100; - } - else if (oldEnterIdx == 2) - { - entered += num * 10; - } - } - - private class openosrsKeyListener implements KeyListener - { - private int lastKeyCycle; - - @Override - public void keyTyped(KeyEvent keyEvent) - { - if (!Character.isDigit(keyEvent.getKeyChar())) - { - return; - } - - if (client.getGameCycle() - lastKeyCycle <= 5) - { - keyEvent.consume(); - return; - } - - lastKeyCycle = client.getGameCycle(); - - clientThread.invoke(() -> handleKey(keyEvent.getKeyChar())); - keyEvent.consume(); - } - - @Override - public void keyPressed(KeyEvent keyEvent) - { - } - - @Override - public void keyReleased(KeyEvent keyEvent) - { - } - } -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/ExternalBox.java b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/ExternalBox.java deleted file mode 100644 index 51c9e94b90..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/ExternalBox.java +++ /dev/null @@ -1,97 +0,0 @@ -package net.runelite.client.plugins.openosrs.externals; - -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Font; -import java.net.URL; -import javax.swing.BorderFactory; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JTextArea; -import javax.swing.border.CompoundBorder; -import javax.swing.border.EmptyBorder; -import javax.swing.text.DefaultCaret; -import net.runelite.client.ui.ColorScheme; -import net.runelite.client.ui.FontManager; -import org.pf4j.update.PluginInfo; - -public class ExternalBox extends JPanel -{ - private static final Font normalFont = FontManager.getRunescapeFont(); - private static final Font smallFont = FontManager.getRunescapeSmallFont(); - - PluginInfo pluginInfo; - JLabel install = new JLabel(); - JMultilineLabel description = new JMultilineLabel(); - - ExternalBox(String name, URL url) - { - this(name, url.toString().replace("https://raw.githubusercontent.com/", "").replace("/master/", "")); - } - - ExternalBox(PluginInfo pluginInfo) - { - this(pluginInfo.name, pluginInfo.description); - } - - ExternalBox(String name, String desc) - { - setLayout(new BorderLayout()); - setBackground(ColorScheme.DARKER_GRAY_COLOR); - - JPanel titleWrapper = new JPanel(new BorderLayout()); - titleWrapper.setBackground(ColorScheme.DARKER_GRAY_COLOR); - titleWrapper.setBorder(new CompoundBorder( - BorderFactory.createMatteBorder(0, 0, 1, 0, ColorScheme.DARK_GRAY_COLOR), - BorderFactory.createLineBorder(ColorScheme.DARKER_GRAY_COLOR) - )); - - JLabel title = new JLabel(); - title.setText(name); - title.setFont(normalFont); - title.setBorder(null); - title.setBackground(ColorScheme.DARKER_GRAY_COLOR); - title.setPreferredSize(new Dimension(0, 24)); - title.setForeground(Color.WHITE); - title.setBorder(new EmptyBorder(0, 8, 0, 0)); - - JPanel titleActions = new JPanel(new BorderLayout(3, 0)); - titleActions.setBorder(new EmptyBorder(0, 0, 0, 8)); - titleActions.setBackground(ColorScheme.DARKER_GRAY_COLOR); - - titleActions.add(install, BorderLayout.EAST); - - titleWrapper.add(title, BorderLayout.CENTER); - titleWrapper.add(titleActions, BorderLayout.EAST); - - description.setText(desc); - description.setFont(smallFont); - description.setDisabledTextColor(Color.WHITE); - description.setBackground(ColorScheme.DARKER_GRAY_COLOR); - - add(titleWrapper, BorderLayout.NORTH); - add(description, BorderLayout.CENTER); - } - - public static class JMultilineLabel extends JTextArea - { - private static final long serialVersionUID = 1L; - - public JMultilineLabel() - { - super(); - setEditable(false); - setCursor(null); - setOpaque(false); - setFocusable(false); - setWrapStyleWord(true); - setLineWrap(true); - setBorder(new EmptyBorder(0, 8, 0, 8)); - setAlignmentY(JLabel.CENTER_ALIGNMENT); - - DefaultCaret caret = (DefaultCaret) getCaret(); - caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE); - } - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/ExternalPluginManagerPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/ExternalPluginManagerPanel.java deleted file mode 100644 index a91170d9bc..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/ExternalPluginManagerPanel.java +++ /dev/null @@ -1,294 +0,0 @@ -package net.runelite.client.plugins.openosrs.externals; - -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.image.BufferedImage; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.concurrent.ScheduledExecutorService; -import javax.inject.Inject; -import javax.swing.ImageIcon; -import javax.swing.JCheckBox; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTabbedPane; -import javax.swing.JTextField; -import javax.swing.border.EmptyBorder; -import net.runelite.client.eventbus.EventBus; -import net.runelite.client.plugins.ExternalPluginManager; -import net.runelite.client.ui.ClientUI; -import net.runelite.client.ui.ColorScheme; -import net.runelite.client.ui.PluginPanel; -import net.runelite.client.util.ImageUtil; - -public class ExternalPluginManagerPanel extends PluginPanel -{ - private static final ImageIcon ADD_ICON_RAW; - private static final ImageIcon ADD_HOVER_ICON_RAW; - private static final ImageIcon ADD_ICON_GH; - private static final ImageIcon ADD_HOVER_ICON_GH; - - static - { - final BufferedImage addIconRaw = - ImageUtil.getResourceStreamFromClass(ExternalPluginManagerPanel.class, "add_raw_icon.png"); - final BufferedImage addIconGh = ImageUtil - .resizeImage(ImageUtil.getResourceStreamFromClass(ExternalPluginManagerPanel.class, "gh_icon.png"), 14, 14); - ADD_ICON_RAW = new ImageIcon(addIconRaw); - ADD_HOVER_ICON_RAW = new ImageIcon(ImageUtil.alphaOffset(addIconRaw, 0.53f)); - ADD_ICON_GH = new ImageIcon(addIconGh); - ADD_HOVER_ICON_GH = new ImageIcon(ImageUtil.alphaOffset(addIconGh, 0.53f)); - } - - private final ExternalPluginManager externalPluginManager; - private final ScheduledExecutorService executor; - private final EventBus eventBus; - - @Inject - private ExternalPluginManagerPanel(ExternalPluginManager externalPluginManager, ScheduledExecutorService executor, EventBus eventBus) - { - super(false); - - this.externalPluginManager = externalPluginManager; - this.executor = executor; - this.eventBus = eventBus; - - buildPanel(); - } - - private void buildPanel() - { - removeAll(); - - setLayout(new BorderLayout(0, 10)); - setBackground(ColorScheme.DARK_GRAY_COLOR); - - add(titleBar(), BorderLayout.NORTH); - add(tabbedPane(), BorderLayout.CENTER); - - revalidate(); - repaint(); - } - - private JPanel titleBar() - { - JPanel titlePanel = new JPanel(new BorderLayout()); - titlePanel.setBorder(new EmptyBorder(10, 10, 10, 10)); - - JLabel title = new JLabel(); - JLabel addGHRepo = new JLabel(ADD_ICON_GH); - JLabel addRawRepo = new JLabel(ADD_ICON_RAW); - - JPanel buttonHolder = new JPanel(new BorderLayout()); - buttonHolder.setBorder(new EmptyBorder(0, 0, 0, 0)); - - title.setText("External Plugin Manager"); - title.setForeground(Color.WHITE); - - addGHRepo.setToolTipText("Add new GitHub repository"); - addGHRepo.addMouseListener(new MouseAdapter() - { - @Override - public void mousePressed(MouseEvent mouseEvent) - { - if (externalPluginManager.getWarning()) - { - JCheckBox checkbox = new JCheckBox("Don't show again."); - int answer = showWarningDialog(checkbox); - - if (answer == 1) - { - return; - } - - if (checkbox.isSelected()) - { - externalPluginManager.setWarning(false); - } - } - - JTextField owner = new JTextField(); - JTextField name = new JTextField(); - Object[] message = { - "Github Repository owner:", owner, - "Github Repository name:", name - }; - - int option = - JOptionPane.showConfirmDialog(ClientUI.getFrame(), message, "Add repository", JOptionPane.OK_CANCEL_OPTION); - if (option != JOptionPane.OK_OPTION || owner.getText().equals("") || name.getText().equals("")) - { - return; - } - - if (externalPluginManager.doesGhRepoExist(owner.getText(), name.getText())) - { - JOptionPane.showMessageDialog(ClientUI.getFrame(), "This repository already exists.", "Error!", - JOptionPane.ERROR_MESSAGE); - return; - } - - if (ExternalPluginManager.testGHRepository(owner.getText(), name.getText())) - { - JOptionPane.showMessageDialog(ClientUI.getFrame(), "This doesn't appear to be a valid repository.", "Error!", - JOptionPane.ERROR_MESSAGE); - return; - } - - externalPluginManager.addGHRepository(owner.getText(), name.getText()); - } - - @Override - public void mouseEntered(MouseEvent mouseEvent) - { - addGHRepo.setIcon(ADD_HOVER_ICON_GH); - } - - @Override - public void mouseExited(MouseEvent mouseEvent) - { - addGHRepo.setIcon(ADD_ICON_GH); - } - }); - addGHRepo.setBorder(new EmptyBorder(0, 3, 0, 0)); - - addRawRepo.setToolTipText("Add new raw repository"); - addRawRepo.addMouseListener(new MouseAdapter() - { - @Override - public void mousePressed(MouseEvent mouseEvent) - { - if (externalPluginManager.getWarning()) - { - JCheckBox checkbox = new JCheckBox("Don't show again."); - int answer = showWarningDialog(checkbox); - - if (answer == 1) - { - return; - } - - if (checkbox.isSelected()) - { - externalPluginManager.setWarning(false); - } - } - - JTextField id = new JTextField(); - JTextField url = new JTextField(); - Object[] message = { - "Repository ID:", id, - "Repository URL:", url - }; - - int option = - JOptionPane.showConfirmDialog(ClientUI.getFrame(), message, "Add repository", JOptionPane.OK_CANCEL_OPTION); - if (option != JOptionPane.OK_OPTION || id.getText().equals("") || url.getText().equals("")) - { - return; - } - - if (id.getText().startsWith("gh:") || id.getText().contains("|")) - { - JOptionPane.showMessageDialog(ClientUI.getFrame(), - "Repository id cannot begin with \"gh:\"\nor contain the pipe character '|'.", "Error!", - JOptionPane.ERROR_MESSAGE); - return; - } - - if (externalPluginManager.doesRepoExist(id.getText())) - { - JOptionPane.showMessageDialog(ClientUI.getFrame(), - String.format("The repository with id %s already exists.", id.getText()), "Error!", - JOptionPane.ERROR_MESSAGE); - return; - } - - URL urlActual; - try - { - urlActual = new URL(url.getText()); - } - catch (MalformedURLException e) - { - JOptionPane.showMessageDialog(ClientUI.getFrame(), "This doesn't appear to be a valid repository.", "Error!", - JOptionPane.ERROR_MESSAGE); - return; - } - - if (ExternalPluginManager.testRepository(urlActual)) - { - JOptionPane.showMessageDialog(ClientUI.getFrame(), "This doesn't appear to be a valid repository.", "Error!", - JOptionPane.ERROR_MESSAGE); - return; - } - - externalPluginManager.addRepository(id.getText(), urlActual); - } - - @Override - public void mouseEntered(MouseEvent mouseEvent) - { - addRawRepo.setIcon(ADD_HOVER_ICON_RAW); - } - - @Override - public void mouseExited(MouseEvent mouseEvent) - { - addRawRepo.setIcon(ADD_ICON_RAW); - } - }); - addRawRepo.setBorder(new EmptyBorder(0, 0, 0, 3)); - - titlePanel.add(title, BorderLayout.WEST); - buttonHolder.add(addRawRepo, BorderLayout.WEST); - buttonHolder.add(addGHRepo, BorderLayout.EAST); - titlePanel.add(buttonHolder, BorderLayout.EAST); - - return titlePanel; - } - - private JTabbedPane tabbedPane() - { - JTabbedPane mainTabPane = new JTabbedPane(); - - PluginsPanel pluginPanel = new PluginsPanel(this.externalPluginManager, this.executor, this.eventBus); - JScrollPane repositoryPanel = wrapContainer(new RepositoryPanel(this.externalPluginManager, this.eventBus)); - - mainTabPane.add("Plugins", pluginPanel); - mainTabPane.add("Repositories", repositoryPanel); - - return mainTabPane; - } - - private int showWarningDialog(JCheckBox checkbox) - { - Object[] options = {"Okay, I accept the risk", "Never mind, turn back", checkbox}; - return JOptionPane.showOptionDialog(new JFrame(), - "Adding plugins from unverified sources may put your account, or personal information at risk! \n", - "Account security warning", - JOptionPane.YES_NO_OPTION, - JOptionPane.WARNING_MESSAGE, - null, - options, - options[0]); - } - - static JScrollPane wrapContainer(final JPanel container) - { - final JPanel wrapped = new JPanel(new BorderLayout()); - wrapped.add(container, BorderLayout.NORTH); - - final JScrollPane scroller = new JScrollPane(wrapped); - scroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); - scroller.getVerticalScrollBar().setPreferredSize(new Dimension(8, 0)); - - return scroller; - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/PluginsPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/PluginsPanel.java deleted file mode 100644 index 325b1730eb..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/PluginsPanel.java +++ /dev/null @@ -1,587 +0,0 @@ -package net.runelite.client.plugins.openosrs.externals; - -import com.google.gson.JsonSyntaxException; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.image.BufferedImage; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ScheduledExecutorService; -import javax.swing.ImageIcon; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JTabbedPane; -import javax.swing.SwingConstants; -import javax.swing.SwingWorker; -import javax.swing.border.EmptyBorder; -import lombok.extern.slf4j.Slf4j; -import static net.runelite.api.util.Text.DISTANCE; -import net.runelite.client.eventbus.EventBus; -import net.runelite.client.events.ExternalPluginChanged; -import net.runelite.client.events.ExternalRepositoryChanged; -import net.runelite.client.plugins.ExternalPluginManager; -import static net.runelite.client.plugins.openosrs.externals.ExternalPluginManagerPanel.wrapContainer; -import net.runelite.client.ui.ClientUI; -import net.runelite.client.ui.ColorScheme; -import net.runelite.client.ui.FontManager; -import net.runelite.client.ui.PluginPanel; -import net.runelite.client.ui.components.IconTextField; -import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; -import net.runelite.client.util.DeferredDocumentChangedListener; -import net.runelite.client.util.ImageUtil; -import net.runelite.client.util.SwingUtil; -import org.pf4j.update.PluginInfo; -import org.pf4j.update.UpdateManager; -import org.pf4j.update.UpdateRepository; -import org.pf4j.update.VerifyException; - -@Slf4j -public class PluginsPanel extends JPanel -{ - private static final ImageIcon ADD_ICON; - private static final ImageIcon ADD_HOVER_ICON; - private static final ImageIcon DELETE_ICON; - private static final ImageIcon DELETE_HOVER_ICON; - private static final ImageIcon DELETE_ICON_GRAY; - private static final ImageIcon DELETE_HOVER_ICON_GRAY; - - static - { - final BufferedImage addIcon = - ImageUtil.recolorImage( - ImageUtil.getResourceStreamFromClass(PluginsPanel.class, "add_icon.png"), ColorScheme.BRAND_BLUE - ); - ADD_ICON = new ImageIcon(addIcon); - ADD_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(addIcon, 0.53f)); - - final BufferedImage deleteImg = - ImageUtil.recolorImage( - ImageUtil.resizeCanvas( - ImageUtil.getResourceStreamFromClass(PluginsPanel.class, "delete_icon.png"), 14, 14 - ), ColorScheme.BRAND_BLUE - ); - DELETE_ICON = new ImageIcon(deleteImg); - DELETE_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(deleteImg, 0.53f)); - - DELETE_ICON_GRAY = new ImageIcon(ImageUtil.grayscaleImage(deleteImg)); - DELETE_HOVER_ICON_GRAY = new ImageIcon(ImageUtil.alphaOffset(ImageUtil.grayscaleImage(deleteImg), 0.53f)); - } - - private final ExternalPluginManager externalPluginManager; - private final UpdateManager updateManager; - private final ScheduledExecutorService executor; - private final EventBus eventBus; - - private final IconTextField searchBar = new IconTextField(); - private final JPanel filterwrapper = new JPanel(new BorderLayout(0, 10)); - private final List installedPluginsList = new ArrayList<>(); - private final List availablePluginsList = new ArrayList<>(); - private final JPanel installedPluginsPanel = new JPanel(new GridBagLayout()); - private final JPanel availablePluginsPanel = new JPanel(new GridBagLayout()); - - private JComboBox filterComboBox; - private Set deps; - - PluginsPanel(ExternalPluginManager externalPluginManager, ScheduledExecutorService executor, EventBus eventBus) - { - this.externalPluginManager = externalPluginManager; - this.updateManager = externalPluginManager.getUpdateManager(); - this.executor = executor; - this.eventBus = eventBus; - - setLayout(new BorderLayout(0, 10)); - setBackground(ColorScheme.DARK_GRAY_COLOR); - - buildFilter(); - - JTabbedPane mainTabPane = new JTabbedPane(); - - mainTabPane.add("Installed", wrapContainer(installedPluginsPanel())); - mainTabPane.add("Available", wrapContainer(availablePluginsPanel())); - - add(filterwrapper, BorderLayout.NORTH); - add(mainTabPane, BorderLayout.CENTER); - - eventBus.subscribe(ExternalPluginChanged.class, this, this::onExternalPluginChanged); - eventBus.subscribe(ExternalRepositoryChanged.class, this, (e) -> { - buildFilter(); - reloadPlugins(); - repaint(); - }); - - reloadPlugins(); - } - - private void buildFilter() - { - filterwrapper.removeAll(); - - DeferredDocumentChangedListener listener = new DeferredDocumentChangedListener(); - listener.addChangeListener(e -> - { - installedPlugins(); - availablePlugins(); - }); - - filterwrapper.setBorder(new EmptyBorder(10, 10, 0, 10)); - - List repositories = getRepositories(); - filterComboBox = new JComboBox<>(repositories.toArray(new String[0])); - filterComboBox.setPreferredSize(new Dimension(PluginPanel.PANEL_WIDTH - 20, 30)); - filterComboBox.addActionListener(e -> { - installedPlugins(); - availablePlugins(); - }); - - if (repositories.size() > 2) - { - filterwrapper.add(filterComboBox, BorderLayout.NORTH); - } - - searchBar.setIcon(IconTextField.Icon.SEARCH); - searchBar.setPreferredSize(new Dimension(PluginPanel.PANEL_WIDTH - 20, 30)); - searchBar.setBackground(ColorScheme.DARKER_GRAY_COLOR); - searchBar.setHoverBackgroundColor(ColorScheme.DARK_GRAY_HOVER_COLOR); - searchBar.getDocument().addDocumentListener(listener); - - filterwrapper.add(searchBar, BorderLayout.CENTER); - } - - private List getRepositories() - { - List repositories = new ArrayList<>(); - repositories.add("All"); - for (UpdateRepository updateRepository : this.updateManager.getRepositories()) - { - repositories.add(updateRepository.getUrl().toString().replace("https://raw.githubusercontent.com/", "").replace("/master/", "")); - } - - return repositories; - } - - private JLabel titleLabel(String text) - { - JLabel title = new JShadowedLabel(); - - title.setFont(FontManager.getRunescapeSmallFont()); - title.setForeground(Color.WHITE); - title.setHorizontalAlignment(SwingConstants.CENTER); - title.setText("" + text + ""); - - return title; - } - - private JPanel installedPluginsPanel() - { - JPanel installedPluginsContainer = new JPanel(); - installedPluginsContainer.setLayout(new BorderLayout(0, 5)); - installedPluginsContainer.setBorder(new EmptyBorder(0, 10, 10, 10)); - installedPluginsContainer.add(installedPluginsPanel, BorderLayout.CENTER); - - return installedPluginsContainer; - } - - private JPanel availablePluginsPanel() - { - JPanel availablePluginsContainer = new JPanel(); - availablePluginsContainer.setLayout(new BorderLayout(0, 5)); - availablePluginsContainer.setBorder(new EmptyBorder(0, 10, 10, 10)); - availablePluginsContainer.add(availablePluginsPanel, BorderLayout.CENTER); - - return availablePluginsContainer; - } - - static boolean mismatchesSearchTerms(String search, PluginInfo pluginInfo) - { - final String[] searchTerms = search.toLowerCase().split(" "); - final String[] pluginTerms = (pluginInfo.name + " " + pluginInfo.description).toLowerCase().split("[/\\s]"); - for (String term : searchTerms) - { - if (Arrays.stream(pluginTerms).noneMatch((t) -> t.contains(term) || - DISTANCE.apply(t, term) > 0.9)) - { - return true; - } - } - return false; - } - - private void reloadPlugins() - { - fetchPlugins(); - - try - { - SwingUtil.syncExec(() -> { - this.installedPlugins(); - this.availablePlugins(); - }); - - } - catch (InvocationTargetException | InterruptedException e) - { - e.printStackTrace(); - } - } - - private void fetchPlugins() - { - List availablePlugins = null; - List plugins = null; - List disabledPlugins = externalPluginManager.getDisabledPlugins(); - - try - { - availablePlugins = updateManager.getAvailablePlugins(); - plugins = updateManager.getPlugins(); - } - catch (JsonSyntaxException ex) - { - log.error(String.valueOf(ex)); - } - - if (availablePlugins == null || plugins == null) - { - JOptionPane.showMessageDialog(ClientUI.getFrame(), "The external plugin list could not be loaded.", "Error", JOptionPane.ERROR_MESSAGE); - return; - } - - availablePluginsList.clear(); - installedPluginsList.clear(); - - deps = externalPluginManager.getDependencies(); - - for (PluginInfo pluginInfo : plugins) - { - if (availablePlugins.contains(pluginInfo) || disabledPlugins.contains(pluginInfo.id)) - { - availablePluginsList.add(pluginInfo); - } - else - { - installedPluginsList.add(pluginInfo); - } - } - } - - private void onExternalPluginChanged(ExternalPluginChanged externalPluginChanged) - { - String pluginId = externalPluginChanged.getPluginId(); - Optional externalBox; - - if (externalPluginChanged.isAdded()) - { - externalBox = Arrays.stream( - availablePluginsPanel.getComponents() - ).filter(extBox -> - extBox instanceof ExternalBox && ((ExternalBox) extBox).pluginInfo.id.equals(pluginId) - ).findFirst(); - } - else - { - externalBox = Arrays.stream( - installedPluginsPanel.getComponents() - ).filter(extBox -> - extBox instanceof ExternalBox && ((ExternalBox) extBox).pluginInfo.id.equals(pluginId) - ).findFirst(); - } - - if (externalBox.isEmpty()) - { - log.info("EXTERNALBOX IS EMPTY: {}", pluginId); - return; - } - - ExternalBox extBox = (ExternalBox) externalBox.get(); - deps = externalPluginManager.getDependencies(); - - try - { - SwingUtil.syncExec(() -> - { - if (externalPluginChanged.isAdded()) - { - availablePluginsPanel.remove(externalBox.get()); - availablePluginsList.remove(extBox.pluginInfo); - - installedPluginsList.add(extBox.pluginInfo); - installedPluginsList.sort(Comparator.naturalOrder()); - - installedPlugins(); - - pluginInstallButton(extBox.install, extBox.pluginInfo, true, deps.contains(extBox.pluginInfo.id)); - } - else - { - installedPluginsPanel.remove(externalBox.get()); - installedPluginsList.remove(extBox.pluginInfo); - - availablePluginsList.add(extBox.pluginInfo); - availablePluginsList.sort(Comparator.naturalOrder()); - - availablePlugins(); - - pluginInstallButton(extBox.install, extBox.pluginInfo, false, false); - } - }); - } - catch (InvocationTargetException | InterruptedException e) - { - e.printStackTrace(); - } - } - - private void installedPlugins() - { - GridBagConstraints c = new GridBagConstraints(); - - installedPluginsPanel.removeAll(); - String search = searchBar.getText(); - - for (PluginInfo pluginInfo : installedPluginsList) - { - if (!search.equals("") && mismatchesSearchTerms(search, pluginInfo)) - { - continue; - } - - if (filterComboBox.getSelectedIndex() != 0) - { - boolean filtered = true; - String filter = String.valueOf(filterComboBox.getSelectedItem()); - for (UpdateRepository updateRepository : updateManager.getRepositories()) - { - if (filter.equals(updateRepository.getUrl().toString().replace("https://raw.githubusercontent.com/", "").replace("/master/", "")) && - pluginInfo.getRepositoryId().equals(updateRepository.getId())) - { - filtered = false; - } - } - - if (filtered) - { - continue; - } - } - - ExternalBox pluginBox = new ExternalBox(pluginInfo); - pluginBox.pluginInfo = pluginInfo; - - c.fill = GridBagConstraints.HORIZONTAL; - c.weightx = 1.0; - c.gridy += 1; - c.insets = new Insets(5, 0, 0, 0); - - pluginInstallButton(pluginBox.install, pluginInfo, true, deps.contains(pluginInfo.id)); - installedPluginsPanel.add(pluginBox, c); - } - - if (installedPluginsPanel.getComponents().length < 1) - { - installedPluginsPanel.add(titleLabel("No plugins found")); - } - } - - private void availablePlugins() - { - GridBagConstraints c = new GridBagConstraints(); - - availablePluginsPanel.removeAll(); - String search = searchBar.getText(); - - for (PluginInfo pluginInfo : availablePluginsList) - { - if (!search.equals("") && mismatchesSearchTerms(search, pluginInfo)) - { - continue; - } - - if (filterComboBox.getSelectedIndex() != 0) - { - boolean filtered = true; - String filter = String.valueOf(filterComboBox.getSelectedItem()); - for (UpdateRepository updateRepository : updateManager.getRepositories()) - { - if (filter.equals(updateRepository.getUrl().toString().replace("https://raw.githubusercontent.com/", "").replace("/master/", "")) && - pluginInfo.getRepositoryId().equals(updateRepository.getId())) - { - filtered = false; - } - } - - if (filtered) - { - continue; - } - } - - ExternalBox pluginBox = new ExternalBox(pluginInfo); - pluginBox.pluginInfo = pluginInfo; - - c.fill = GridBagConstraints.HORIZONTAL; - c.weightx = 1.0; - c.gridy += 1; - c.insets = new Insets(5, 0, 0, 0); - - pluginInstallButton(pluginBox.install, pluginInfo, false, false); - availablePluginsPanel.add(pluginBox, c); - } - - if (availablePluginsPanel.getComponents().length < 1) - { - availablePluginsPanel.add(titleLabel("No plugins found")); - } - } - - - private void pluginInstallButton(JLabel install, PluginInfo pluginInfo, boolean installed, boolean hideAction) - { - install.setIcon(installed ? hideAction ? DELETE_ICON_GRAY : DELETE_ICON : ADD_ICON); - install.setText(""); - - if (!hideAction) - { - install.setToolTipText(installed ? "Uninstall" : "Install"); - } - install.addMouseListener(new MouseAdapter() - { - @Override - public void mousePressed(MouseEvent e) - { - if (installed) - { - if (hideAction) - { - JOptionPane.showMessageDialog(ClientUI.getFrame(), "This plugin can't be uninstalled because one or more other plugins have a dependency on it.", "Error!", JOptionPane.ERROR_MESSAGE); - } - else - { - install.setIcon(null); - install.setText("Uninstalling"); - - SwingWorker worker = new SwingWorker<>() - { - @Override - protected Boolean doInBackground() - { - return externalPluginManager.uninstall(pluginInfo.id); - } - - @Override - protected void done() - { - - boolean status = false; - try - { - status = get(); - } - catch (InterruptedException | ExecutionException e) - { - } - - if (!status) - { - pluginInstallButton(install, pluginInfo, installed, hideAction); - } - } - }; - worker.execute(); - } - } - else - { - install.setIcon(null); - install.setText("Installing"); - - SwingWorker worker = new SwingWorker<>() - { - @Override - protected Boolean doInBackground() - { - return installPlugin(pluginInfo); - } - - @Override - protected void done() - { - - boolean status = false; - try - { - status = get(); - } - catch (InterruptedException | ExecutionException e) - { - } - - if (!status) - { - pluginInstallButton(install, pluginInfo, installed, hideAction); - } - } - }; - worker.execute(); - } - } - - @Override - public void mouseEntered(MouseEvent e) - { - if (install.getText().toLowerCase().contains("installing")) - { - return; - } - - install.setIcon(installed ? hideAction ? DELETE_HOVER_ICON_GRAY : DELETE_HOVER_ICON : ADD_HOVER_ICON); - } - - @Override - public void mouseExited(MouseEvent e) - { - if (install.getText().toLowerCase().contains("installing")) - { - return; - } - - install.setIcon(installed ? hideAction ? DELETE_ICON_GRAY : DELETE_ICON : ADD_ICON); - } - }); - } - - private boolean installPlugin(PluginInfo pluginInfo) - { - try - { - return externalPluginManager.install(pluginInfo.id); - } - catch (VerifyException ex) - { - try - { - SwingUtil.syncExec(() -> - JOptionPane.showMessageDialog(ClientUI.getFrame(), pluginInfo.name + " could not be installed, the hash could not be verified.", "Error!", JOptionPane.ERROR_MESSAGE)); - } - catch (InvocationTargetException | InterruptedException ignored) - { - } - } - - return false; - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/RepositoryBox.java b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/RepositoryBox.java deleted file mode 100644 index 215bec1d55..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/RepositoryBox.java +++ /dev/null @@ -1,179 +0,0 @@ -package net.runelite.client.plugins.openosrs.externals; - -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.image.BufferedImage; -import java.util.Optional; -import javax.swing.BorderFactory; -import javax.swing.ImageIcon; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.border.CompoundBorder; -import javax.swing.border.EmptyBorder; -import net.runelite.client.plugins.ExternalPluginManager; -import net.runelite.client.ui.ColorScheme; -import net.runelite.client.ui.FontManager; -import net.runelite.client.ui.JMultilineLabel; -import net.runelite.client.util.ImageUtil; -import net.runelite.client.util.LinkBrowser; -import org.pf4j.update.PluginInfo; -import org.pf4j.update.UpdateRepository; - -public class RepositoryBox extends JPanel -{ - private static final Font normalFont = FontManager.getRunescapeFont(); - private static final Font smallFont = FontManager.getRunescapeSmallFont(); - private static final ImageIcon DELETE_ICON; - private static final ImageIcon DELETE_HOVER_ICON; - private static final ImageIcon DISCORD_ICON; - private static final ImageIcon DISCORD_HOVER_ICON; - - static - { - final BufferedImage deleteImg = - ImageUtil.recolorImage( - ImageUtil.resizeCanvas( - ImageUtil.getResourceStreamFromClass(ExternalPluginManagerPanel.class, "delete_icon.png"), 14, 14 - ), ColorScheme.BRAND_BLUE - ); - DELETE_ICON = new ImageIcon(deleteImg); - DELETE_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(deleteImg, 0.53f)); - - final BufferedImage discordImg = - ImageUtil.recolorImage( - ImageUtil.resizeCanvas( - ImageUtil.getResourceStreamFromClass(ExternalPluginManagerPanel.class, "discord_icon.png"), 14, 14 - ), Color.WHITE - ); - DISCORD_ICON = new ImageIcon(discordImg); - DISCORD_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(discordImg, 0.53f)); - } - - RepositoryBox(ExternalPluginManager externalPluginManager, UpdateRepository updateRepository) - { - setLayout(new BorderLayout()); - setBackground(ColorScheme.DARKER_GRAY_COLOR); - - String name = updateRepository.getId(); - String urlString = updateRepository.getUrl().toString(); - if (urlString.startsWith("/")) - { - urlString = urlString.substring(1); - } - - JPanel titleWrapper = new JPanel(new BorderLayout()); - titleWrapper.setBackground(ColorScheme.DARKER_GRAY_COLOR); - titleWrapper.setBorder(new CompoundBorder( - BorderFactory.createMatteBorder(0, 0, 1, 0, ColorScheme.DARK_GRAY_COLOR), - BorderFactory.createLineBorder(ColorScheme.DARKER_GRAY_COLOR) - )); - - JLabel title = new JLabel(); - title.setText(name); - title.setFont(normalFont); - title.setBorder(null); - title.setBackground(ColorScheme.DARKER_GRAY_COLOR); - title.setPreferredSize(new Dimension(0, 24)); - title.setForeground(Color.WHITE); - title.setBorder(new EmptyBorder(0, 8, 0, 0)); - - JPanel titleActions = new JPanel(new BorderLayout(3, 0)); - titleActions.setBorder(new EmptyBorder(0, 0, 0, 8)); - titleActions.setBackground(ColorScheme.DARKER_GRAY_COLOR); - - Optional firstPlugin = updateRepository.getPlugins().values().stream().findFirst(); - - if (firstPlugin.isPresent() && !firstPlugin.get().projectUrl.equals("")) - { - JLabel support = new JLabel(); - support.setIcon(DISCORD_ICON); - support.setToolTipText("Support"); - support.addMouseListener(new MouseAdapter() - { - @Override - public void mousePressed(MouseEvent e) - { - LinkBrowser.browse(firstPlugin.get().projectUrl); - } - - @Override - public void mouseEntered(MouseEvent e) - { - support.setIcon(DISCORD_HOVER_ICON); - } - - @Override - public void mouseExited(MouseEvent e) - { - support.setIcon(DISCORD_ICON); - } - }); - - titleActions.add(support, BorderLayout.WEST); - } - - if (!name.equals("OpenOSRS") && !name.equals("Plugin-Hub")) - { - JLabel install = new JLabel(); - install.setIcon(DELETE_ICON); - install.setToolTipText("Remove"); - install.addMouseListener(new MouseAdapter() - { - @Override - public void mousePressed(MouseEvent e) - { - externalPluginManager.removeRepository(updateRepository.getId()); - } - - @Override - public void mouseEntered(MouseEvent e) - { - install.setIcon(DELETE_HOVER_ICON); - } - - @Override - public void mouseExited(MouseEvent e) - { - install.setIcon(DELETE_ICON); - } - }); - - titleActions.add(install, BorderLayout.EAST); - } - - titleWrapper.add(title, BorderLayout.CENTER); - titleWrapper.add(titleActions, BorderLayout.EAST); - - JMultilineLabel repository = new JMultilineLabel(); - repository.setText(formatURL(urlString)); - repository.setFont(smallFont); - repository.setDisabledTextColor(Color.WHITE); - - String finalUrlString = urlString; - repository.addMouseListener(new MouseAdapter() - { - @Override - public void mouseClicked(MouseEvent e) - { - LinkBrowser.browse(formatURL(finalUrlString)); - } - }); - - add(titleWrapper, BorderLayout.NORTH); - add(repository, BorderLayout.CENTER); - } - - private String formatURL(String url) - { - if (url.contains("githubusercontent")) - { - url = url.replace("raw.githubusercontent", "github").replace("/master/", ""); - } - - return url; - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/RepositoryPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/RepositoryPanel.java deleted file mode 100644 index 4b541ccd04..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/RepositoryPanel.java +++ /dev/null @@ -1,49 +0,0 @@ -package net.runelite.client.plugins.openosrs.externals; - -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import javax.swing.JPanel; -import javax.swing.border.EmptyBorder; -import net.runelite.client.eventbus.EventBus; -import net.runelite.client.events.ExternalRepositoryChanged; -import net.runelite.client.plugins.ExternalPluginManager; -import net.runelite.client.ui.ColorScheme; -import org.pf4j.update.UpdateRepository; - -public class RepositoryPanel extends JPanel -{ - private final ExternalPluginManager externalPluginManager; - - private final GridBagConstraints c = new GridBagConstraints(); - - RepositoryPanel(ExternalPluginManager externalPluginManager, EventBus eventBus) - { - this.externalPluginManager = externalPluginManager; - - setLayout(new GridBagLayout()); - setBackground(ColorScheme.DARK_GRAY_COLOR); - setBorder(new EmptyBorder(0, 10, 0, 10)); - - createPanel(); - - eventBus.subscribe(ExternalRepositoryChanged.class, this, (e) -> createPanel()); - } - - private void createPanel() - { - removeAll(); - - c.fill = GridBagConstraints.HORIZONTAL; - c.weightx = 1; - c.gridy = 0; - c.insets = new Insets(5, 0, 0, 0); - - for (UpdateRepository repository : externalPluginManager.getRepositories()) - { - final RepositoryBox p = new RepositoryBox(externalPluginManager, repository); - add(p, c); - c.gridy++; - } - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xtea/XteaPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/xtea/XteaPlugin.java deleted file mode 100644 index a0e3086b72..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xtea/XteaPlugin.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) 2017, Adam - * Copyright (c) 2020, ThatGamerBlue - * 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.plugins.xtea; - -import java.io.IOException; -import java.util.HashMap; -import com.google.inject.Provides; -import javax.inject.Inject; -import lombok.extern.slf4j.Slf4j; -import net.runelite.api.Client; -import net.runelite.api.GameState; -import net.runelite.api.events.GameStateChanged; -import net.runelite.client.eventbus.Subscribe; -import net.runelite.client.plugins.Plugin; -import net.runelite.client.plugins.PluginDescriptor; -import net.runelite.http.api.xtea.XteaClient; -import org.apache.commons.lang3.ArrayUtils; -import okhttp3.OkHttpClient; - -@PluginDescriptor( - name = "Xtea", - hidden = true -) -@Slf4j -public class XteaPlugin extends Plugin -{ - @Inject - private XteaClient xteaClient; - - @Inject - private Client client; - - private HashMap xteas; - - @Override - public void startUp() - { - try - { - xteas = xteaClient.get(); - } - catch (IOException e) - { - e.printStackTrace(); - } - } - - @Provides - XteaClient provideXteaClient(OkHttpClient okHttpClient) - { - return new XteaClient(okHttpClient); - } - - @Subscribe - private void onGameStateChanged(GameStateChanged gameStateChanged) - { - if (gameStateChanged.getGameState() != GameState.LOGGED_IN) - { - return; - } - - int[] regions = client.getMapRegions(); - int[][] xteaKeys = client.getXteaKeys(); - - for (int idx = 0; idx < regions.length; ++idx) - { - int region = regions[idx]; - int[] keys = xteaKeys[idx]; - - if (xteas.get(region) != null && areKeysEqual(xteas.get(region), keys)) - { - continue; - } - - xteas.put(region, ArrayUtils.toObject(keys)); - - log.info("Submitting region {} keys {}, {}, {}, {}", region, keys[0], keys[1], keys[2], keys[3]); - - //Don't post non encrypted regions - if (keys[0] == 0 && keys[1] == 0 && keys[2] == 0 && keys[3] == 0) - { - continue; - } - - xteaClient.submit(region, keys); - } - } - - private boolean areKeysEqual(Integer[] existingKeys, int[] newKeys) - { - return existingKeys[0] == newKeys[0] && - existingKeys[1] == newKeys[1] && - existingKeys[2] == newKeys[2] && - existingKeys[3] == newKeys[3]; - } -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/rs/ClientConfigLoader.java b/runelite-client/src/main/java/net/runelite/client/rs/ClientConfigLoader.java index 1803e2a633..ba9d3ebe2e 100644 --- a/runelite-client/src/main/java/net/runelite/client/rs/ClientConfigLoader.java +++ b/runelite-client/src/main/java/net/runelite/client/rs/ClientConfigLoader.java @@ -89,4 +89,4 @@ class ClientConfigLoader return config; } -} \ No newline at end of file +} 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 2412d8254f..6128c4474b 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 @@ -1,7 +1,7 @@ /* * Copyright (c) 2016-2017, Adam * Copyright (c) 2018, Tomas Slusny - * Copyright (c) 2018 Abex + * Copyright (c) 2019 Abex * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,102 +26,78 @@ */ package net.runelite.client.rs; +import com.google.archivepatcher.applier.FileByFileV1DeltaApplier; import com.google.common.base.Strings; +import com.google.common.hash.Hashing; +import com.google.common.hash.HashingOutputStream; import com.google.common.io.ByteStreams; +import com.google.common.io.Files; import java.applet.Applet; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.net.URL; -import java.net.URLClassLoader; +import java.io.OutputStream; +import java.nio.channels.Channels; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.nio.file.StandardOpenOption; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.util.Arrays; +import java.util.Collection; +import java.util.Enumeration; import java.util.Map; import java.util.function.Supplier; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarInputStream; +import javax.annotation.Nonnull; +import javax.swing.SwingUtilities; import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; import net.runelite.client.RuneLite; -import net.runelite.client.ui.RuneLiteSplashScreen; +import net.runelite.client.RuneLiteProperties; +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.client.util.CountingInputStream; +import net.runelite.client.util.VerificationException; import net.runelite.http.api.worlds.World; import okhttp3.HttpUrl; import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; @Slf4j +@SuppressWarnings("deprecation") public class ClientLoader implements Supplier { - private static final String CONFIG_URL = "https://oldschool.runescape.com/jav_config.ws"; - private static final String BACKUP_CONFIG_URL = "https://raw.githubusercontent.com/open-osrs/hosting/master/jav_config.ws"; - private static final int NUM_ATTEMPTS = 10; + private static final int NUM_ATTEMPTS = 6; + private static File LOCK_FILE = new File(RuneLite.CACHE_DIR, "cache.lock"); + private static File VANILLA_CACHE = new File(RuneLite.CACHE_DIR, "vanilla.cache"); + private static File PATCHED_CACHE = new File(RuneLite.CACHE_DIR, "patched.cache"); + private final OkHttpClient okHttpClient; private final ClientConfigLoader clientConfigLoader; - private final ClientUpdateCheckMode updateCheckMode; + private ClientUpdateCheckMode updateCheckMode; private final WorldSupplier worldSupplier; private Object client; - private RSConfig config; public ClientLoader(OkHttpClient okHttpClient, ClientUpdateCheckMode updateCheckMode) { + this.okHttpClient = okHttpClient; this.clientConfigLoader = new ClientConfigLoader(okHttpClient); this.updateCheckMode = updateCheckMode; this.worldSupplier = new WorldSupplier(okHttpClient); } - private static Applet loadRLPlus(final RSConfig config) - throws ClassNotFoundException, InstantiationException, IllegalAccessException - { - RuneLiteSplashScreen.stage(.465, "Starting Open Old School RuneScape"); - - ClassLoader rsClassLoader = new ClassLoader(ClientLoader.class.getClassLoader()) - { - @Override - protected Class findClass(String name) throws ClassNotFoundException - { - String path = name.replace('.', '/').concat(".class"); - InputStream inputStream = ClientLoader.class.getResourceAsStream(path); - if (inputStream == null) - { - throw new ClassNotFoundException(name + " " + path); - } - byte[] data; - try - { - data = ByteStreams.toByteArray(inputStream); - } - catch (IOException e) - { - e.printStackTrace(); - RuneLiteSplashScreen.setError("Failed to load!", "Failed to load class: " + name + " " + path); - throw new RuntimeException("Failed to load class: " + name + " " + path); - } - return defineClass(name, data, 0, data.length); - } - }; - Class clientClass = rsClassLoader.loadClass("client"); - return loadFromClass(config, clientClass); - } - - private static Applet loadVanilla(final RSConfig config) - throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException - { - RuneLiteSplashScreen.stage(.465, "Starting Vanilla Old School RuneScape"); - - final String codebase = config.getCodeBase(); - final String initialJar = config.getInitialJar(); - final String initialClass = config.getInitialClass(); - final URL url = new URL(codebase + initialJar); - - // Must set parent classloader to null, or it will pull from - // this class's classloader first - final URLClassLoader classloader = new URLClassLoader(new URL[]{url}, null); - final Class clientClass = classloader.loadClass(initialClass); - return loadFromClass(config, clientClass); - } - - private static Applet loadFromClass(final RSConfig config, final Class clientClass) - throws IllegalAccessException, InstantiationException - { - final Applet rs = (Applet) clientClass.newInstance(); - rs.setStub(new RSAppletStub(config)); - return rs; - } - @Override public synchronized Applet get() { @@ -139,57 +115,91 @@ public class ClientLoader implements Supplier private Object doLoad() { + if (updateCheckMode == NONE) + { + return null; + } + try { - downloadConfig(); + SplashScreen.stage(0, null, "Fetching applet viewer config"); + RSConfig config = downloadConfig(); - switch (updateCheckMode) + SplashScreen.stage(.05, null, "Waiting for other clients to start"); + + LOCK_FILE.getParentFile().mkdirs(); + ClassLoader classLoader; + try (FileChannel lockfile = FileChannel.open(LOCK_FILE.toPath(), + StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE); + FileLock flock = lockfile.lock()) { - case AUTO: - default: - return loadRLPlus(config); - case VANILLA: - return loadVanilla(config); - case NONE: - return null; - case RSPS: - RuneLite.allowPrivateServer = true; - return loadRLPlus(config); - } - } - catch (IOException | InstantiationException | IllegalAccessException e) - { - log.error("Error loading RS!", e); - return null; - } - catch (ClassNotFoundException e) - { - RuneLiteSplashScreen.setError("Unable to load client", "Class not found. This means you" - + " are not running OpenOSRS with Gradle as the injected client" - + " is not in your classpath."); + SplashScreen.stage(.05, null, "Downloading Old School RuneScape"); + try + { + updateVanilla(config); + } + catch (IOException ex) + { + // try again with the fallback config and gamepack + if (!config.isFallback()) + { + log.warn("Unable to download game client, attempting to use fallback config", ex); + config = downloadFallbackConfig(); + updateVanilla(config); + } + else + { + throw ex; + } + } + if (updateCheckMode == AUTO) + { + SplashScreen.stage(.35, null, "Patching"); + applyPatch(); + } + + SplashScreen.stage(.40, null, "Loading client"); + File jarFile = updateCheckMode == AUTO ? PATCHED_CACHE : VANILLA_CACHE; + // create the classloader for the jar while we hold the lock, and eagerly load and link all classes + // in the jar. Otherwise the jar can change on disk and can break future classloads. + classLoader = createJarClassLoader(jarFile); + } + + SplashScreen.stage(.465, "Starting", "Starting Old School RuneScape"); + + Applet rs = loadClient(config, classLoader); + + SplashScreen.stage(.5, null, "Starting core classes"); + + return rs; + } + catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException + | VerificationException | SecurityException e) + { log.error("Error loading RS!", e); - return null; + + SwingUtilities.invokeLater(() -> FatalErrorDialog.showNetErrorWindow("loading the client", e)); + return e; } } - private void downloadConfig() throws IOException + private RSConfig downloadConfig() throws IOException { - HttpUrl url = HttpUrl.parse(CONFIG_URL); + HttpUrl url = HttpUrl.parse(RuneLiteProperties.getJavConfig()); IOException err = null; for (int attempt = 0; attempt < NUM_ATTEMPTS; attempt++) { - RuneLiteSplashScreen.stage(.0, "Connecting with gameserver (try " + (attempt + 1) + "/" + NUM_ATTEMPTS + ")"); try { - config = clientConfigLoader.fetch(url); + RSConfig config = clientConfigLoader.fetch(url); if (Strings.isNullOrEmpty(config.getCodeBase()) || Strings.isNullOrEmpty(config.getInitialJar()) || Strings.isNullOrEmpty(config.getInitialClass())) { throw new IOException("Invalid or missing jav_config"); } - return; + return config; } catch (IOException e) { @@ -204,26 +214,377 @@ public class ClientLoader implements Supplier try { - RSConfig backupConfig = clientConfigLoader.fetch(HttpUrl.parse(BACKUP_CONFIG_URL)); - - if (Strings.isNullOrEmpty(backupConfig.getCodeBase()) || Strings.isNullOrEmpty(backupConfig.getInitialJar()) - || Strings.isNullOrEmpty(backupConfig.getInitialClass()) || Strings.isNullOrEmpty(backupConfig.getRuneLiteWorldParam())) - { - throw new IOException("Invalid or missing jav_config"); - } - - // Randomize the codebase - World world = worldSupplier.get(); - backupConfig.setCodebase("http://" + world.getAddress() + "/"); - - // Update the world applet parameter - Map appletProperties = backupConfig.getAppletProperties(); - appletProperties.put(backupConfig.getRuneLiteWorldParam(), Integer.toString(world.getId())); - config = backupConfig; + return downloadFallbackConfig(); } catch (IOException ex) { + log.debug("error downloading backup config", ex); throw err; // use error from Jagex's servers } } + + @Nonnull + private RSConfig downloadFallbackConfig() throws IOException + { + RSConfig backupConfig = clientConfigLoader.fetch(HttpUrl.parse(RuneLiteProperties.getJavConfigBackup())); + + if (Strings.isNullOrEmpty(backupConfig.getCodeBase()) || Strings.isNullOrEmpty(backupConfig.getInitialJar()) || Strings.isNullOrEmpty(backupConfig.getInitialClass())) + { + throw new IOException("Invalid or missing jav_config"); + } + + if (Strings.isNullOrEmpty(backupConfig.getRuneLiteGamepack()) || Strings.isNullOrEmpty(backupConfig.getRuneLiteWorldParam())) + { + throw new IOException("Backup config does not have RuneLite gamepack url"); + } + + // Randomize the codebase + World world = worldSupplier.get(); + backupConfig.setCodebase("http://" + world.getAddress() + "/"); + + // Update the world applet parameter + Map appletProperties = backupConfig.getAppletProperties(); + appletProperties.put(backupConfig.getRuneLiteWorldParam(), Integer.toString(world.getId())); + + return backupConfig; + } + + private void updateVanilla(RSConfig config) throws IOException, VerificationException + { + Certificate[] jagexCertificateChain = getJagexCertificateChain(); + + // Get the mtime of the first thing in the vanilla cache + // we check this against what the server gives us to let us skip downloading and patching the whole thing + + try (FileChannel vanilla = FileChannel.open(VANILLA_CACHE.toPath(), + StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)) + { + long vanillaCacheMTime = -1; + boolean vanillaCacheIsInvalid = false; + try + { + JarInputStream vanillaCacheTest = new JarInputStream(Channels.newInputStream(vanilla)); + vanillaCacheTest.skip(Long.MAX_VALUE); + JarEntry je = vanillaCacheTest.getNextJarEntry(); + if (je != null) + { + verifyJarEntry(je, jagexCertificateChain); + vanillaCacheMTime = je.getLastModifiedTime().toMillis(); + } + else + { + vanillaCacheIsInvalid = true; + } + } + catch (Exception e) + { + log.info("Failed to read the vanilla cache: {}", e.toString()); + vanillaCacheIsInvalid = true; + } + vanilla.position(0); + + // Start downloading the vanilla client + HttpUrl url; + if (config.isFallback()) + { + // If we are using the backup config, use our own gamepack and ignore the codebase + url = HttpUrl.parse(config.getRuneLiteGamepack()); + } + else + { + String codebase = config.getCodeBase(); + String initialJar = config.getInitialJar(); + url = HttpUrl.parse(codebase + initialJar); + } + + for (int attempt = 0; ; attempt++) + { + Request request = new Request.Builder() + .url(url) + .build(); + + try (Response response = okHttpClient.newCall(request).execute()) + { + // Its important to not close the response manually - this should be the only close or + // try-with-resources on this stream or it's children + + if (!response.isSuccessful()) + { + throw new IOException("unsuccessful response fetching gamepack: " + response.message()); + } + + int length = (int) response.body().contentLength(); + if (length < 0) + { + length = 3 * 1024 * 1024; + } + else + { + if (!vanillaCacheIsInvalid && vanilla.size() != length) + { + // The zip trailer filetab can be missing and the ZipInputStream will not notice + log.info("Vanilla cache is the wrong size"); + vanillaCacheIsInvalid = true; + } + } + final int flength = length; + TeeInputStream copyStream = new TeeInputStream(new CountingInputStream(response.body().byteStream(), + read -> SplashScreen.stage(.05, .35, null, "Downloading Old School RuneScape", read, flength, true))); + + // Save the bytes from the mtime test so we can write it to disk + // if the test fails, or the cache doesn't verify + ByteArrayOutputStream preRead = new ByteArrayOutputStream(); + copyStream.setOut(preRead); + + JarInputStream networkJIS = new JarInputStream(copyStream); + + // Get the mtime from the first entry so check it against the cache + { + JarEntry je = networkJIS.getNextJarEntry(); + if (je == null) + { + throw new IOException("unable to peek first jar entry"); + } + + networkJIS.skip(Long.MAX_VALUE); + verifyJarEntry(je, jagexCertificateChain); + long vanillaClientMTime = je.getLastModifiedTime().toMillis(); + if (!vanillaCacheIsInvalid && vanillaClientMTime != vanillaCacheMTime) + { + log.info("Vanilla cache is out of date: {} != {}", vanillaClientMTime, vanillaCacheMTime); + vanillaCacheIsInvalid = true; + } + } + + // the mtime matches so the cache is probably up to date, but just make sure its fully + // intact before closing the server connection + if (!vanillaCacheIsInvalid) + { + try + { + // as with the request stream, its important to not early close vanilla too + JarInputStream vanillaCacheTest = new JarInputStream(Channels.newInputStream(vanilla)); + verifyWholeJar(vanillaCacheTest, jagexCertificateChain); + } + catch (Exception e) + { + log.warn("Failed to verify the vanilla cache", e); + vanillaCacheIsInvalid = true; + } + } + + if (vanillaCacheIsInvalid) + { + // the cache is not up to date, commit our peek to the file and write the rest of it, while verifying + vanilla.position(0); + OutputStream out = Channels.newOutputStream(vanilla); + out.write(preRead.toByteArray()); + copyStream.setOut(out); + verifyWholeJar(networkJIS, jagexCertificateChain); + copyStream.skip(Long.MAX_VALUE); // write the trailer to the file too + out.flush(); + vanilla.truncate(vanilla.position()); + } + else + { + log.info("Using cached vanilla client"); + } + return; + } + catch (IOException e) + { + log.warn("Failed to download gamepack from \"{}\"", url, e); + + // With fallback config do 1 attempt (there are no additional urls to try) + if (config.isFallback() || attempt >= NUM_ATTEMPTS) + { + throw e; + } + + url = url.newBuilder().host(worldSupplier.get().getAddress()).build(); + } + } + } + } + + private void applyPatch() throws IOException + { + byte[] vanillaHash = new byte[64]; + byte[] appliedPatchHash = new byte[64]; + + try (InputStream is = ClientLoader.class.getResourceAsStream("/client.serial")) + { + 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(); + } + + DataInputStream dis = new DataInputStream(is); + dis.readFully(vanillaHash); + dis.readFully(appliedPatchHash); + } + + byte[] vanillaCacheHash = Files.asByteSource(VANILLA_CACHE).hash(Hashing.sha512()).asBytes(); + if (!Arrays.equals(vanillaHash, vanillaCacheHash)) + { + log.info("Client is outdated!"); + updateCheckMode = VANILLA; + return; + } + + if (PATCHED_CACHE.exists()) + { + byte[] diskBytes = Files.asByteSource(PATCHED_CACHE).hash(Hashing.sha512()).asBytes(); + if (!Arrays.equals(diskBytes, appliedPatchHash)) + { + log.warn("Cached patch hash mismatches, regenerating patch"); + } + else + { + log.info("Using cached patched client"); + return; + } + } + + try (HashingOutputStream hos = new HashingOutputStream(Hashing.sha512(), new FileOutputStream(PATCHED_CACHE)); + InputStream patch = ClientLoader.class.getResourceAsStream("/client.patch")) + { + new FileByFileV1DeltaApplier().applyDelta(VANILLA_CACHE, patch, hos); + + if (!Arrays.equals(hos.hash().asBytes(), appliedPatchHash)) + { + log.error("Patched client hash mismatch"); + updateCheckMode = VANILLA; + return; + } + } + catch (IOException e) + { + log.error("Unable to apply patch despite hash matching", e); + updateCheckMode = VANILLA; + return; + } + } + + private ClassLoader createJarClassLoader(File jar) throws IOException, ClassNotFoundException + { + try (JarFile jarFile = new JarFile(jar)) + { + ClassLoader classLoader = new ClassLoader(ClientLoader.class.getClassLoader()) + { + @Override + protected Class findClass(String name) throws ClassNotFoundException + { + String entryName = name.replace('.', '/').concat(".class"); + JarEntry jarEntry; + + try + { + jarEntry = jarFile.getJarEntry(entryName); + } + catch (IllegalStateException ex) + { + throw new ClassNotFoundException(name, ex); + } + + if (jarEntry == null) + { + throw new ClassNotFoundException(name); + } + + try + { + InputStream inputStream = jarFile.getInputStream(jarEntry); + if (inputStream == null) + { + throw new ClassNotFoundException(name); + } + + byte[] bytes = ByteStreams.toByteArray(inputStream); + return defineClass(name, bytes, 0, bytes.length); + } + catch (IOException e) + { + throw new ClassNotFoundException(null, e); + } + } + }; + + // load all of the classes in this jar; after the jar is closed the classloader + // will no longer be able to look up classes + Enumeration entries = jarFile.entries(); + while (entries.hasMoreElements()) + { + JarEntry jarEntry = entries.nextElement(); + String name = jarEntry.getName(); + if (name.endsWith(".class")) + { + name = name.substring(0, name.length() - 6); + classLoader.loadClass(name); + } + } + + return classLoader; + } + } + + private Applet loadClient(RSConfig config, ClassLoader classLoader) throws ClassNotFoundException, IllegalAccessException, InstantiationException + { + String initialClass = config.getInitialClass(); + Class clientClass = classLoader.loadClass(initialClass); + + Applet rs = (Applet) clientClass.newInstance(); + rs.setStub(new RSAppletStub(config)); + + if (rs instanceof Client) + { + log.info("client-patch {}", ((Client) rs).getBuildID()); + } + + return rs; + } + + private static Certificate[] getJagexCertificateChain() + { + try + { + CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); + Collection certificates = certificateFactory.generateCertificates(ClientLoader.class.getResourceAsStream("jagex.crt")); + return certificates.toArray(new Certificate[0]); + } + catch (CertificateException e) + { + throw new RuntimeException("Unable to parse pinned certificates", e); + } + } + + private void verifyJarEntry(JarEntry je, Certificate[] certs) throws VerificationException + { + switch (je.getName()) + { + case "META-INF/JAGEXLTD.SF": + case "META-INF/JAGEXLTD.RSA": + // You can't sign the signing files + return; + default: + if (!Arrays.equals(je.getCertificates(), certs)) + { + throw new VerificationException("Unable to verify jar entry: " + je.getName()); + } + } + } + + private void verifyWholeJar(JarInputStream jis, Certificate[] certs) throws IOException, VerificationException + { + for (JarEntry je; (je = jis.getNextJarEntry()) != null; ) + { + jis.skip(Long.MAX_VALUE); + verifyJarEntry(je, certs); + } + } } diff --git a/runelite-client/src/main/java/net/runelite/client/rs/ClientUpdateCheckMode.java b/runelite-client/src/main/java/net/runelite/client/rs/ClientUpdateCheckMode.java index bec27bb7c5..f8afce30be 100644 --- a/runelite-client/src/main/java/net/runelite/client/rs/ClientUpdateCheckMode.java +++ b/runelite-client/src/main/java/net/runelite/client/rs/ClientUpdateCheckMode.java @@ -28,6 +28,5 @@ public enum ClientUpdateCheckMode { AUTO, NONE, - VANILLA, - RSPS + VANILLA } diff --git a/runelite-client/src/main/java/net/runelite/client/rs/RSAppletStub.java b/runelite-client/src/main/java/net/runelite/client/rs/RSAppletStub.java index 6b73a5f284..a3a2a0f14a 100644 --- a/runelite-client/src/main/java/net/runelite/client/rs/RSAppletStub.java +++ b/runelite-client/src/main/java/net/runelite/client/rs/RSAppletStub.java @@ -27,12 +27,9 @@ package net.runelite.client.rs; import java.applet.AppletContext; import java.applet.AppletStub; -import java.io.File; import java.net.MalformedURLException; import java.net.URL; import lombok.RequiredArgsConstructor; -import net.runelite.client.RuneLite; -import net.runelite.client.util.StringFileUtils; @RequiredArgsConstructor class RSAppletStub implements AppletStub @@ -56,16 +53,6 @@ class RSAppletStub implements AppletStub { try { - if (RuneLite.allowPrivateServer) - { - File f = new File(RuneLite.RUNELITE_DIR + "/codebase"); - if (!f.exists()) - { - StringFileUtils.writeStringToFile(f.getAbsolutePath(), "http://127.0.0.1"); - } - return new URL(StringFileUtils.readStringFromFile(f.getAbsolutePath())); - } - return new URL(config.getCodeBase()); } catch (MalformedURLException ex) diff --git a/runelite-client/src/main/java/net/runelite/client/rs/RSConfig.java b/runelite-client/src/main/java/net/runelite/client/rs/RSConfig.java index 415b21b4e0..cf7a46ad9d 100644 --- a/runelite-client/src/main/java/net/runelite/client/rs/RSConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/rs/RSConfig.java @@ -55,6 +55,16 @@ class RSConfig return classLoaderProperties.get("initial_class").replace(".class", ""); } + boolean isFallback() + { + return getRuneLiteGamepack() != null; + } + + String getRuneLiteGamepack() + { + return classLoaderProperties.get("runelite.gamepack"); + } + String getRuneLiteWorldParam() { return classLoaderProperties.get("runelite.worldparam"); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/PluginClassLoader.java b/runelite-client/src/main/java/net/runelite/client/rs/TeeInputStream.java similarity index 56% rename from runelite-client/src/main/java/net/runelite/client/plugins/PluginClassLoader.java rename to runelite-client/src/main/java/net/runelite/client/rs/TeeInputStream.java index 94e7123da6..143f50740e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/PluginClassLoader.java +++ b/runelite-client/src/main/java/net/runelite/client/rs/TeeInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, Adam + * Copyright (c) 2019 Abex * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,50 +22,74 @@ * (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.plugins; +package net.runelite.client.rs; -import java.io.File; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import lombok.Getter; +import lombok.Setter; -/** - * A classloader for external plugins - * - * @author Adam - */ -public class PluginClassLoader extends URLClassLoader +class TeeInputStream extends FilterInputStream { - private final ClassLoader parent; + @Getter + @Setter + private OutputStream out; - public PluginClassLoader(File plugin, ClassLoader parent) throws MalformedURLException + TeeInputStream(InputStream in) { - super( - new URL[] - { - plugin.toURI().toURL() - }, - null // null or else class path scanning includes everything from the main class loader - ); - - this.parent = parent; + super(in); } @Override - public Class loadClass(String name) throws ClassNotFoundException + public int read(byte[] b, int off, int len) throws IOException { - try + int thisRead = super.read(b, off, len); + + if (thisRead > 0) { - return super.loadClass(name); + out.write(b, off, thisRead); } - catch (ClassNotFoundException ex) + + return thisRead; + } + + @Override + public int read() throws IOException + { + int val = super.read(); + if (val != -1) { - // fall back to main class loader - return parent.loadClass(name); + out.write(val); } - catch (NoClassDefFoundError ex) + return val; + } + + @Override + public long skip(long n) throws IOException + { + byte[] buf = new byte[(int) Math.min(n, 0x4000)]; + long total = 0; + while (n > 0) { - return null; + int read = (int) Math.min(n, buf.length); + + read = read(buf, 0, read); + if (read == -1) + { + break; + } + + total += read; + n -= read; } + return total; + } + + @Override + public boolean markSupported() + { + return false; } } diff --git a/runelite-client/src/main/java/net/runelite/client/task/Scheduler.java b/runelite-client/src/main/java/net/runelite/client/task/Scheduler.java index 8fcba6baf1..8ecf6ab665 100644 --- a/runelite-client/src/main/java/net/runelite/client/task/Scheduler.java +++ b/runelite-client/src/main/java/net/runelite/client/task/Scheduler.java @@ -24,11 +24,6 @@ */ package net.runelite.client.task; -import java.lang.invoke.CallSite; -import java.lang.invoke.LambdaMetafactory; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.time.Duration; @@ -65,59 +60,6 @@ public class Scheduler return Collections.unmodifiableList(scheduledMethods); } - public void registerObject(Object obj) - { - for (Method method : obj.getClass().getMethods()) - { - Schedule schedule = method.getAnnotation(Schedule.class); - - if (schedule == null) - { - continue; - } - - Runnable runnable = null; - try - { - final Class clazz = method.getDeclaringClass(); - final MethodHandles.Lookup caller = MethodHandles.privateLookupIn(clazz, MethodHandles.lookup()); - final MethodType subscription = MethodType.methodType(method.getReturnType(), method.getParameterTypes()); - final MethodHandle target = caller.findVirtual(clazz, method.getName(), subscription); - final CallSite site = LambdaMetafactory.metafactory( - caller, - "run", - MethodType.methodType(Runnable.class, clazz), - subscription, - target, - subscription); - - final MethodHandle factory = site.getTarget(); - runnable = (Runnable) factory.bindTo(obj).invokeExact(); - } - catch (Throwable e) - { - log.warn("Unable to create lambda for method {}", method, e); - } - - ScheduledMethod scheduledMethod = new ScheduledMethod(schedule, method, obj, runnable); - log.debug("Scheduled task {}", scheduledMethod); - - addScheduledMethod(scheduledMethod); - } - } - - public void unregisterObject(Object obj) - { - for (ScheduledMethod sm : scheduledMethods) - { - if (sm.getObject() == obj) - { - removeScheduledMethod(sm); - break; - } - } - } - public void tick() { Instant now = Instant.now(); @@ -166,7 +108,7 @@ public class Scheduler } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { - //log.warn("error invoking scheduled task", ex); + log.warn("error invoking scheduled task", ex); } catch (Exception ex) { diff --git a/runelite-client/src/main/java/net/runelite/client/ui/ClientPanel.java b/runelite-client/src/main/java/net/runelite/client/ui/ClientPanel.java index 1e4890d996..c7ccb1a3ed 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/ClientPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/ClientPanel.java @@ -24,21 +24,17 @@ */ package net.runelite.client.ui; -import io.sentry.Sentry; import java.applet.Applet; import java.awt.BorderLayout; import java.awt.Color; import javax.annotation.Nullable; -import javax.swing.JOptionPane; import javax.swing.JPanel; import net.runelite.api.Client; import net.runelite.api.Constants; -import net.runelite.client.RuneLite; -import net.runelite.client.util.StringFileUtils; final class ClientPanel extends JPanel { - ClientPanel(@Nullable Applet client) + public ClientPanel(@Nullable Applet client) { setSize(Constants.GAME_FIXED_SIZE); setMinimumSize(Constants.GAME_FIXED_SIZE); @@ -54,30 +50,7 @@ final class ClientPanel extends JPanel client.setLayout(null); client.setSize(Constants.GAME_FIXED_SIZE); - try - { - client.init(); - } - catch (Exception e) - { - if (RuneLite.allowPrivateServer) - { - String message = "Detected a bad codebase. Resetting...\n" - + "Please restart client.\n"; - JOptionPane.showMessageDialog(ClientUI.getFrame(), message, "Bad Codebase", - JOptionPane.ERROR_MESSAGE); - StringFileUtils.writeStringToFile(RuneLite.RUNELITE_DIR + "/codebase", "http://127.0.0.1/"); - } - else - { - JOptionPane.showMessageDialog(ClientUI.getFrame(), "Error loading Oldschool RuneScape!", "Error", - JOptionPane.ERROR_MESSAGE); - Sentry.captureException(e); - } - - ((Client) client).getLogger().error(null, e); - System.exit(0); - } + client.init(); client.start(); add(client, BorderLayout.CENTER); @@ -87,7 +60,7 @@ final class ClientPanel extends JPanel // and draw anywhere without it leaving artifacts if (client instanceof Client) { - ((Client) client).setGameDrawingMode(2); + ((Client)client).setGameDrawingMode(2); } } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/ui/ClientToolbar.java b/runelite-client/src/main/java/net/runelite/client/ui/ClientToolbar.java index e49ef782ce..20bfe8897b 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/ClientToolbar.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/ClientToolbar.java @@ -61,7 +61,7 @@ public class ClientToolbar if (buttons.add(button)) { - eventBus.post(NavigationButtonAdded.class, new NavigationButtonAdded(button)); + eventBus.post(new NavigationButtonAdded(button)); } } @@ -74,7 +74,7 @@ public class ClientToolbar { if (buttons.remove(button)) { - eventBus.post(NavigationButtonRemoved.class, new NavigationButtonRemoved(button)); + eventBus.post(new NavigationButtonRemoved(button)); } } } 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 a38f30308f..fdde73c995 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 @@ -36,23 +36,16 @@ import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.GraphicsDevice; -import static java.awt.GraphicsDevice.WindowTranslucency.TRANSLUCENT; import java.awt.GraphicsEnvironment; import java.awt.LayoutManager; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.TrayIcon; -import java.awt.Window; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.time.Duration; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.TimeUnit; import javax.annotation.Nullable; import javax.inject.Inject; import javax.inject.Named; @@ -85,6 +78,7 @@ import net.runelite.client.config.ExpandResizeType; import net.runelite.client.config.RuneLiteConfig; import net.runelite.client.config.WarningOnExit; import net.runelite.client.eventbus.EventBus; +import net.runelite.client.eventbus.Subscribe; import net.runelite.client.events.ClientShutdown; import net.runelite.client.events.ConfigChanged; import net.runelite.client.events.NavigationButtonAdded; @@ -93,6 +87,7 @@ import net.runelite.client.input.KeyManager; import net.runelite.client.input.MouseAdapter; import net.runelite.client.input.MouseListener; import net.runelite.client.input.MouseManager; +import net.runelite.client.ui.skin.SubstanceRuneLiteLookAndFeel; import net.runelite.client.util.HotkeyListener; import net.runelite.client.util.ImageUtil; import net.runelite.client.util.OSType; @@ -111,36 +106,21 @@ import org.pushingpixels.substance.internal.utils.SubstanceTitlePaneUtilities; public class ClientUI { private static final String CONFIG_GROUP = "runelite"; - private static final String PLUS_CONFIG_GROUP = "openosrs"; private static final String CONFIG_CLIENT_BOUNDS = "clientBounds"; private static final String CONFIG_CLIENT_MAXIMIZED = "clientMaximized"; - private static final String CONFIG_OPACITY = "enableOpacity"; - private static final String CONFIG_OPACITY_AMOUNT = "opacityPercentage"; private static final String CONFIG_CLIENT_SIDEBAR_CLOSED = "clientSidebarClosed"; - private static final int CLIENT_WELL_HIDDEN_MARGIN = 160; - private static final int CLIENT_WELL_HIDDEN_MARGIN_TOP = 10; - public static final BufferedImage ICON = ImageUtil.getResourceStreamFromClass(ClientUI.class, "/openosrs.png"); - public static boolean allowInput = false; + public static final BufferedImage ICON = ImageUtil.getResourceStreamFromClass(ClientUI.class, "/runelite.png"); @Getter private TrayIcon trayIcon; - @Getter - public static ContainableFrame frame; - @Getter - public static ClientPluginToolbar pluginToolbar; - @Getter - private boolean sidebarOpen; - @Getter - public static PluginPanel pluginPanel; private final RuneLiteConfig config; private final KeyManager keyManager; private final MouseManager mouseManager; private final Applet client; private final ConfigManager configManager; - private final ExecutorService executorService; private final Provider clientThreadProvider; - private final EventBus eventbus; + private final EventBus eventBus; private final boolean safeMode; private final CardLayout cardLayout = new CardLayout(); @@ -148,18 +128,19 @@ public class ClientUI private boolean withTitleBar; private BufferedImage sidebarOpenIcon; private BufferedImage sidebarClosedIcon; + private ContainableFrame frame; private JPanel navContainer; + private PluginPanel pluginPanel; + private ClientPluginToolbar pluginToolbar; private ClientTitleToolbar titleToolbar; private JButton currentButton; private NavigationButton currentNavButton; + private boolean sidebarOpen; private JPanel container; private NavigationButton sidebarNavigationButton; private JButton sidebarNavigationJButton; private Dimension lastClientSize; private Cursor defaultCursor; - private Field opacityField; - private Field peerField; - private Method setOpacityMethod; @Inject private ClientUI( @@ -168,9 +149,8 @@ public class ClientUI MouseManager mouseManager, @Nullable Applet client, ConfigManager configManager, - ExecutorService executorService, Provider clientThreadProvider, - EventBus eventbus, + EventBus eventBus, @Named("safeMode") boolean safeMode) { this.config = config; @@ -178,23 +158,15 @@ public class ClientUI this.mouseManager = mouseManager; this.client = client; this.configManager = configManager; - this.executorService = executorService; this.clientThreadProvider = clientThreadProvider; - this.eventbus = eventbus; + this.eventBus = eventBus; this.safeMode = safeMode; - - eventbus.subscribe(ConfigChanged.class, this, this::onConfigChanged); - eventbus.subscribe(NavigationButtonAdded.class, this, this::onNavigationButtonAdded); - eventbus.subscribe(NavigationButtonRemoved.class, this, this::onNavigationButtonRemoved); - eventbus.subscribe(GameStateChanged.class, this, this::onGameStateChanged); } - private void onConfigChanged(ConfigChanged event) + @Subscribe + public void onConfigChanged(ConfigChanged event) { - if (!event.getGroup().equals(CONFIG_GROUP) - && !(event.getGroup().equals(PLUS_CONFIG_GROUP) - && event.getKey().equals(CONFIG_OPACITY) || - event.getKey().equals(CONFIG_OPACITY_AMOUNT)) || + if (!event.getGroup().equals(CONFIG_GROUP) || event.getKey().equals(CONFIG_CLIENT_MAXIMIZED) || event.getKey().equals(CONFIG_CLIENT_BOUNDS)) { @@ -204,7 +176,8 @@ public class ClientUI SwingUtilities.invokeLater(() -> updateFrameConfig(event.getKey().equals("lockWindowSize"))); } - private void onNavigationButtonAdded(final NavigationButtonAdded event) + @Subscribe + public void onNavigationButtonAdded(final NavigationButtonAdded event) { SwingUtilities.invokeLater(() -> { @@ -266,15 +239,11 @@ public class ClientUI pluginToolbar.addComponent(event.getButton(), button); pluginToolbar.revalidate(); } - - if (navigationButton.getOnReady() != null) - { - navigationButton.getOnReady().run(); - } }); } - private void onNavigationButtonRemoved(final NavigationButtonRemoved event) + @Subscribe + public void onNavigationButtonRemoved(final NavigationButtonRemoved event) { SwingUtilities.invokeLater(() -> { @@ -291,7 +260,8 @@ public class ClientUI }); } - private void onGameStateChanged(final GameStateChanged event) + @Subscribe + public void onGameStateChanged(final GameStateChanged event) { if (event.getGameState() != GameState.LOGGED_IN || !(client instanceof Client) || !config.usernameInTitle()) { @@ -335,7 +305,14 @@ public class ClientUI { SwingUtilities.invokeAndWait(() -> { - SwingUtil.setupRuneLiteLookAndFeel(); + // Set some sensible swing defaults + SwingUtil.setupDefaults(); + + // Use substance look and feel + SwingUtil.setTheme(new SubstanceRuneLiteLookAndFeel()); + + // Use custom UI font + SwingUtil.setFont(FontManager.getRunescapeFont()); // Create main window frame = new ContainableFrame(); @@ -440,8 +417,7 @@ public class ClientUI // Decorate window with custom chrome and titlebar if needed withTitleBar = config.enableCustomChrome(); - final boolean borderless = config.borderless(); - frame.setUndecorated(withTitleBar | borderless); + frame.setUndecorated(withTitleBar); if (withTitleBar) { @@ -489,10 +465,6 @@ public class ClientUI } }); } - else if (borderless) - { - frame.getRootPane().setWindowDecorationStyle(JRootPane.NONE); - } // Update config updateFrameConfig(true); @@ -598,7 +570,6 @@ public class ClientUI frame.setVisible(true); frame.toFront(); requestFocus(); - giveClientFocus(); log.info("Showing frame {}", frame); frame.revalidateMinimumSize(); }); @@ -607,9 +578,9 @@ public class ClientUI if (client != null && !(client instanceof Client)) { SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(frame, - "OpenOSRS has not yet been updated to work with the latest\n" + "RuneLite has not yet been updated to work with the latest\n" + "game update, it will work with reduced functionality until then.", - "OpenOSRS is outdated", INFORMATION_MESSAGE)); + "RuneLite is outdated", INFORMATION_MESSAGE)); } } @@ -650,22 +621,10 @@ public class ClientUI { saveClientBoundsConfig(); ClientShutdown csev = new ClientShutdown(); - eventbus.post(ClientShutdown.class, csev); - executorService.shutdown(); - + eventBus.post(csev); new Thread(() -> { csev.waitForAllConsumers(Duration.ofSeconds(10)); - try - { - if (!executorService.awaitTermination(5, TimeUnit.SECONDS)) - { - executorService.shutdownNow(); - } - } - catch (InterruptedException ignored) - { - } if (client != null) { @@ -694,7 +653,7 @@ public class ClientUI } } System.exit(0); - }, "OpenOSRS Shutdown").start(); + }, "RuneLite Shutdown").start(); } /** @@ -780,7 +739,6 @@ public class ClientUI /** * Returns current cursor set on game container - * * @return awt cursor */ public Cursor getCurrentCursor() @@ -790,7 +748,6 @@ public class ClientUI /** * Returns current custom cursor or default system cursor if cursor is not set - * * @return awt cursor */ public Cursor getDefaultCursor() @@ -801,7 +758,6 @@ public class ClientUI /** * Changes cursor for client window. Requires ${@link ClientUI#init()} 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 */ @@ -820,7 +776,6 @@ public class ClientUI /** * Changes cursor for client window. Requires ${@link ClientUI#init()} to be called first. - * * @param cursor awt cursor */ public void setCursor(final Cursor cursor) @@ -830,7 +785,6 @@ public class ClientUI /** * Resets client window cursor to default one. - * * @see ClientUI#setCursor(BufferedImage, String) */ public void resetCursor() @@ -866,7 +820,6 @@ public class ClientUI /** * Paint UI related overlays to target graphics - * * @param graphics target graphics */ public void paintOverlays(final Graphics2D graphics) @@ -1066,9 +1019,17 @@ public class ClientUI return; } + // Update window opacity if the frame is undecorated, translucency capable and not fullscreen + if (frame.isUndecorated() && + frame.getGraphicsConfiguration().isTranslucencyCapable() && + frame.getGraphicsConfiguration().getDevice().getFullScreenWindow() == null) + { + frame.setOpacity(((float) config.windowOpacity()) / 100.0f); + } + if (config.usernameInTitle() && (client instanceof Client)) { - final Player player = ((Client) client).getLocalPlayer(); + final Player player = ((Client)client).getLocalPlayer(); if (player != null && player.getName() != null) { @@ -1107,38 +1068,6 @@ public class ClientUI configManager.unsetConfiguration(CONFIG_GROUP, CONFIG_CLIENT_BOUNDS); } - Boolean opacity = configManager.getConfiguration(PLUS_CONFIG_GROUP, CONFIG_OPACITY, boolean.class); - - if (opacity != null && opacity) - { - // Update window opacity if the frame is undecorated, translucency capable and not fullscreen - if (frame.isUndecorated() && - frame.getGraphicsConfiguration().isTranslucencyCapable() && - frame.getGraphicsConfiguration().getDevice().getFullScreenWindow() == null) - { - frame.setOpacity(Float.parseFloat(configManager.getConfiguration(PLUS_CONFIG_GROUP, CONFIG_OPACITY_AMOUNT)) / 100F); - } - else - { - GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); - GraphicsDevice gd = ge.getDefaultScreenDevice(); - - if (gd.isWindowTranslucencySupported(TRANSLUCENT)) - { - setOpacity(); - } - else - { - log.warn("Opacity isn't supported on your system!"); - configManager.setConfiguration(PLUS_CONFIG_GROUP, CONFIG_OPACITY, false); - } - } - } - else if (frame.getOpacity() != 1F) - { - frame.setOpacity(1F); - } - if (client == null) { return; @@ -1188,40 +1117,4 @@ public class ClientUI configManager.setConfiguration(CONFIG_GROUP, CONFIG_CLIENT_BOUNDS, bounds); } } - - private void setOpacity() - { - SwingUtilities.invokeLater(() -> - { - try - { - if (opacityField == null) - { - opacityField = Window.class.getDeclaredField("opacity"); - opacityField.setAccessible(true); - } - if (peerField == null) - { - peerField = Component.class.getDeclaredField("peer"); - peerField.setAccessible(true); - } - if (setOpacityMethod == null) - { - setOpacityMethod = Class.forName("java.awt.peer.WindowPeer").getDeclaredMethod("setOpacity", float.class); - } - - - final float opacity = Float.parseFloat(configManager.getConfiguration(PLUS_CONFIG_GROUP, CONFIG_OPACITY_AMOUNT)) / 100F; - assert opacity > 0F && opacity <= 1F : "I don't know who you are, I don't know why you tried, and I don't know how you tried, but this is NOT what you're supposed to do and you should honestly feel terrible about what you did, so I want you to take a nice long amount of time to think about what you just tried to do so you are not gonna do this in the future."; - - opacityField.setFloat(frame, opacity); - setOpacityMethod.invoke(peerField.get(frame), opacity); - - } - catch (NoSuchFieldException | NoSuchMethodException | ClassNotFoundException | IllegalAccessException | InvocationTargetException | NullPointerException e) - { - // e.printStackTrace(); - } - }); - } } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/ColorScheme.java b/runelite-client/src/main/java/net/runelite/client/ui/ColorScheme.java index cef0c0b02b..5be1250547 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/ColorScheme.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/ColorScheme.java @@ -31,11 +31,11 @@ import java.awt.Color; */ public class ColorScheme { - /* The blue color used for the branding's accents */ - public static final Color BRAND_BLUE = new Color(25, 194, 255); + /* The orange color used for the branding's accents */ + public static final Color BRAND_ORANGE = new Color(220, 138, 0); - /* The blue color used for the branding's accents, with lowered opacity */ - public static final Color BRAND_BLUE_TRANSPARENT = new Color(25, 194, 255, 120); + /* The orange color used for the branding's accents, with lowered opacity */ + public static final Color BRAND_ORANGE_TRANSPARENT = new Color(220, 138, 0, 120); public static final Color DARKER_GRAY_COLOR = new Color(30, 30, 30); public static final Color DARK_GRAY_COLOR = new Color(40, 40, 40); @@ -51,8 +51,8 @@ public class ColorScheme /* The color for the red progress bar (used in ge offers, farming tracker, etc)*/ public static final Color PROGRESS_ERROR_COLOR = new Color(230, 30, 30); - /* The color for the blue progress bar (used in ge offers, farming tracker, etc)*/ - public static final Color PROGRESS_INPROGRESS_COLOR = new Color(0, 106, 221); + /* The color for the orange progress bar (used in ge offers, farming tracker, etc)*/ + public static final Color PROGRESS_INPROGRESS_COLOR = new Color(230, 150, 30); /* The color for the price indicator in the ge search results */ public static final Color GRAND_EXCHANGE_PRICE = new Color(110, 225, 110); diff --git a/runelite-client/src/main/java/net/runelite/client/ui/ContainableFrame.java b/runelite-client/src/main/java/net/runelite/client/ui/ContainableFrame.java index 5c0ec3ca17..158e22a2b6 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/ContainableFrame.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/ContainableFrame.java @@ -47,7 +47,7 @@ public class ContainableFrame extends JFrame { ALWAYS, RESIZING, - NEVER + NEVER; } private static final int SCREEN_EDGE_CLOSE_DISTANCE = 40; @@ -89,17 +89,6 @@ public class ContainableFrame extends JFrame private Mode containedInScreen; private boolean expandedClientOppositeDirection; - ContainableFrame() - { - addWindowStateListener(windowEvent -> - { - if (windowEvent.getNewState() == Frame.NORMAL) - { - revalidateMinimumSize(); - } - }); - } - public void setContainedInScreen(Mode value) { this.containedInScreen = value; @@ -149,7 +138,6 @@ public class ContainableFrame extends JFrame /** * Expand frame by specified value. If the frame is going to be expanded outside of screen push the frame to * the side. - * * @param value size to expand frame by */ public void expandBy(final int value) @@ -186,7 +174,6 @@ public class ContainableFrame extends JFrame if (wouldExpandThroughEdge) { - if (!isFrameCloseToRightEdge() || isFrameCloseToLeftEdge()) { // Move the window to the edge @@ -210,7 +197,6 @@ public class ContainableFrame extends JFrame /** * Contract frame by specified value. If new frame size is less than it's minimum size, force the minimum size. * If the frame was pushed from side before, restore it's original position. - * * @param value value to contract frame by */ public void contractBy(final int value) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/DynamicGridLayout.java b/runelite-client/src/main/java/net/runelite/client/ui/DynamicGridLayout.java index 3d6912d156..120decaf13 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/DynamicGridLayout.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/DynamicGridLayout.java @@ -33,7 +33,7 @@ import java.util.function.Function; /** * Grid layout implementation with support for cells with unequal size. - *

+ * * See https://www.javaworld.com/article/2077486/core-java/java-tip-121--flex-your-grid-layout.html */ public class DynamicGridLayout extends GridLayout @@ -152,9 +152,8 @@ public class DynamicGridLayout extends GridLayout /** * Calculate outer size of the layout based on it's children and sizer - * * @param parent parent component - * @param sizer functioning returning dimension of the child component + * @param sizer functioning returning dimension of the child component * @return outer size */ private Dimension calculateSize(final Container parent, final Function sizer) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/FatalErrorDialog.java b/runelite-client/src/main/java/net/runelite/client/ui/FatalErrorDialog.java new file mode 100644 index 0000000000..dcf1b9fe48 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/FatalErrorDialog.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.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +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.net.ConnectException; +import java.net.UnknownHostException; +import java.security.GeneralSecurityException; +import java.util.concurrent.atomic.AtomicBoolean; +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.util.ImageUtil; +import net.runelite.client.util.LinkBrowser; +import net.runelite.client.util.VerificationException; + +@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 = ImageUtil.getResourceStreamFromClass(FatalErrorDialog.class, "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 (RuntimeException 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", () -> + { + 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); + } + + public void open() + { + addButton("Exit", () -> System.exit(-1)); + + pack(); + setLocationRelativeTo(null); + 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(); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/FontManager.java b/runelite-client/src/main/java/net/runelite/client/ui/FontManager.java index 631260bc7a..564ebcba62 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/FontManager.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/FontManager.java @@ -24,12 +24,11 @@ */ package net.runelite.client.ui; +import javax.swing.text.StyleContext; import java.awt.Font; import java.awt.FontFormatException; import java.awt.GraphicsEnvironment; import java.io.IOException; -import java.io.InputStream; -import javax.swing.text.StyleContext; public class FontManager { @@ -43,42 +42,31 @@ public class FontManager try { - Font font; - - try (InputStream runescapeIn = FontManager.class.getResourceAsStream("runescape.ttf")) - { - font = Font.createFont(Font.TRUETYPE_FONT, - runescapeIn) - .deriveFont(Font.PLAIN, 16); - ge.registerFont(font); - } + Font font = Font.createFont(Font.TRUETYPE_FONT, + FontManager.class.getResourceAsStream("runescape.ttf")) + .deriveFont(Font.PLAIN, 16); + ge.registerFont(font); runescapeFont = StyleContext.getDefaultStyleContext() - .getFont(font.getName(), Font.PLAIN, 16); + .getFont(font.getName(), Font.PLAIN, 16); ge.registerFont(runescapeFont); - try (InputStream smallIn = FontManager.class.getResourceAsStream("runescape_small.ttf")) - { - font = Font.createFont(Font.TRUETYPE_FONT, - smallIn) - .deriveFont(Font.PLAIN, 16); - ge.registerFont(font); - } + Font smallFont = Font.createFont(Font.TRUETYPE_FONT, + FontManager.class.getResourceAsStream("runescape_small.ttf")) + .deriveFont(Font.PLAIN, 16); + ge.registerFont(smallFont); runescapeSmallFont = StyleContext.getDefaultStyleContext() - .getFont(font.getName(), Font.PLAIN, 16); + .getFont(smallFont.getName(), Font.PLAIN, 16); ge.registerFont(runescapeSmallFont); - try (InputStream boldIn = FontManager.class.getResourceAsStream("runescape_bold.ttf")) - { - font = Font.createFont(Font.TRUETYPE_FONT, - boldIn) + Font boldFont = Font.createFont(Font.TRUETYPE_FONT, + FontManager.class.getResourceAsStream("runescape_bold.ttf")) .deriveFont(Font.BOLD, 16); - ge.registerFont(font); - } + ge.registerFont(boldFont); runescapeBoldFont = StyleContext.getDefaultStyleContext() - .getFont(font.getName(), Font.BOLD, 16); + .getFont(boldFont.getName(), Font.BOLD, 16); ge.registerFont(runescapeBoldFont); } catch (FontFormatException ex) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/JMultilineLabel.java b/runelite-client/src/main/java/net/runelite/client/ui/JMultilineLabel.java deleted file mode 100644 index a321967ef7..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/ui/JMultilineLabel.java +++ /dev/null @@ -1,27 +0,0 @@ -package net.runelite.client.ui; - -import javax.swing.JLabel; -import javax.swing.JTextArea; -import javax.swing.border.EmptyBorder; -import javax.swing.text.DefaultCaret; - -public class JMultilineLabel extends JTextArea -{ - private static final long serialVersionUID = 1L; - - public JMultilineLabel() - { - super(); - setEditable(false); - setCursor(null); - setOpaque(false); - setFocusable(false); - setWrapStyleWord(true); - setLineWrap(true); - setBorder(new EmptyBorder(0, 8, 0, 8)); - setAlignmentY(JLabel.CENTER_ALIGNMENT); - - DefaultCaret caret = (DefaultCaret) getCaret(); - caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/MultiplexingPluginPanel.java b/runelite-client/src/main/java/net/runelite/client/ui/MultiplexingPluginPanel.java index b51ce4027d..6454058a4c 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/MultiplexingPluginPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/MultiplexingPluginPanel.java @@ -45,6 +45,7 @@ public class MultiplexingPluginPanel extends PluginPanel { for (int i = getComponentCount() - 1; i > 0; i--) { + onRemove((PluginPanel) getComponent(i)); remove(i); } } @@ -80,6 +81,7 @@ public class MultiplexingPluginPanel extends PluginPanel else { add(subpanel, name); + onAdd(subpanel); } layout.show(this, name); @@ -103,10 +105,19 @@ public class MultiplexingPluginPanel extends PluginPanel current = subpanel; } layout.show(this, System.identityHashCode(subpanel) + ""); + onRemove((PluginPanel) getComponent(count - 1)); remove(count - 1); revalidate(); } + protected void onAdd(PluginPanel p) + { + } + + protected void onRemove(PluginPanel p) + { + } + @Override public void onActivate() { @@ -120,4 +131,4 @@ public class MultiplexingPluginPanel extends PluginPanel active = false; current.onDeactivate(); } -} \ No newline at end of file +} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/NavigationButton.java b/runelite-client/src/main/java/net/runelite/client/ui/NavigationButton.java index 5ebbb7fd39..4cc334ac76 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/NavigationButton.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/NavigationButton.java @@ -56,11 +56,6 @@ public class NavigationButton @Builder.Default private final String tooltip = ""; - /** - * On button having been fully initialized and added to frame. - */ - private Runnable onReady; - /** * Button selection state */ diff --git a/runelite-client/src/main/java/net/runelite/client/ui/PluginPanel.java b/runelite-client/src/main/java/net/runelite/client/ui/PluginPanel.java index a971079a6f..f5678f9f8d 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/PluginPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/PluginPanel.java @@ -44,7 +44,7 @@ public abstract class PluginPanel extends JPanel @Getter(AccessLevel.PROTECTED) private final JScrollPane scrollPane; - @Getter(AccessLevel.PUBLIC) + @Getter(AccessLevel.PACKAGE) private final JPanel wrappedPanel; protected PluginPanel() diff --git a/runelite-client/src/main/java/net/runelite/client/ui/RuneLiteSplashScreen.java b/runelite-client/src/main/java/net/runelite/client/ui/RuneLiteSplashScreen.java deleted file mode 100644 index 923d955d51..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/ui/RuneLiteSplashScreen.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (c) 2019, TheStonedTurtle - * 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.Dimension; -import javax.swing.JFrame; -import javax.swing.JPanel; -import javax.swing.JProgressBar; -import javax.swing.SwingUtilities; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; -import net.runelite.client.ui.components.InfoPanel; -import net.runelite.client.ui.components.MessagePanel; - -@Slf4j -public class RuneLiteSplashScreen extends JFrame -{ - private static RuneLiteSplashScreen INSTANCE; - public static final Dimension FRAME_SIZE = new Dimension(600, 350); - - @Getter - private final MessagePanel messagePanel = new MessagePanel(); - - private RuneLiteSplashScreen() - { - this.setTitle("OpenOSRS"); - this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - this.setSize(FRAME_SIZE); - this.setLayout(new BorderLayout()); - this.setUndecorated(true); - this.setIconImage(ClientUI.ICON); - - final JPanel panel = new JPanel(); - panel.setLayout(new BorderLayout()); - panel.setPreferredSize(RuneLiteSplashScreen.FRAME_SIZE); - - panel.add(new InfoPanel(), BorderLayout.EAST); - panel.add(messagePanel, BorderLayout.WEST); - - this.setContentPane(panel); - pack(); - - this.setLocationRelativeTo(null); - this.setVisible(true); - } - - public static boolean showing() - { - return INSTANCE != null; - } - - public static void setError(String title, String content) - { - if (INSTANCE != null) - { - INSTANCE.setErrorInstance(title, content); - } - } - - private void setErrorInstance(String title, String content) - { - messagePanel.setMessageContent(content); - messagePanel.setMessageTitle("Error!"); - - messagePanel.getBarLabel().setText(title); - messagePanel.getBar().setBackground(ColorScheme.PROGRESS_ERROR_COLOR.darker()); - messagePanel.getBar().setForeground(ColorScheme.PROGRESS_ERROR_COLOR); - - this.getContentPane().revalidate(); - this.getContentPane().repaint(); - } - - private void setBarText(final String text) - { - final JProgressBar bar = messagePanel.getBar(); - bar.setString(text); - bar.setStringPainted(text != null); - bar.revalidate(); - bar.repaint(); - } - - private void setMessage(final String msg, final double value) - { - messagePanel.getBarLabel().setText(msg); - messagePanel.getBar().setMaximum(1000); - messagePanel.getBar().setValue((int) (value * 1000)); - setBarText(null); - - this.getContentPane().revalidate(); - this.getContentPane().repaint(); - } - - public static void init() - { - SwingUtilities.invokeLater(() -> - { - try - { - INSTANCE = new RuneLiteSplashScreen(); - } - catch (Exception e) - { - log.warn("Unable to start splash screen", e); - } - }); - } - - public static void close() - { - SwingUtilities.invokeLater(() -> - { - if (INSTANCE == null) - { - return; - } - - INSTANCE.setVisible(false); - INSTANCE.dispose(); - INSTANCE = null; - }); - } - - public static void stage(double startProgress, double endProgress, String progressText, int done, int total) - { - String progress = done + " / " + total; - stage(startProgress + ((endProgress - startProgress) * done / total), progressText + " " + progress); - } - - public static void stage(double overallProgress, String progressText) - { - if (INSTANCE != null) - { - INSTANCE.setMessage(progressText, overallProgress); - } - } -} \ No newline at end of file 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..77cac7340b --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/SplashScreen.java @@ -0,0 +1,248 @@ +/* + * 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.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; +import net.runelite.client.ui.skin.SubstanceRuneLiteLookAndFeel; +import net.runelite.client.util.ImageUtil; +import org.pushingpixels.substance.internal.SubstanceSynapse; + +@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 = ImageUtil.getResourceStreamFromClass(SplashScreen.class, "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 boolean isOpen() + { + return INSTANCE != null; + } + + public static void init() + { + try + { + SwingUtilities.invokeAndWait(() -> + { + if (INSTANCE != null) + { + return; + } + + try + { + boolean hasLAF = UIManager.getLookAndFeel() instanceof SubstanceRuneLiteLookAndFeel; + if (!hasLAF) + { + UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); + } + INSTANCE = new SplashScreen(); + if (hasLAF) + { + INSTANCE.getRootPane().putClientProperty(SubstanceSynapse.COLORIZATION_FACTOR, 1.0); + } + } + 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 * 1024; + final double CEIL = 1.d / 10.d; + progress = String.format("%.1f / %.1f MiB", done / MiB, (total / MiB) + CEIL); + } + 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/java/net/runelite/client/ui/components/ComboBoxListRenderer.java b/runelite-client/src/main/java/net/runelite/client/ui/components/ComboBoxListRenderer.java index 2699fe0f20..5aeb710115 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/components/ComboBoxListRenderer.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/ComboBoxListRenderer.java @@ -30,9 +30,8 @@ import javax.swing.JLabel; import javax.swing.JList; import javax.swing.ListCellRenderer; import javax.swing.border.EmptyBorder; -import lombok.Setter; -import net.runelite.api.util.Text; import net.runelite.client.ui.ColorScheme; +import net.runelite.client.util.Text; /** * A custom list renderer to avoid substance's weird coloring. @@ -42,8 +41,6 @@ import net.runelite.client.ui.ColorScheme; */ public final class ComboBoxListRenderer extends JLabel implements ListCellRenderer { - @Setter - private String defaultText = "Select an option..."; @Override public Component getListCellRendererComponent(JList list, Object o, int index, boolean isSelected, boolean cellHasFocus) @@ -59,25 +56,13 @@ public final class ComboBoxListRenderer extends JLabel implements ListCellRender setForeground(ColorScheme.LIGHT_GRAY_COLOR); } - setBorder(new EmptyBorder(5, 10, 5, 10)); - setIcon(null); + setBorder(new EmptyBorder(5, 5, 5, 0)); String text; - // If using setSelectedItem(null) or setSelectedIndex(-1) show default text until a selection is made - if (index == -1 && o == null) - { - text = defaultText; - } - else if (o instanceof Enum) + if (o instanceof Enum) { text = Text.titleCase((Enum) o); } - else if (o instanceof ComboBoxIconEntry) - { - ComboBoxIconEntry e = (ComboBoxIconEntry) o; - text = e.getText(); - setIcon(e.getIcon()); - } else { text = o.toString(); diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/CustomScrollBarUI.java b/runelite-client/src/main/java/net/runelite/client/ui/components/CustomScrollBarUI.java index 29125a9ee3..128c88197a 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/components/CustomScrollBarUI.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/CustomScrollBarUI.java @@ -76,7 +76,7 @@ public class CustomScrollBarUI extends BasicScrollBarUI /** * Creates an empty JButton to be used as the scroll bar's arrows (to disable the arrows). */ - private JButton createEmptyButton() + protected JButton createEmptyButton() { JButton button = new JButton(); Dimension zeroDim = new Dimension(0, 0); diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/DimmableJPanel.java b/runelite-client/src/main/java/net/runelite/client/ui/components/DimmableJPanel.java index 58a6fd7e95..c36dd72abe 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/components/DimmableJPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/DimmableJPanel.java @@ -70,10 +70,9 @@ public class DimmableJPanel extends JPanel * Dimming sets all parts of this component with darker colors except for the central label * This is useful for showing that progress is paused * Setting dim to false will restore the original colors from before the component was dimmed. - * * @param dimmed */ - void setDimmed(boolean dimmed) + public void setDimmed(boolean dimmed) { this.dimmed = dimmed; diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/DragAndDropReorderPane.java b/runelite-client/src/main/java/net/runelite/client/ui/components/DragAndDropReorderPane.java index b4c0803a33..32ecaaf69c 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/components/DragAndDropReorderPane.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/DragAndDropReorderPane.java @@ -196,4 +196,4 @@ public class DragAndDropReorderPane extends JLayeredPane } } } -} \ No newline at end of file +} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/FlatTextField.java b/runelite-client/src/main/java/net/runelite/client/ui/components/FlatTextField.java index e70a31b06d..1c13cad475 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/components/FlatTextField.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/FlatTextField.java @@ -67,7 +67,7 @@ public class FlatTextField extends JPanel this.textField.setBorder(null); this.textField.setOpaque(false); this.textField.setSelectedTextColor(Color.WHITE); - this.textField.setSelectionColor(ColorScheme.BRAND_BLUE_TRANSPARENT); + this.textField.setSelectionColor(ColorScheme.BRAND_ORANGE_TRANSPARENT); add(textField, BorderLayout.CENTER); diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/InfoPanel.java b/runelite-client/src/main/java/net/runelite/client/ui/components/InfoPanel.java deleted file mode 100644 index fc357d2f3f..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/ui/components/InfoPanel.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (c) 2019, TheStonedTurtle - * 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.components; - -import java.awt.Color; -import java.awt.Cursor; -import java.awt.Dimension; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Image; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.image.BufferedImage; -import java.io.File; -import javax.swing.ImageIcon; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.border.CompoundBorder; -import javax.swing.border.EmptyBorder; -import javax.swing.border.MatteBorder; -import lombok.extern.slf4j.Slf4j; -import net.runelite.client.RuneLiteProperties; -import net.runelite.client.ui.ColorScheme; -import net.runelite.client.ui.FontManager; -import net.runelite.client.ui.RuneLiteSplashScreen; -import net.runelite.client.util.ImageUtil; -import net.runelite.client.util.LinkBrowser; - -@Slf4j -public class InfoPanel extends JPanel -{ - private static final Color DARK_GREY = new Color(10, 10, 10, 255); - - private static final BufferedImage TRANSPARENT_LOGO = ImageUtil.getResourceStreamFromClass(InfoPanel.class, "/openosrs.png"); - static final Dimension PANEL_SIZE = new Dimension(200, RuneLiteSplashScreen.FRAME_SIZE.height); - private static final Dimension VERSION_SIZE = new Dimension(PANEL_SIZE.width, 25); - private static final File RUNELITE_DIR = new File(System.getProperty("user.home"), ".runelite"); - private static final File LOGS_DIR = new File(RUNELITE_DIR, "logs"); - - public InfoPanel() - { - this.setLayout(new GridBagLayout()); - this.setPreferredSize(PANEL_SIZE); - this.setBackground(new Color(38, 38, 38)); - - final GridBagConstraints c = new GridBagConstraints(); - c.fill = GridBagConstraints.HORIZONTAL; - c.weightx = 1; - c.gridx = 0; - c.gridy = 0; - c.ipady = 5; - - // Logo - final ImageIcon transparentLogo = new ImageIcon(); - if (TRANSPARENT_LOGO != null) - { - transparentLogo.setImage(TRANSPARENT_LOGO.getScaledInstance(128, 128, Image.SCALE_SMOOTH)); - } - final JLabel logo = new JLabel(transparentLogo); - - c.anchor = GridBagConstraints.NORTH; - c.weighty = 1; - this.add(logo, c); - c.gridy++; - c.anchor = GridBagConstraints.SOUTH; - c.weighty = 0; - - // OpenOSRS version - this.add(createPanelTextButton("OpenOSRS Version: " + RuneLiteProperties.getPlusVersion()), c); - c.gridy++; - - // Build date - this.add(createPanelTextButton("Build date: " + RuneLiteProperties.getPlusDate()), c); - c.gridy++; - - final JLabel logsFolder = createPanelButton("Open logs folder", null, () -> LinkBrowser.openLocalFile(LOGS_DIR)); - this.add(logsFolder, c); - c.gridy++; - - final JLabel discord = createPanelButton("Get help on Discord", "Instant invite link to join the openosrs discord", () -> LinkBrowser.browse(RuneLiteProperties.getDiscordInvite())); - this.add(discord, c); - c.gridy++; - - final JLabel troubleshooting = createPanelButton("Troubleshooting steps", "Opens a link to the troubleshooting wiki", () -> LinkBrowser.browse(RuneLiteProperties.getTroubleshootingLink())); - this.add(troubleshooting, c); - c.gridy++; - - final JLabel exit = createPanelButton("Exit", "Closes the application immediately", () -> System.exit(0)); - this.add(exit, c); - c.gridy++; - } - - private static JLabel createPanelTextButton(final String title) - { - final JLabel textButton = new JLabel(title); - textButton.setFont(FontManager.getRunescapeSmallFont()); - textButton.setHorizontalAlignment(JLabel.CENTER); - textButton.setForeground(ColorScheme.BRAND_BLUE); - textButton.setBackground(null); - textButton.setPreferredSize(VERSION_SIZE); - textButton.setMinimumSize(VERSION_SIZE); - textButton.setBorder(new MatteBorder(1, 0, 0, 0, DARK_GREY)); - - return textButton; - } - - private static JLabel createPanelButton(final String name, final String tooltip, final Runnable runnable) - { - final JLabel btn = new JLabel(name, JLabel.CENTER); - btn.setToolTipText(tooltip); - btn.setOpaque(true); - btn.setBackground(null); - btn.setForeground(Color.WHITE); - btn.setFont(FontManager.getRunescapeFont()); - btn.setBorder(new CompoundBorder( - new MatteBorder(1, 0, 0, 0, DARK_GREY), - new EmptyBorder(3, 0, 3, 0)) - ); - btn.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - btn.addMouseListener(new MouseAdapter() - { - @Override - public void mouseClicked(MouseEvent e) - { - runnable.run(); - } - - @Override - public void mouseEntered(MouseEvent e) - { - btn.setBackground(new Color(60, 60, 60)); - btn.repaint(); - } - - @Override - public void mouseExited(MouseEvent e) - { - btn.setBackground(null); - btn.repaint(); - } - }); - - return btn; - } -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/MessagePanel.java b/runelite-client/src/main/java/net/runelite/client/ui/components/MessagePanel.java deleted file mode 100644 index 79b833fa17..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/ui/components/MessagePanel.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (c) 2019, TheStonedTurtle - * 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.components; - -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JProgressBar; -import javax.swing.JScrollPane; -import javax.swing.JViewport; -import javax.swing.border.EmptyBorder; -import javax.swing.border.MatteBorder; -import javax.swing.plaf.basic.BasicProgressBarUI; -import lombok.AccessLevel; -import lombok.Getter; -import net.runelite.client.ui.ColorScheme; -import net.runelite.client.ui.FontManager; -import net.runelite.client.ui.RuneLiteSplashScreen; - -@Getter -public class MessagePanel extends JPanel -{ - private static final Dimension PANEL_SIZE = new Dimension(RuneLiteSplashScreen.FRAME_SIZE.width - InfoPanel.PANEL_SIZE.width, RuneLiteSplashScreen.FRAME_SIZE.height); - private static final Dimension BAR_SIZE = new Dimension(PANEL_SIZE.width, 30); - private static final int MESSAGE_AREA_PADDING = 15; - - private final JLabel titleLabel = new JLabel("Welcome to OpenOSRS"); - private final JLabel messageArea; - private final JLabel barLabel = new JLabel("Connecting with gameserver (try 1/10)"); - private final JProgressBar bar = new JProgressBar(0, 100); - - @Getter(AccessLevel.NONE) - private final JScrollPane scrollPane; - - public MessagePanel() - { - this.setPreferredSize(PANEL_SIZE); - this.setLayout(new GridBagLayout()); - this.setBackground(ColorScheme.DARKER_GRAY_COLOR); - - final GridBagConstraints c = new GridBagConstraints(); - c.fill = GridBagConstraints.HORIZONTAL; - c.anchor = GridBagConstraints.NORTH; - c.weightx = 1; - c.gridx = 0; - c.gridy = 0; - c.ipady = 25; - - // main message - titleLabel.setFont(new Font(FontManager.getRunescapeFont().getName(), FontManager.getRunescapeFont().getStyle(), 32)); - titleLabel.setHorizontalAlignment(JLabel.CENTER); - titleLabel.setForeground(Color.WHITE); - this.add(titleLabel, c); - c.gridy++; - - // alternate message action - messageArea = new JLabel("

Open-source client for Old School RuneScape with more functionality and less restrictions.
") - { - @Override - public Dimension getPreferredSize() - { - final Dimension results = super.getPreferredSize(); - results.width = PANEL_SIZE.width - MESSAGE_AREA_PADDING; - return results; - } - }; - messageArea.setFont(new Font(FontManager.getRunescapeFont().getName(), FontManager.getRunescapeSmallFont().getStyle(), 16)); - messageArea.setForeground(Color.WHITE); - messageArea.setBorder(new EmptyBorder(0, MESSAGE_AREA_PADDING, 0, MESSAGE_AREA_PADDING)); - - scrollPane = new JScrollPane(messageArea, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); - scrollPane.setBorder(new EmptyBorder(0, 0, 0, 0)); - scrollPane.getVerticalScrollBar().setUI(new CustomScrollBarUI()); - final JViewport viewport = scrollPane.getViewport(); - viewport.setForeground(Color.WHITE); - viewport.setBackground(ColorScheme.DARKER_GRAY_COLOR); - viewport.setOpaque(true); - - c.weighty = 1; - c.fill = 1; - this.add(scrollPane, c); - c.gridy++; - - c.weighty = 0; - c.weightx = 1; - c.ipady = 5; - - barLabel.setFont(FontManager.getRunescapeFont()); - barLabel.setHorizontalAlignment(JLabel.CENTER); - barLabel.setForeground(Color.WHITE); - barLabel.setBorder(new EmptyBorder(5, 0, 5, 0)); - this.add(barLabel, c); - c.gridy++; - - bar.setBackground(ColorScheme.BRAND_BLUE_TRANSPARENT.darker()); - bar.setForeground(ColorScheme.BRAND_BLUE); - bar.setMinimumSize(BAR_SIZE); - bar.setMaximumSize(BAR_SIZE); - bar.setBorder(new MatteBorder(0, 0, 0, 0, Color.LIGHT_GRAY)); - bar.setUI(new BasicProgressBarUI() - { - protected Color getSelectionBackground() - { - return ColorScheme.DARKER_GRAY_COLOR; - } - - protected Color getSelectionForeground() - { - return ColorScheme.DARKER_GRAY_COLOR; - } - }); - bar.setFont(FontManager.getRunescapeFont()); - bar.setVisible(true); - this.add(bar, c); - c.gridy++; - } - - public void setMessageContent(String content) - { - if (!content.startsWith(""; - } - - messageArea.setText(content); - messageArea.revalidate(); - messageArea.repaint(); - } - - public void setMessageTitle(String text) - { - titleLabel.setText(text); - titleLabel.revalidate(); - titleLabel.repaint(); - } -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/MinimumSizedPanel.java b/runelite-client/src/main/java/net/runelite/client/ui/components/MinimumSizedPanel.java deleted file mode 100644 index 6ff6ba7705..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/ui/components/MinimumSizedPanel.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2019, Hydrox6 - * 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.components; - -import java.awt.Dimension; -import javax.swing.JPanel; - -public class MinimumSizedPanel extends JPanel -{ - @Override - public Dimension getPreferredSize() - { - final Dimension pref = super.getPreferredSize(); - final Dimension minimum = super.getMinimumSize(); - return new Dimension(Math.max(pref.width, minimum.width), Math.max(pref.height, minimum.height)); - } -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/PopupMenuOwner.java b/runelite-client/src/main/java/net/runelite/client/ui/components/PopupMenuOwner.java deleted file mode 100644 index ba5a019447..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/ui/components/PopupMenuOwner.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2018, Shingyx - * 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.components; - -import javax.swing.JPopupMenu; - -/** - * Represents a UI component which has a popup menu, to be used on child components inside DragAndDropReorderPane. - * This is because using setComponentPopupMenu consumes the MouseEvents required for drag and drop reordering. - */ -public interface PopupMenuOwner -{ - JPopupMenu getPopupMenu(); -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/ProgressBar.java b/runelite-client/src/main/java/net/runelite/client/ui/components/ProgressBar.java index 2204e47ead..54b2eec733 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/components/ProgressBar.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/ProgressBar.java @@ -51,9 +51,6 @@ public class ProgressBar extends DimmableJPanel @Setter private List positions = Collections.emptyList(); - @Setter - private int positionWidth = 1; - private final JLabel leftLabel = new JShadowedLabel(); private final JLabel rightLabel = new JShadowedLabel(); private final JLabel centerLabel = new JShadowedLabel(); @@ -69,26 +66,18 @@ public class ProgressBar extends DimmableJPanel setPreferredSize(new Dimension(100, 16)); - int topIndent = 0; - if (getFont().equals(FontManager.getRunescapeSmallFont()) - || getFont().equals(FontManager.getRunescapeFont()) - || getFont().equals(FontManager.getRunescapeBoldFont())) - { - topIndent = 2; - } - leftLabel.setFont(FontManager.getRunescapeSmallFont()); leftLabel.setForeground(Color.WHITE); - leftLabel.setBorder(new EmptyBorder(topIndent, 5, 0, 0)); + leftLabel.setBorder(new EmptyBorder(2, 5, 0, 0)); rightLabel.setFont(FontManager.getRunescapeSmallFont()); rightLabel.setForeground(Color.WHITE); - rightLabel.setBorder(new EmptyBorder(topIndent, 0, 0, 5)); + rightLabel.setBorder(new EmptyBorder(2, 0, 0, 5)); centerLabel.setFont(FontManager.getRunescapeSmallFont()); centerLabel.setForeground(Color.WHITE); centerLabel.setHorizontalAlignment(SwingConstants.CENTER); - centerLabel.setBorder(new EmptyBorder(topIndent, 0, 0, 0)); + centerLabel.setBorder(new EmptyBorder(2, 0, 0, 0)); // Adds components to be automatically redrawn when paintComponents is called add(leftLabel, BorderLayout.WEST); @@ -111,7 +100,7 @@ public class ProgressBar extends DimmableJPanel final int xCord = getSize().width * position / maximumValue; if (xCord > topWidth) { - g.fillRect(xCord, 0, positionWidth, 16); + g.fillRect(xCord, 0, 1, 16); } } @@ -159,7 +148,7 @@ public class ProgressBar extends DimmableJPanel centerLabel.setText(isDimmed() ? txt : centerLabelText); } - private int getPercentage() + public int getPercentage() { if (value == 0) { diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/SliderUI.java b/runelite-client/src/main/java/net/runelite/client/ui/components/SliderUI.java deleted file mode 100644 index 55b13f8411..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/ui/components/SliderUI.java +++ /dev/null @@ -1,300 +0,0 @@ -package net.runelite.client.ui.components; - -import java.awt.BasicStroke; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Rectangle; -import java.awt.RenderingHints; -import java.awt.Shape; -import java.awt.Stroke; -import java.awt.event.MouseEvent; -import java.awt.geom.Ellipse2D; -import javax.swing.JComponent; -import javax.swing.JSlider; -import javax.swing.SwingConstants; -import javax.swing.plaf.ComponentUI; -import javax.swing.plaf.basic.BasicSliderUI; -import net.runelite.client.ui.ColorScheme; - -public class SliderUI extends BasicSliderUI -{ - private final Color rangeColor = ColorScheme.BRAND_BLUE; - private final BasicStroke stroke = new BasicStroke(2f); - - private transient boolean upperDragging; - - public SliderUI(JSlider b) - { - super(b); - } - - public static ComponentUI createUI(JComponent c) - { - return new SliderUI((JSlider) c); - } - - @Override - protected void calculateThumbSize() - { - super.calculateThumbSize(); - thumbRect.setSize(thumbRect.width, thumbRect.height); - } - - /** - * Creates a listener to handle track events in the specified slider. - */ - @Override - protected TrackListener createTrackListener(JSlider slider) - { - return new RangeTrackListener(); - } - - @Override - protected void calculateThumbLocation() - { - // Call superclass method for lower thumb location. - super.calculateThumbLocation(); - - // Adjust upper value to snap to ticks if necessary. - if (slider.getSnapToTicks()) - { - int upperValue = slider.getValue() + slider.getExtent(); - int snappedValue = upperValue; - int majorTickSpacing = slider.getMajorTickSpacing(); - int minorTickSpacing = slider.getMinorTickSpacing(); - int tickSpacing = 0; - - if (minorTickSpacing > 0) - { - tickSpacing = minorTickSpacing; - } - else if (majorTickSpacing > 0) - { - tickSpacing = majorTickSpacing; - } - - if (tickSpacing != 0) - { - // If it's not on a tick, change the value - if ((upperValue - slider.getMinimum()) % tickSpacing != 0) - { - float temp = (float) (upperValue - slider.getMinimum()) / (float) tickSpacing; - int whichTick = Math.round(temp); - snappedValue = slider.getMinimum() + (whichTick * tickSpacing); - } - - if (snappedValue != upperValue) - { - slider.setExtent(snappedValue - slider.getValue()); - } - } - } - - // Calculate upper thumb location. The thumb is centered over its - // value on the track. - if (slider.getOrientation() == JSlider.HORIZONTAL) - { - int upperPosition = xPositionForValue(slider.getValue() + slider.getExtent()); - thumbRect.x = upperPosition - (thumbRect.width / 2); - thumbRect.y = trackRect.y; - - } - else - { - int upperPosition = yPositionForValue(slider.getValue() + slider.getExtent()); - thumbRect.x = trackRect.x; - thumbRect.y = upperPosition - (thumbRect.height / 2); - } - slider.repaint(); - } - - /** - * Returns the size of a thumb. - * Parent method not use size from LaF - * - * @return size of trumb - */ - @Override - protected Dimension getThumbSize() - { - return new Dimension(16, 16); - } - - private Shape createThumbShape(int width, int height) - { - return new Ellipse2D.Double(0, 0, width, height); - } - - @Override - public void paintTrack(Graphics g) - { - Graphics2D g2d = (Graphics2D) g; - Stroke old = g2d.getStroke(); - g2d.setStroke(stroke); - g2d.setPaint(ColorScheme.LIGHT_GRAY_COLOR); - Color oldColor = ColorScheme.LIGHT_GRAY_COLOR; - Rectangle trackBounds = trackRect; - if (slider.getOrientation() == SwingConstants.HORIZONTAL) - { - g2d.drawLine(trackRect.x, trackRect.y + trackRect.height / 2, - trackRect.x + trackRect.width, trackRect.y + trackRect.height / 2); - int lowerX = thumbRect.width / 2; - int upperX = thumbRect.x + (thumbRect.width / 2); - int cy = (trackBounds.height / 2) - 2; - g2d.translate(trackBounds.x, trackBounds.y + cy); - g2d.setColor(rangeColor); - g2d.drawLine(lowerX - trackBounds.x, 2, upperX - trackBounds.x, 2); - g2d.translate(-trackBounds.x, -(trackBounds.y + cy)); - g2d.setColor(oldColor); - } - g2d.setStroke(old); - } - - /** - * Overrides superclass method to do nothing. Thumb painting is handled - * within the paint() method. - */ - @Override - public void paintThumb(Graphics g) - { - Rectangle knobBounds = thumbRect; - int w = knobBounds.width; - int h = knobBounds.height; - Graphics2D g2d = (Graphics2D) g.create(); - Shape thumbShape = createThumbShape(w - 1, h - 1); - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); - g2d.translate(knobBounds.x, knobBounds.y); - g2d.setColor(ColorScheme.BRAND_BLUE); - g2d.fill(thumbShape); - - g2d.setColor(ColorScheme.DARK_GRAY_COLOR); - g2d.draw(thumbShape); - g2d.dispose(); - } - - /** - * Listener to handle mouse movements in the slider track. - */ - public class RangeTrackListener extends TrackListener - { - @Override - public void mouseClicked(MouseEvent e) - { - if (!slider.isEnabled()) - { - return; - } - currentMouseX -= thumbRect.width / 2; // Because we want the mouse location correspond to middle of the "thumb", not left side of it. - moveUpperThumb(); - } - - public void mousePressed(MouseEvent e) - { - if (!slider.isEnabled()) - { - return; - } - - currentMouseX = e.getX(); - currentMouseY = e.getY(); - - if (slider.isRequestFocusEnabled()) - { - slider.requestFocus(); - } - - boolean upperPressed = false; - if (thumbRect.contains(currentMouseX, currentMouseY)) - { - upperPressed = true; - } - - if (upperPressed) - { - switch (slider.getOrientation()) - { - case JSlider.VERTICAL: - offset = currentMouseY - thumbRect.y; - break; - case JSlider.HORIZONTAL: - offset = currentMouseX - thumbRect.x; - break; - } - //upperThumbSelected = true; - upperDragging = true; - return; - } - - upperDragging = false; - } - - @Override - public void mouseReleased(MouseEvent e) - { - upperDragging = false; - slider.setValueIsAdjusting(false); - super.mouseReleased(e); - } - - @Override - public void mouseDragged(MouseEvent e) - { - if (!slider.isEnabled()) - { - return; - } - - currentMouseX = e.getX(); - currentMouseY = e.getY(); - - if (upperDragging) - { - slider.setValueIsAdjusting(true); - moveUpperThumb(); - - } - } - - @Override - public boolean shouldScroll(int direction) - { - return false; - } - - /** - * Moves the location of the upper thumb, and sets its corresponding value in the slider. - */ - public void moveUpperThumb() - { - int thumbMiddle; - if (slider.getOrientation() == JSlider.HORIZONTAL) - { - int halfThumbWidth = thumbRect.width / 2; - int thumbLeft = currentMouseX - offset; - int trackLeft = trackRect.x; - int trackRight = trackRect.x + (trackRect.width - 1); - int hMax = xPositionForValue(slider.getMaximum() - - slider.getExtent()); - - if (drawInverted()) - { - trackLeft = hMax; - } - else - { - trackRight = hMax; - } - thumbLeft = Math.max(thumbLeft, trackLeft - halfThumbWidth); - thumbLeft = Math.min(thumbLeft, trackRight - halfThumbWidth); - - setThumbLocation(thumbLeft, thumbRect.y);//setThumbLocation - - thumbMiddle = thumbLeft + halfThumbWidth; - slider.setValue(valueForXPosition(thumbMiddle)); - } - } - } -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/ThinProgressBar.java b/runelite-client/src/main/java/net/runelite/client/ui/components/ThinProgressBar.java index 80c5207e88..4b9b798c81 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/components/ThinProgressBar.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/ThinProgressBar.java @@ -54,7 +54,7 @@ public class ThinProgressBar extends JPanel public double getPercentage() { - return (value * 100) / (double) maximumValue; + return (value * 100) / maximumValue; } @Override diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/colorpicker/ColorPanel.java b/runelite-client/src/main/java/net/runelite/client/ui/components/colorpicker/ColorPanel.java index 036de05ae2..8394093fe7 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/components/colorpicker/ColorPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/colorpicker/ColorPanel.java @@ -25,6 +25,7 @@ */ package net.runelite.client.ui.components.colorpicker; +import com.google.common.primitives.Ints; import java.awt.Color; import java.awt.Dimension; import java.awt.GradientPaint; @@ -38,7 +39,6 @@ import java.awt.image.BufferedImage; import java.util.function.Consumer; import javax.swing.JPanel; import lombok.Setter; -import net.runelite.client.util.MiscUtils; public class ColorPanel extends JPanel { @@ -144,8 +144,8 @@ public class ColorPanel extends JPanel return; } - x = MiscUtils.clamp(x, 0, size - 1); - y = MiscUtils.clamp(y, 0, size - 1); + x = Ints.constrainToRange(x, 0, size - 1); + y = Ints.constrainToRange(y, 0, size - 1); targetPosition = new Point(x, y); paintImmediately(0, 0, size, size); @@ -200,8 +200,8 @@ public class ColorPanel extends JPanel */ private Color colorAt(int x, int y) { - x = MiscUtils.clamp(x, 0, size - 1); - y = MiscUtils.clamp(y, 0, size - 1); + x = Ints.constrainToRange(x, 0, size - 1); + y = Ints.constrainToRange(y, 0, size - 1); return new Color(image.getRGB(x, y)); } } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/colorpicker/ColorValuePanel.java b/runelite-client/src/main/java/net/runelite/client/ui/components/colorpicker/ColorValuePanel.java index 43f549143b..9dddff5bb7 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/components/colorpicker/ColorValuePanel.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/colorpicker/ColorValuePanel.java @@ -43,7 +43,7 @@ import javax.swing.text.DocumentFilter; import net.runelite.client.ui.ColorScheme; import net.runelite.client.util.ColorUtil; -class ColorValuePanel extends JPanel +public class ColorValuePanel extends JPanel { private static final int DEFAULT_VALUE = ColorUtil.MAX_RGB_VALUE; diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/colorpicker/ColorValueSlider.java b/runelite-client/src/main/java/net/runelite/client/ui/components/colorpicker/ColorValueSlider.java index 97ef6252d9..05d74684ec 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/components/colorpicker/ColorValueSlider.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/colorpicker/ColorValueSlider.java @@ -25,6 +25,7 @@ */ package net.runelite.client.ui.components.colorpicker; +import com.google.common.primitives.Ints; import java.awt.Color; import java.awt.Graphics; import java.awt.event.MouseAdapter; @@ -34,7 +35,6 @@ import java.util.function.Consumer; import javax.swing.JPanel; import lombok.Setter; import net.runelite.client.util.ColorUtil; -import net.runelite.client.util.MiscUtils; public class ColorValueSlider extends JPanel { @@ -83,7 +83,7 @@ public class ColorValueSlider extends JPanel private void moveTarget(int x, boolean shouldUpdate) { - value = MiscUtils.clamp(x, ColorUtil.MIN_RGB_VALUE + KNOB_WIDTH, ColorUtil.MAX_RGB_VALUE + KNOB_WIDTH); + value = Ints.constrainToRange(x, ColorUtil.MIN_RGB_VALUE + KNOB_WIDTH, ColorUtil.MAX_RGB_VALUE + KNOB_WIDTH); paintImmediately(0, 0, this.getWidth(), this.getHeight()); if (shouldUpdate && onValueChanged != null) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/colorpicker/HuePanel.java b/runelite-client/src/main/java/net/runelite/client/ui/components/colorpicker/HuePanel.java index b7689b5e47..392255daa7 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/components/colorpicker/HuePanel.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/colorpicker/HuePanel.java @@ -25,6 +25,7 @@ */ package net.runelite.client.ui.components.colorpicker; +import com.google.common.primitives.Ints; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; @@ -35,7 +36,6 @@ import java.util.function.Consumer; import javax.swing.JPanel; import lombok.Getter; import lombok.Setter; -import net.runelite.client.util.MiscUtils; public class HuePanel extends JPanel { @@ -94,7 +94,7 @@ public class HuePanel extends JPanel */ private void moveSelector(int y) { - y = MiscUtils.clamp(y, 0, height - 1); + y = Ints.constrainToRange(y, 0, height - 1); if (y == this.selectedY) { return; diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/colorpicker/RecentColors.java b/runelite-client/src/main/java/net/runelite/client/ui/components/colorpicker/RecentColors.java index 6a38e49da5..fcb69a3198 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/components/colorpicker/RecentColors.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/colorpicker/RecentColors.java @@ -35,10 +35,10 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.function.Consumer; import javax.swing.JPanel; -import net.runelite.api.util.Text; import net.runelite.client.config.ConfigManager; import static net.runelite.client.ui.components.colorpicker.RuneliteColorPicker.CONFIG_GROUP; import net.runelite.client.util.ColorUtil; +import net.runelite.client.util.Text; final class RecentColors { diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/colorpicker/RuneliteColorPicker.java b/runelite-client/src/main/java/net/runelite/client/ui/components/colorpicker/RuneliteColorPicker.java index 55913e5a2a..c32274e9ed 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/components/colorpicker/RuneliteColorPicker.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/colorpicker/RuneliteColorPicker.java @@ -94,7 +94,7 @@ public class RuneliteColorPicker extends JDialog private Consumer onClose; RuneliteColorPicker(Window parent, Color previousColor, String title, boolean alphaHidden, - final ConfigManager configManager, final ColorPickerManager colorPickerManager) + final ConfigManager configManager, final ColorPickerManager colorPickerManager) { super(parent, "RuneLite Color Picker - " + title, ModalityType.MODELESS); diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/materialtabs/MaterialTab.java b/runelite-client/src/main/java/net/runelite/client/ui/components/materialtabs/MaterialTab.java index 19fb3fc175..9ba4fe0463 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/components/materialtabs/MaterialTab.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/materialtabs/MaterialTab.java @@ -51,7 +51,7 @@ import net.runelite.client.ui.ColorScheme; public class MaterialTab extends JLabel { private static final Border SELECTED_BORDER = new CompoundBorder( - BorderFactory.createMatteBorder(0, 0, 1, 0, ColorScheme.BRAND_BLUE), + BorderFactory.createMatteBorder(0, 0, 1, 0, ColorScheme.BRAND_ORANGE), BorderFactory.createEmptyBorder(5, 10, 4, 10)); private static final Border UNSELECTED_BORDER = BorderFactory diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/materialtabs/MaterialTabGroup.java b/runelite-client/src/main/java/net/runelite/client/ui/components/materialtabs/MaterialTabGroup.java index b106428a96..e98b520fdc 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/components/materialtabs/MaterialTabGroup.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/materialtabs/MaterialTabGroup.java @@ -75,7 +75,7 @@ public class MaterialTabGroup extends JPanel public MaterialTab getTab(int index) { - if (tabs.isEmpty()) + if (tabs == null || tabs.isEmpty()) { return null; } @@ -87,18 +87,6 @@ public class MaterialTabGroup extends JPanel { tabs.add(tab); add(tab, BorderLayout.NORTH); - - invalidate(); - repaint(); - } - - public void removeTab(MaterialTab tab) - { - tabs.remove(tab); - remove(tab); - - invalidate(); - repaint(); } /*** @@ -139,11 +127,4 @@ public class MaterialTabGroup extends JPanel return true; } - - @Override - public void removeAll() - { - super.removeAll(); - tabs.clear(); - } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/shadowlabel/JShadowedLabel.java b/runelite-client/src/main/java/net/runelite/client/ui/components/shadowlabel/JShadowedLabel.java index 8132cb3f7e..35ccf12374 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/components/shadowlabel/JShadowedLabel.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/shadowlabel/JShadowedLabel.java @@ -25,11 +25,7 @@ package net.runelite.client.ui.components.shadowlabel; import java.awt.Color; -import java.awt.Graphics; -import java.awt.Graphics2D; import java.awt.Point; -import java.awt.Toolkit; -import java.util.Map; import javax.swing.JLabel; import lombok.Getter; @@ -65,17 +61,4 @@ public class JShadowedLabel extends JLabel revalidate(); repaint(); } - - @Override - public void paint(Graphics g) - { - // Set font rendering properties like the OS's font rendering - Toolkit tk = Toolkit.getDefaultToolkit(); - Map desktopHints = (Map) (tk.getDesktopProperty("awt.font.desktophints")); - if (desktopHints != null) - { - ((Graphics2D) g).addRenderingHints(desktopHints); - } - super.paint(g); - } } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/Overlay.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/Overlay.java index 60ed8d84a7..405fe28080 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/Overlay.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/Overlay.java @@ -66,7 +66,7 @@ public abstract class Overlay implements LayoutableRenderableEntity plugin = null; } - protected Overlay(@Nullable Plugin plugin) + protected Overlay(Plugin plugin) { this.plugin = plugin; } @@ -80,7 +80,7 @@ public abstract class Overlay implements LayoutableRenderableEntity { return this.getClass().getSimpleName(); } - + public void onMouseOver() { } @@ -97,4 +97,15 @@ public abstract class Overlay implements LayoutableRenderableEntity { return false; } + + /** + * Get the parent bounds for overlay dragging. The overlay will + * not be allowed to be moved outside of the parent bounds. + * @return + */ + @Nullable + public Rectangle getParentBounds() + { + return null; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayLayer.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayLayer.java index ca91721b4f..ac75932ad6 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayLayer.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayLayer.java @@ -50,9 +50,4 @@ public enum OverlayLayer * Render over the map, even when it's fullscreen */ ABOVE_MAP, - - /** - * Render overlay after mirror is produced - */ - AFTER_MIRROR, } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayManager.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayManager.java index 799b3f2b3f..b64f845695 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayManager.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayManager.java @@ -33,20 +33,19 @@ import java.util.Comparator; import java.util.EnumMap; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.function.Predicate; import javax.inject.Inject; import javax.inject.Singleton; import lombok.AccessLevel; import lombok.Getter; -import net.runelite.api.MenuOpcode; +import net.runelite.api.MenuAction; import net.runelite.api.events.MenuOptionClicked; -import net.runelite.api.widgets.WidgetInfo; import net.runelite.api.widgets.WidgetItem; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigManager; import net.runelite.client.config.RuneLiteConfig; import net.runelite.client.eventbus.EventBus; +import net.runelite.client.eventbus.Subscribe; import net.runelite.client.events.ConfigChanged; import net.runelite.client.events.OverlayMenuClicked; import net.runelite.client.events.PluginChanged; @@ -113,12 +112,9 @@ public class OverlayManager this.configManager = configManager; this.eventBus = eventBus; this.runeLiteConfig = runeLiteConfig; - - eventBus.subscribe(PluginChanged.class, this, this::onPluginChanged); - eventBus.subscribe(MenuOptionClicked.class, this, this::onMenuOptionClicked); - eventBus.subscribe(ConfigChanged.class, this, this::onConfigChanged); } + @Subscribe public void onConfigChanged(final ConfigChanged event) { if (!RuneLiteConfig.GROUP_NAME.equals(event.getGroup()) || !"overlayBackgroundColor".equals(event.getKey())) @@ -129,30 +125,36 @@ public class OverlayManager overlays.forEach(this::updateOverlayConfig); } - private void onPluginChanged(final PluginChanged event) + @Subscribe + public void onPluginChanged(final PluginChanged event) { overlays.forEach(this::loadOverlay); rebuildOverlayLayers(); } - private void onMenuOptionClicked(MenuOptionClicked event) + @Subscribe + public void onMenuOptionClicked(MenuOptionClicked event) { - MenuOpcode menuOpcode = event.getMenuOpcode(); - if (menuOpcode != MenuOpcode.RUNELITE_OVERLAY && menuOpcode != MenuOpcode.RUNELITE_OVERLAY_CONFIG) + MenuAction menuAction = event.getMenuAction(); + if (menuAction != MenuAction.RUNELITE_OVERLAY && menuAction != MenuAction.RUNELITE_OVERLAY_CONFIG) { return; } event.consume(); - Overlay overlay = overlays.get(event.getIdentifier()); + Overlay overlay = overlays.get(event.getId()); if (overlay != null) { List menuEntries = overlay.getMenuEntries(); - Optional optionalOverlayMenuEntry = menuEntries.stream() - .filter(me -> me.getOption().equals(event.getOption())) - .findAny(); - optionalOverlayMenuEntry.ifPresent(overlayMenuEntry -> eventBus.post(OverlayMenuClicked.class, new OverlayMenuClicked(overlayMenuEntry, overlay))); + OverlayMenuEntry overlayMenuEntry = menuEntries.stream() + .filter(me -> me.getOption().equals(event.getMenuOption())) + .findAny() + .orElse(null); + if (overlayMenuEntry != null) + { + eventBus.post(new OverlayMenuClicked(overlayMenuEntry, overlay)); + } } } @@ -399,21 +401,4 @@ public class OverlayManager final String locationKey = overlay.getName() + OVERLAY_CONFIG_PREFERRED_POSITION; return configManager.getConfiguration(RUNELITE_CONFIG_GROUP_NAME, locationKey, OverlayPosition.class); } - - public WidgetOverlay getWidgetOverlay(final WidgetInfo info) - { - for (Overlay o : overlays) - { - if (o instanceof WidgetOverlay) - { - WidgetOverlay overlay = (WidgetOverlay) o; - if (overlay.getWidgetInfo().equals(info)) - { - return overlay; - } - } - } - - return null; - } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayMenuEntry.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayMenuEntry.java index 9dd7f856c2..f952f37713 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayMenuEntry.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayMenuEntry.java @@ -25,14 +25,12 @@ package net.runelite.client.ui.overlay; import lombok.Value; -import lombok.experimental.NonFinal; -import net.runelite.api.MenuOpcode; +import net.runelite.api.MenuAction; @Value -@NonFinal public class OverlayMenuEntry { - private MenuOpcode menuOpcode; + private MenuAction menuAction; private String option; private String target; } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java index 4f9c9f3738..6b6c01add2 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java @@ -24,6 +24,7 @@ */ package net.runelite.client.ui.overlay; +import com.google.common.base.MoreObjects; import com.google.common.primitives.Ints; import java.awt.Color; import java.awt.Composite; @@ -35,17 +36,13 @@ import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Stroke; -import java.awt.Toolkit; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.awt.geom.AffineTransform; import java.util.List; -import java.util.Map; -import java.util.Objects; import javax.inject.Inject; import javax.inject.Singleton; import javax.swing.SwingUtilities; -import lombok.Setter; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; import net.runelite.api.GameState; @@ -57,7 +54,7 @@ import net.runelite.api.events.FocusChanged; import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetInfo; import net.runelite.client.config.RuneLiteConfig; -import net.runelite.client.eventbus.EventBus; +import net.runelite.client.eventbus.Subscribe; import net.runelite.client.input.KeyListener; import net.runelite.client.input.KeyManager; import net.runelite.client.input.MouseAdapter; @@ -65,7 +62,6 @@ import net.runelite.client.input.MouseManager; import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.JagexColors; import net.runelite.client.util.ColorUtil; -import net.runelite.client.util.MiscUtils; @Singleton @Slf4j @@ -107,9 +103,6 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener private boolean isResizeable; private OverlayBounds snapCorners; - @Setter - private boolean shouldRender = true; - @Inject private OverlayRenderer( final Client client, @@ -117,7 +110,6 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener final RuneLiteConfig runeLiteConfig, final MouseManager mouseManager, final KeyManager keyManager, - final EventBus eventbus, final ClientUI clientUI) { this.client = client; @@ -126,13 +118,10 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener this.clientUI = clientUI; keyManager.registerKeyListener(this); mouseManager.registerMouseListener(this); - - eventbus.subscribe(FocusChanged.class, this, this::onFocusChanged); - eventbus.subscribe(ClientTick.class, this, this::onClientTick); - eventbus.subscribe(BeforeRender.class, this, this::onBeforeRender); } - private void onFocusChanged(FocusChanged event) + @Subscribe + public void onFocusChanged(FocusChanged event) { if (!event.isFocused()) { @@ -146,7 +135,8 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener } } - private void onClientTick(ClientTick t) + @Subscribe + protected void onClientTick(ClientTick t) { if (menuEntries == null) { @@ -173,17 +163,14 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener client.setMenuEntries(newEntries); } - private void onBeforeRender(BeforeRender event) + @Subscribe + public void onBeforeRender(BeforeRender event) { menuEntries = null; } public void render(Graphics2D graphics, final OverlayLayer layer) { - if (!shouldRender) - { - return; - } if (layer != OverlayLayer.ABOVE_MAP && client.getWidget(WidgetInfo.FULLSCREEN_MAP_ROOT) != null && !client.getWidget(WidgetInfo.FULLSCREEN_MAP_ROOT).isHidden()) @@ -202,14 +189,6 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener return; } - // Set font rendering properties like the OS's font rendering - Toolkit tk = Toolkit.getDefaultToolkit(); - Map desktopHints = (Map) (tk.getDesktopProperty("awt.font.desktophints")); - if (desktopHints != null) - { - graphics.addRenderingHints(desktopHints); - } - if (shouldInvalidateBounds()) { snapCorners = buildSnapCorners(); @@ -267,11 +246,14 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener } else { - final Point location = overlay.getBounds().getLocation(); - final Dimension dimension = overlay.getBounds().getSize(); + final Rectangle bounds = overlay.getBounds(); + final Point location = bounds.getLocation(); + final Dimension dimension = bounds.getSize(); + + final Point preferredLocation = overlay.getPreferredLocation(); // If the final position is not modified, layout it - if (overlayPosition != OverlayPosition.DETACHED && (overlay.getPreferredLocation() == null || overlay.getPreferredPosition() != null)) + if (overlayPosition != OverlayPosition.DETACHED && (preferredLocation == null || overlay.getPreferredPosition() != null)) { final Rectangle snapCorner = snapCorners.forPosition(overlayPosition); final Point translation = OverlayUtil.transformPosition(overlayPosition, dimension); @@ -281,21 +263,18 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener } else { - final Point preferredLocation = overlay.getPreferredLocation(); - if (preferredLocation != null) { location.setLocation(preferredLocation); } - final Dimension realDimensions = client.getRealDimensions(); - location.x = MiscUtils.clamp(location.x, 0, Math.max(0, realDimensions.width - dimension.width)); - location.y = MiscUtils.clamp(location.y, 0, Math.max(0, realDimensions.height - dimension.height)); + // Clamp the overlay position to ensure it is on screen or within parent bounds + clampOverlayLocation(location, dimension.width, dimension.height, overlay); } if (overlay.getPreferredSize() != null) { - overlay.getBounds().setSize(overlay.getPreferredSize()); + bounds.setSize(overlay.getPreferredSize()); } safeRender(client, overlay, layer, graphics, location); @@ -308,8 +287,6 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener graphics.setRenderingHints(renderingHints); graphics.setBackground(background); - final Rectangle bounds = overlay.getBounds(); - if (!bounds.isEmpty()) { if (inOverlayManagingMode) @@ -340,7 +317,7 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener graphics.setPaint(paint); } - if (!client.isMenuOpen() && !client.isSpellSelected() && bounds.contains(mouse)) + if (!client.isMenuOpen() && !client.getSpellSelected() && bounds.contains(mouse)) { if (menuEntries == null) { @@ -365,6 +342,9 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener final Point mousePoint = mouseEvent.getPoint(); mousePosition.setLocation(mousePoint); + // See if we've clicked on an overlay + currentManagedOverlay = findMangedOverlay(mousePoint); + if (currentManagedOverlay == null) { return mouseEvent; @@ -410,26 +390,7 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener if (!inOverlayResizingMode && !inOverlayDraggingMode) { - currentManagedOverlay = null; - - synchronized (overlayManager) - { - for (Overlay overlay : overlayManager.getOverlays()) - { - if (overlay.getPosition() == OverlayPosition.DYNAMIC || overlay.getPosition() == OverlayPosition.TOOLTIP) - { - // never allow moving dynamic or tooltip overlays - continue; - } - - final Rectangle bounds = overlay.getBounds(); - if (bounds.contains(mousePoint)) - { - currentManagedOverlay = overlay; - break; - } - } - } + currentManagedOverlay = findMangedOverlay(mousePoint); } if (currentManagedOverlay == null || !currentManagedOverlay.isResizable()) @@ -476,6 +437,33 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener return mouseEvent; } + /** + * Find an overlay to manage which is under the given mouse point + * @param mousePoint + * @return + */ + private Overlay findMangedOverlay(Point mousePoint) + { + synchronized (overlayManager) + { + for (Overlay overlay : overlayManager.getOverlays()) + { + if (overlay.getPosition() == OverlayPosition.DYNAMIC || overlay.getPosition() == OverlayPosition.TOOLTIP) + { + // never allow moving dynamic or tooltip overlays + continue; + } + + final Rectangle bounds = overlay.getBounds(); + if (bounds.contains(mousePoint)) + { + return overlay; + } + } + } + return null; + } + @Override public MouseEvent mouseDragged(MouseEvent mouseEvent) { @@ -593,12 +581,14 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener } else if (inOverlayDraggingMode) { - final Dimension realDimension = client.getRealDimensions(); - p.translate(-overlayOffset.x, -overlayOffset.y); - p.x = Ints.constrainToRange(p.x, 0, Math.max(0, realDimension.width - currentManagedOverlay.getBounds().width)); - p.y = Ints.constrainToRange(p.y, 0, Math.max(0, realDimension.height - currentManagedOverlay.getBounds().height)); + Point overlayPosition = new Point(p); + overlayPosition.translate(-overlayOffset.x, -overlayOffset.y); // adjust by mouse offset to get overlay position + + // Clamp drag to parent component + final Rectangle overlayBounds = currentManagedOverlay.getBounds(); + clampOverlayLocation(overlayPosition, overlayBounds.width, overlayBounds.height, currentManagedOverlay); currentManagedOverlay.setPreferredPosition(null); - currentManagedOverlay.setPreferredLocation(p); + currentManagedOverlay.setPreferredLocation(overlayPosition); } else { @@ -612,6 +602,7 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener startedMovingOverlay = false; } + mouseEvent.consume(); return mouseEvent; } @@ -729,17 +720,11 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener } catch (Exception ex) { - log.warn("Exception during overlay rendering", ex); - return; - } - catch (Error er) - { - log.warn("Error during overlay rendering, removing overlay", er); - overlayManager.remove(overlay); + log.warn("Error during overlay rendering", ex); return; } - final Dimension dimension = Objects.requireNonNullElse(overlayDimension, new Dimension()); + final Dimension dimension = MoreObjects.firstNonNull(overlayDimension, new Dimension()); overlay.getBounds().setSize(dimension); } @@ -863,7 +848,7 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener bottomLeftPoint.y) : bottomRightPoint; final Point canvasTopRightPoint = isResizeable ? new Point( - (int) client.getRealDimensions().getWidth(), + (int)client.getRealDimensions().getWidth(), 0) : topRightPoint; return new OverlayBounds( @@ -894,7 +879,7 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener final MenuEntry entry = new MenuEntry(); entry.setOption(overlayMenuEntry.getOption()); entry.setTarget(ColorUtil.wrapWithColorTag(overlayMenuEntry.getTarget(), JagexColors.MENU_TARGET)); - entry.setOpcode(overlayMenuEntry.getMenuOpcode().getId()); + entry.setType(overlayMenuEntry.getMenuAction().getId()); entry.setIdentifier(overlayManager.getOverlays().indexOf(overlay)); // overlay id entries[i] = entry; @@ -902,4 +887,29 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener return entries; } + + /** + * Adjust the given overlay position to be within its parent's bounds. + * + * @param overlayPosition the overlay position, which is modified in place + * @param overlayWidth + * @param overlayHeight + * @param overlay the overlay + */ + private void clampOverlayLocation(Point overlayPosition, int overlayWidth, int overlayHeight, Overlay overlay) + { + Rectangle parentBounds = overlay.getParentBounds(); + if (parentBounds == null || parentBounds.isEmpty()) + { + // If no bounds are set, use the full client bounds + Dimension dim = client.getRealDimensions(); + parentBounds = new Rectangle(0, 0, dim.width, dim.height); + } + + // Constrain overlay position to be within the parent bounds + overlayPosition.x = Ints.constrainToRange(overlayPosition.x, parentBounds.x, + Math.max(parentBounds.x, parentBounds.width - overlayWidth)); + overlayPosition.y = Ints.constrainToRange(overlayPosition.y, parentBounds.y, + Math.max(parentBounds.y, parentBounds.height - overlayHeight)); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayUtil.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayUtil.java index 041b6be392..912abab8f4 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayUtil.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayUtil.java @@ -28,29 +28,18 @@ import com.google.common.base.Strings; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Dimension; -import java.awt.Font; import java.awt.Graphics2D; import java.awt.Polygon; -import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.Stroke; import java.awt.image.BufferedImage; -import java.util.List; import net.runelite.api.Actor; import net.runelite.api.Client; -import net.runelite.api.NPC; -import net.runelite.api.NPCDefinition; import net.runelite.api.Perspective; import net.runelite.api.Point; -import net.runelite.api.Prayer; import net.runelite.api.TileObject; -import net.runelite.api.VarClientInt; import net.runelite.api.coords.LocalPoint; -import net.runelite.api.coords.WorldArea; -import net.runelite.api.coords.WorldPoint; -import net.runelite.api.vars.InterfaceTab; -import net.runelite.api.widgets.Widget; /** @@ -72,60 +61,6 @@ public class OverlayUtil graphics.setStroke(originalStroke); } - public static void renderOutlinePolygon(Graphics2D graphics, Shape poly, Color color) - { - graphics.setColor(color); - final Stroke originalStroke = graphics.getStroke(); - graphics.setStroke(new BasicStroke(2)); - graphics.draw(poly); - graphics.setStroke(originalStroke); - } - - public static void renderFilledPolygon(Graphics2D graphics, Shape poly, Color color) - { - graphics.setColor(color); - final Stroke originalStroke = graphics.getStroke(); - graphics.setStroke(new BasicStroke(2)); - graphics.draw(poly); - graphics.fill(poly); - graphics.setStroke(originalStroke); - } - - public static void renderAreaTilePolygon(Graphics2D graphics, Shape poly, Color color) - { - graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), 10)); - graphics.fill(poly); - } - - public static void renderFullLine(Graphics2D graphics, int[][] line, Color color) - { - graphics.setColor(color); - final Stroke originalStroke = graphics.getStroke(); - graphics.setStroke(new BasicStroke(2)); - graphics.drawLine(line[0][0], line[0][1], line[1][0], line[1][1]); - graphics.setStroke(originalStroke); - } - - public static void renderDashedLine(Graphics2D graphics, int[][] line, Color color) - { - graphics.setColor(color); - final Stroke originalStroke = graphics.getStroke(); - graphics.setStroke(new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[]{9}, 0)); - graphics.drawLine(line[0][0], line[0][1], line[1][0], line[1][1]); - graphics.setStroke(originalStroke); - } - - public static void renderPolygonThin(Graphics2D graphics, Polygon poly, Color color) - { - graphics.setColor(color); - final Stroke originalStroke = graphics.getStroke(); - graphics.setStroke(new BasicStroke(1)); - graphics.drawPolygon(poly); - graphics.setColor(new Color(0, 0, 0, 50)); - graphics.fillPolygon(poly); - graphics.setStroke(originalStroke); - } - public static void renderMinimapLocation(Graphics2D graphics, Point mini, Color color) { graphics.setColor(Color.BLACK); @@ -141,7 +76,7 @@ public class OverlayUtil graphics.setColor(color); graphics.rotate(angle, center.getX(), center.getY()); graphics.drawRect(center.getX() - width / 2, center.getY() - height / 2, width, height); - graphics.rotate(-angle, center.getX(), center.getY()); + graphics.rotate(-angle , center.getX(), center.getY()); } public static void renderTextLocation(Graphics2D graphics, Point txtLoc, String text, Color color) @@ -193,20 +128,6 @@ public class OverlayUtil } } - public static void renderActorTextOverlay(Graphics2D graphics, Actor actor, String text, Color color) - { - renderActorTextOverlay(graphics, actor, text, color, 40); - } - - public static void renderActorTextOverlay(Graphics2D graphics, Actor actor, String text, Color color, int offset) - { - Point textLocation = actor.getCanvasTextLocation(graphics, text, actor.getLogicalHeight() + offset); - if (textLocation != null) - { - renderTextLocation(graphics, textLocation, text, color); - } - } - public static void renderActorOverlayImage(Graphics2D graphics, Actor actor, BufferedImage image, Color color, int zOffset) { Polygon poly = actor.getCanvasTilePoly(); @@ -295,6 +216,8 @@ public class OverlayUtil break; case TOP_LEFT: case TOP_CENTER: + result.y += dimension.height + (dimension.height == 0 ? 0 : padding); + break; case CANVAS_TOP_RIGHT: case TOP_RIGHT: result.y += dimension.height + (dimension.height == 0 ? 0 : padding); @@ -335,156 +258,4 @@ public class OverlayUtil return result; } - - public static void renderActorTextAndImage(Graphics2D graphics, Actor actor, String text, Color color, BufferedImage image, int yOffset, int xOffset) - { - Point textLocation = actor.getCanvasTextLocation(graphics, text, actor.getLogicalHeight() + yOffset); - - if (textLocation != null) - { - renderImageLocation(graphics, textLocation, image); - textLocation = new Point(textLocation.getX() + xOffset, textLocation.getY()); - renderTextLocation(graphics, textLocation, text, color); - } - } - - public static void renderTextLocation(Graphics2D graphics, String txtString, int fontSize, int fontStyle, Color fontColor, Point canvasPoint, boolean shadows, int yOffset) - { - graphics.setFont(new Font("Arial", fontStyle, fontSize)); - if (canvasPoint != null) - { - final Point canvasCenterPoint = new Point( - canvasPoint.getX(), - canvasPoint.getY() + yOffset); - final Point canvasCenterPoint_shadow = new Point( - canvasPoint.getX() + 1, - canvasPoint.getY() + 1 + yOffset); - if (shadows) - { - renderTextLocation(graphics, canvasCenterPoint_shadow, txtString, Color.BLACK); - } - renderTextLocation(graphics, canvasCenterPoint, txtString, fontColor); - } - } - - public static void renderClickBox(Graphics2D graphics, Point mousePosition, Shape objectClickbox, Color configColor) - { - if (objectClickbox.contains(mousePosition.getX(), mousePosition.getY())) - { - graphics.setColor(configColor.darker()); - } - else - { - graphics.setColor(configColor); - } - - graphics.draw(objectClickbox); - graphics.setColor(new Color(configColor.getRed(), configColor.getGreen(), configColor.getBlue(), 50)); - graphics.fill(objectClickbox); - } - - public static void drawTiles(Graphics2D graphics, Client client, WorldPoint point, WorldPoint playerPoint, Color color, int strokeWidth, int outlineAlpha, int fillAlpha) - { - if (point.distanceTo(playerPoint) >= 32) - { - return; - } - LocalPoint lp = LocalPoint.fromWorld(client, point); - if (lp == null) - { - return; - } - - Polygon poly = Perspective.getCanvasTilePoly(client, lp); - if (poly == null) - { - return; - } - drawStrokeAndFillPoly(graphics, color, strokeWidth, outlineAlpha, fillAlpha, poly); - } - - public static void drawStrokeAndFillPoly(Graphics2D graphics, Color color, int strokeWidth, int outlineAlpha, int fillAlpha, Polygon poly) - { - graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), outlineAlpha)); - graphics.setStroke(new BasicStroke(strokeWidth)); - graphics.draw(poly); - graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), fillAlpha)); - graphics.fill(poly); - } - - public static Rectangle renderPrayerOverlay(Graphics2D graphics, Client client, Prayer prayer, Color color) - { - Widget widget = client.getWidget(prayer.getWidgetInfo()); - - if (widget == null || client.getVar(VarClientInt.INTERFACE_TAB) != InterfaceTab.PRAYER.getId()) - { - return null; - } - - Rectangle bounds = widget.getBounds(); - renderPolygon(graphics, rectangleToPolygon(bounds), color); - return bounds; - } - - private static Polygon rectangleToPolygon(Rectangle rect) - { - int[] xpoints = {rect.x, rect.x + rect.width, rect.x + rect.width, rect.x}; - int[] ypoints = {rect.y, rect.y, rect.y + rect.height, rect.y + rect.height}; - - return new Polygon(xpoints, ypoints, 4); - } - - public static List getHitSquares(WorldPoint npcLoc, int npcSize, int thickness, boolean includeUnder) - { - List little = new WorldArea(npcLoc, npcSize, npcSize).toWorldPointList(); - List big = new WorldArea(npcLoc.getX() - thickness, npcLoc.getY() - thickness, npcSize + (thickness * 2), npcSize + (thickness * 2), npcLoc.getPlane()).toWorldPointList(); - if (!includeUnder) - { - big.removeIf(little::contains); - } - return big; - } - - public static void renderNpcOverlay(Graphics2D graphics, NPC actor, Color color, int outlineWidth, int outlineAlpha, int fillAlpha, Client client) - { - int size = 1; - NPCDefinition composition = actor.getTransformedDefinition(); - if (composition != null) - { - size = composition.getSize(); - } - LocalPoint lp = actor.getLocalLocation(); - Polygon tilePoly = Perspective.getCanvasTileAreaPoly(client, lp, size); - - if (tilePoly != null) - { - OverlayUtil.drawStrokeAndFillPoly(graphics, color, outlineWidth, outlineAlpha, fillAlpha, tilePoly); - } - } - - public static void setProgressIcon(Graphics2D graphics, Point point, BufferedImage currentPhaseIcon, int totalWidth, int bgPadding, int currentPosX, Color colorIconBackground, int overlayIconDistance, Color colorIconBorder, Color colorIconBorderFill) - { - graphics.setStroke(new BasicStroke(2)); - graphics.setColor(colorIconBackground); - graphics.fillOval( - point.getX() - totalWidth / 2 + currentPosX - bgPadding, - point.getY() - currentPhaseIcon.getHeight() / 2 - overlayIconDistance - bgPadding, - currentPhaseIcon.getWidth() + bgPadding * 2, - currentPhaseIcon.getHeight() + bgPadding * 2); - - graphics.setColor(colorIconBorder); - graphics.drawOval( - point.getX() - totalWidth / 2 + currentPosX - bgPadding, - point.getY() - currentPhaseIcon.getHeight() / 2 - overlayIconDistance - bgPadding, - currentPhaseIcon.getWidth() + bgPadding * 2, - currentPhaseIcon.getHeight() + bgPadding * 2); - - graphics.drawImage( - currentPhaseIcon, - point.getX() - totalWidth / 2 + currentPosX, - point.getY() - currentPhaseIcon.getHeight() / 2 - overlayIconDistance, - null); - - graphics.setColor(colorIconBorderFill); - } } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/WidgetItemOverlay.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/WidgetItemOverlay.java index 1487171183..295c1d618b 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/WidgetItemOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/WidgetItemOverlay.java @@ -69,7 +69,7 @@ public abstract class WidgetItemOverlay extends Overlay super.setLayer(OverlayLayer.ABOVE_WIDGETS); } - protected abstract void renderItemOverlay(Graphics2D graphics, int itemId, WidgetItem itemWidget); + public abstract void renderItemOverlay(Graphics2D graphics, int itemId, WidgetItem itemWidget); @Override public Dimension render(Graphics2D graphics) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/WidgetOverlay.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/WidgetOverlay.java index aa995eafff..d5f57d6590 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/WidgetOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/WidgetOverlay.java @@ -24,61 +24,52 @@ */ package net.runelite.client.ui.overlay; -import com.google.common.collect.ImmutableMap; import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.Rectangle; +import java.util.Arrays; import java.util.Collection; -import java.util.Map; import java.util.Objects; -import java.util.stream.Collectors; -import lombok.Getter; import net.runelite.api.Client; +import net.runelite.api.Varbits; import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetInfo; public class WidgetOverlay extends Overlay { - private static final Map WIDGETS = ImmutableMap - .builder() - .put(WidgetInfo.RESIZABLE_MINIMAP_WIDGET, OverlayPosition.CANVAS_TOP_RIGHT) - .put(WidgetInfo.RESIZABLE_MINIMAP_STONES_WIDGET, OverlayPosition.CANVAS_TOP_RIGHT) - .put(WidgetInfo.FOSSIL_ISLAND_OXYGENBAR, OverlayPosition.TOP_LEFT) - .put(WidgetInfo.EXPERIENCE_TRACKER_WIDGET, OverlayPosition.TOP_RIGHT) - .put(WidgetInfo.RAIDS_POINTS_INFOBOX, OverlayPosition.TOP_RIGHT) - .put(WidgetInfo.TOB_PARTY_INTERFACE, OverlayPosition.TOP_LEFT) - .put(WidgetInfo.TOB_PARTY_STATS, OverlayPosition.TOP_LEFT) - .put(WidgetInfo.GWD_KC, OverlayPosition.TOP_RIGHT) - .put(WidgetInfo.TITHE_FARM, OverlayPosition.TOP_RIGHT) - .put(WidgetInfo.PEST_CONTROL_BOAT_INFO, OverlayPosition.TOP_LEFT) - .put(WidgetInfo.PEST_CONTROL_INFO, OverlayPosition.TOP_LEFT) - .put(WidgetInfo.ZEAH_MESS_HALL_COOKING_DISPLAY, OverlayPosition.TOP_LEFT) - .put(WidgetInfo.PVP_KILLDEATH_COUNTER, OverlayPosition.TOP_LEFT) - .put(WidgetInfo.SKOTIZO_CONTAINER, OverlayPosition.TOP_LEFT) - .put(WidgetInfo.KOUREND_FAVOUR_OVERLAY, OverlayPosition.TOP_CENTER) - .put(WidgetInfo.MULTICOMBAT_FIXED, OverlayPosition.BOTTOM_RIGHT) - .put(WidgetInfo.MULTICOMBAT_RESIZEABLE, OverlayPosition.CANVAS_TOP_RIGHT) - .put(WidgetInfo.PYRAMID_PLUNDER_DATA, OverlayPosition.TOP_CENTER) - .put(WidgetInfo.LMS_INFO, OverlayPosition.TOP_RIGHT) - .put(WidgetInfo.LMS_KDA, OverlayPosition.TOP_RIGHT) - .put(WidgetInfo.THEATRE_OF_BLOOD_HEALTH_ORBS, OverlayPosition.TOP_LEFT) - .put(WidgetInfo.GAUNTLET_TIMER_CONTAINER, OverlayPosition.TOP_LEFT) - .put(WidgetInfo.HALLOWED_SEPULCHRE_TIMER_CONTAINER, OverlayPosition.TOP_LEFT) - .put(WidgetInfo.HEALTH_OVERLAY_BAR, OverlayPosition.TOP_CENTER) - .put(WidgetInfo.NIGHTMARE_PILLAR_HEALTH, OverlayPosition.TOP_LEFT) - .put(WidgetInfo.VOLCANIC_MINE_VENTS_INFOBOX_GROUP, OverlayPosition.BOTTOM_RIGHT) - .put(WidgetInfo.VOLCANIC_MINE_STABILITY_INFOBOX_GROUP, OverlayPosition.BOTTOM_LEFT) - .build(); - public static Collection createOverlays(final Client client) { - return WIDGETS.entrySet().stream() - .map(w -> new WidgetOverlay(client, w.getKey(), w.getValue())) - .collect(Collectors.toList()); + return Arrays.asList( + new WidgetOverlay(client, WidgetInfo.RESIZABLE_MINIMAP_WIDGET, OverlayPosition.CANVAS_TOP_RIGHT), + new WidgetOverlay(client, WidgetInfo.RESIZABLE_MINIMAP_STONES_WIDGET, OverlayPosition.CANVAS_TOP_RIGHT), + new WidgetOverlay(client, WidgetInfo.FOSSIL_ISLAND_OXYGENBAR, OverlayPosition.TOP_LEFT), + new XpTrackerWidgetOverlay(client, WidgetInfo.EXPERIENCE_TRACKER_WIDGET, OverlayPosition.TOP_RIGHT), + new WidgetOverlay(client, WidgetInfo.RAIDS_POINTS_INFOBOX, OverlayPosition.TOP_RIGHT), + new WidgetOverlay(client, WidgetInfo.TOB_PARTY_INTERFACE, OverlayPosition.TOP_LEFT), + new WidgetOverlay(client, WidgetInfo.TOB_PARTY_STATS, OverlayPosition.TOP_LEFT), + new WidgetOverlay(client, WidgetInfo.GWD_KC, OverlayPosition.TOP_RIGHT), + new WidgetOverlay(client, WidgetInfo.TITHE_FARM, OverlayPosition.TOP_RIGHT), + new WidgetOverlay(client, WidgetInfo.PEST_CONTROL_BOAT_INFO, OverlayPosition.TOP_LEFT), + new WidgetOverlay(client, WidgetInfo.PEST_CONTROL_INFO, OverlayPosition.TOP_LEFT), + new WidgetOverlay(client, WidgetInfo.ZEAH_MESS_HALL_COOKING_DISPLAY, OverlayPosition.TOP_LEFT), + new WidgetOverlay(client, WidgetInfo.PVP_KILLDEATH_COUNTER, OverlayPosition.TOP_LEFT), + new WidgetOverlay(client, WidgetInfo.SKOTIZO_CONTAINER, OverlayPosition.TOP_LEFT), + new WidgetOverlay(client, WidgetInfo.KOUREND_FAVOUR_OVERLAY, OverlayPosition.TOP_CENTER), + new WidgetOverlay(client, WidgetInfo.PYRAMID_PLUNDER_DATA, OverlayPosition.TOP_CENTER), + new WidgetOverlay(client, WidgetInfo.LMS_INFO, OverlayPosition.TOP_RIGHT), + new WidgetOverlay(client, WidgetInfo.LMS_KDA, OverlayPosition.TOP_RIGHT), + new WidgetOverlay(client, WidgetInfo.GAUNTLET_TIMER_CONTAINER, OverlayPosition.TOP_LEFT), + new WidgetOverlay(client, WidgetInfo.HALLOWED_SEPULCHRE_TIMER_CONTAINER, OverlayPosition.TOP_LEFT), + new WidgetOverlay(client, WidgetInfo.HEALTH_OVERLAY_BAR, OverlayPosition.TOP_CENTER), + new WidgetOverlay(client, WidgetInfo.NIGHTMARE_PILLAR_HEALTH, OverlayPosition.TOP_LEFT), + new WidgetOverlay(client, WidgetInfo.VOLCANIC_MINE_VENTS_INFOBOX_GROUP, OverlayPosition.BOTTOM_RIGHT), + new WidgetOverlay(client, WidgetInfo.VOLCANIC_MINE_STABILITY_INFOBOX_GROUP, OverlayPosition.BOTTOM_LEFT), + new WidgetOverlay(client, WidgetInfo.MULTICOMBAT_FIXED, OverlayPosition.BOTTOM_RIGHT), + new WidgetOverlay(client, WidgetInfo.MULTICOMBAT_RESIZEABLE, OverlayPosition.CANVAS_TOP_RIGHT) + ); } - private final Client client; - @Getter + protected final Client client; private final WidgetInfo widgetInfo; private final Rectangle parentBounds = new Rectangle(); @@ -97,32 +88,10 @@ public class WidgetOverlay extends Overlay return Objects.toString(widgetInfo); } - @Override - public Rectangle getBounds() - { - final Rectangle bounds = super.getBounds(); - final Rectangle parent = getParentBounds(client.getWidget(widgetInfo)); - - if (parent.isEmpty()) - { - return bounds; - } - - int x = bounds.x; - int y = bounds.y; - x = Math.max(parent.x, x); - y = Math.max(parent.y, y); - x = Math.min((int) parent.getMaxX() - bounds.width, x); - y = Math.min((int) parent.getMaxY() - bounds.height, y); - bounds.setLocation(x, y); - return bounds; - } - @Override public Dimension render(Graphics2D graphics) { final Widget widget = client.getWidget(widgetInfo); - final Rectangle bounds = super.getBounds(); final Rectangle parent = getParentBounds(widget); if (parent.isEmpty()) @@ -130,15 +99,8 @@ public class WidgetOverlay extends Overlay return null; } - int x = bounds.x; - int y = bounds.y; - x = Math.max(parent.x, x); - y = Math.max(parent.y, y); - x = Math.min((int) parent.getMaxX() - bounds.width, x); - y = Math.min((int) parent.getMaxY() - bounds.height, y); - bounds.setLocation(x, y); - widget.setOriginalX(0); - widget.setOriginalY(0); + final Rectangle bounds = getBounds(); + // The widget relative pos is relative to the parent widget.setRelativeX(bounds.x - parent.x); widget.setRelativeY(bounds.y - parent.y); return new Dimension(widget.getWidth(), widget.getHeight()); @@ -146,11 +108,6 @@ public class WidgetOverlay extends Overlay private Rectangle getParentBounds(final Widget widget) { - if (!client.isClientThread()) - { - return parentBounds; - } - if (widget == null || widget.isHidden()) { parentBounds.setBounds(new Rectangle()); @@ -166,10 +123,65 @@ public class WidgetOverlay extends Overlay } else { - bounds = new Rectangle(parent.getCanvasLocation().getX(), parent.getCanvasLocation().getY(), parent.getWidth(), parent.getHeight()); + bounds = parent.getBounds(); } parentBounds.setBounds(bounds); return bounds; } + + @Override + public Rectangle getParentBounds() + { + if (!client.isClientThread()) + { + // During overlay drag this is called on the EDT, so we just + // cache and reuse the last known parent bounds. + return parentBounds; + } + + final Widget widget = client.getWidget(widgetInfo); + return getParentBounds(widget); + } + + private static class XpTrackerWidgetOverlay extends WidgetOverlay + { + private XpTrackerWidgetOverlay(Client client, WidgetInfo widgetInfo, OverlayPosition overlayPosition) + { + super(client, widgetInfo, overlayPosition); + } + + /** + * Get the overlay position of the xptracker based on the configured location in-game. + * + * @return + */ + @Override + public OverlayPosition getPosition() + { + if (!client.isClientThread()) + { + // During overlay drag, getPosition() is called on the EDT, so we just + // cache and reuse the last known configured position. + return super.getPosition(); + } + + OverlayPosition position; + switch (client.getVar(Varbits.EXPERIENCE_TRACKER_POSITION)) + { + case 0: + default: + position = OverlayPosition.TOP_RIGHT; + break; + case 1: + position = OverlayPosition.TOP_CENTER; + break; + case 2: + position = OverlayPosition.TOP_LEFT; + break; + } + setPosition(position); + return position; + } + } } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowMinimapOverlay.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowMinimapOverlay.java deleted file mode 100644 index 91be8caeca..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowMinimapOverlay.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright (c) 2018, Hydrox6 - * 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.overlay.arrow; - -import java.awt.Dimension; -import java.awt.Graphics2D; -import java.awt.geom.AffineTransform; -import java.awt.image.BufferedImage; -import java.util.Collection; -import java.util.List; -import javax.inject.Inject; -import javax.inject.Singleton; -import lombok.extern.slf4j.Slf4j; -import net.runelite.api.Client; -import net.runelite.api.GameObject; -import net.runelite.api.NPC; -import net.runelite.api.Perspective; -import net.runelite.api.Point; -import net.runelite.api.coords.LocalPoint; -import net.runelite.api.coords.WorldPoint; -import net.runelite.client.ui.overlay.Overlay; -import net.runelite.client.ui.overlay.OverlayLayer; -import net.runelite.client.ui.overlay.OverlayPosition; -import net.runelite.client.ui.overlay.OverlayPriority; - -@Singleton -@Slf4j -public class ArrowMinimapOverlay extends Overlay -{ - private static final int MINIMAP_VISIBLE_RANGE = 17 * 128; - - private final ArrowPointManager arrowPointManager; - private final Client client; - - @Inject - private ArrowMinimapOverlay(Client client, ArrowPointManager arrowPointManager) - { - this.client = client; - this.arrowPointManager = arrowPointManager; - setPosition(OverlayPosition.DYNAMIC); - setPriority(OverlayPriority.HIGH); - setLayer(OverlayLayer.ALWAYS_ON_TOP); - } - - @Override - public Dimension render(Graphics2D graphics) - { - - final Collection points = arrowPointManager.getArrowPoints().values(); - - if (points.isEmpty()) - { - return null; - } - - final LocalPoint localPlayerPos = client.getLocalPlayer().getLocalLocation(); - final WorldPoint worldPlayerPos = WorldPoint.fromLocal(client, localPlayerPos); - - for (ArrowPoint arrowPoint : points) - { - final WorldPoint worldPoint = arrowPoint.getWorldPoint(); - - if (worldPoint.distanceTo(worldPlayerPos) < arrowPoint.getVisibleRange()) - { - LocalPoint fallBackPoint = LocalPoint.fromWorld(client, worldPoint); - if (arrowPoint.types.contains(ArrowType.NPC)) - { - boolean found = false; - for (NPC npc : client.getCachedNPCs()) - { - if (npc != null && arrowPoint.getNpcIDs().contains(npc.getId())) - { - found = true; - renderMinimapArrow(graphics, arrowPoint, npc.getLocalLocation(), localPlayerPos, worldPlayerPos); - } - } - if (found || fallBackPoint == null) - { - continue; - } - - renderMinimapArrow(graphics, arrowPoint, null, localPlayerPos, worldPlayerPos); - } - else if (arrowPoint.types.contains(ArrowType.OBJECT)) - { - - List objects = ArrowUtil.getObjects(client, arrowPoint.getObjectIDs()); - if (objects.isEmpty() && fallBackPoint != null) - { - renderMinimapArrow(graphics, arrowPoint, null, localPlayerPos, worldPlayerPos); - continue; - } - for (GameObject object : objects) - { - renderMinimapArrow(graphics, arrowPoint, object.getLocalLocation(), localPlayerPos, worldPlayerPos); - } - } - else if (arrowPoint.types.contains(ArrowType.WORLD_POINT)) - { - renderMinimapArrow(graphics, arrowPoint, null, localPlayerPos, worldPlayerPos); - } - } - } - - return null; - } - - private void renderMinimapArrow(Graphics2D graphics, ArrowPoint arrowPoint, LocalPoint localPoint, LocalPoint localPlayerPos, WorldPoint worldPlayerPos) - { - final BufferedImage minimapImage = arrowPoint.getMinimapImage(); - final WorldPoint worldPoint = arrowPoint.getWorldPoint(); - final Point minimapImageOffset = arrowPoint.getMinimapImageOffset(); - - if (localPoint != null && localPlayerPos.distanceTo(localPoint) < MINIMAP_VISIBLE_RANGE) - { - final Point minimapLoc = Perspective.getMiniMapImageLocation(client, localPoint, minimapImage); - if (minimapLoc != null) - { - graphics.drawImage(minimapImage, minimapLoc.getX() + minimapImageOffset.getX(), minimapLoc.getY() + minimapImageOffset.getY(), null); - } - } - else - { - if (!arrowPoint.isMinimapUseFallback()) - { - return; - } - final Point minimapPlayerPos = Perspective.localToMinimap(client, localPlayerPos); - double cameraAngle = ((client.getCameraYaw()) / 2048.0) * 2 * Math.PI; - //Use localPoint if it's available for a smoother rotation - double theta; - if (localPoint != null) - { - theta = Math.atan2(localPoint.getX() - localPlayerPos.getX(), localPlayerPos.getY() - localPoint.getY()); - } - else - { - theta = Math.atan2(worldPoint.getX() - worldPlayerPos.getX(), worldPlayerPos.getY() - worldPoint.getY()); - } - - AffineTransform at = new AffineTransform(); - if (arrowPoint.isMinimapImagePointToTarget()) - { - if (minimapPlayerPos != null) - { - at.translate(minimapPlayerPos.getX(), minimapPlayerPos.getY()); - } - at.rotate(cameraAngle - theta); - at.translate(0, 66); - at.translate(minimapImageOffset.getX() - minimapImage.getWidth() / 2, minimapImageOffset.getY() - minimapImage.getHeight() / 2); - } - else - { - //Get the correct position as if it were rotated - at.rotate(cameraAngle - theta); - at.translate(0, 66); - double dX = at.getTranslateX(); - double dY = at.getTranslateY(); - //Then apply that position to an un-rotated transform - at = new AffineTransform(); - if (minimapPlayerPos != null) - { - at.translate(minimapPlayerPos.getX(), minimapPlayerPos.getY()); - } - at.translate(dX, dY); - at.translate(minimapImageOffset.getX() - minimapImage.getWidth() / 2, minimapImageOffset.getY() - minimapImage.getHeight() / 2); - } - - graphics.drawImage(minimapImage, at, null); - } - } - - private void renderMinimapArrowNPC(Graphics2D graphics, ArrowPoint arrowPoint, NPC npc, LocalPoint localPlayerPos, WorldPoint worldPlayerPos) - { - final BufferedImage minimapImage = arrowPoint.getMinimapImage(); - final WorldPoint worldPoint = arrowPoint.getWorldPoint(); - LocalPoint localPoint; - if (npc != null) - { - localPoint = LocalPoint.fromWorld(client, worldPoint); - if (localPoint != null) - { - //For whatever reason, LocalPoint.fromWorld returns a point (-1, -1) from the actual point - localPoint = new LocalPoint(localPoint.getX() + 1, localPoint.getY() + 1); - } - } - localPoint = null; - - renderMinimapArrow(graphics, arrowPoint, localPoint, localPlayerPos, worldPlayerPos); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowPoint.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowPoint.java deleted file mode 100644 index 2b381ca9cc..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowPoint.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2018, Morgan Lewis - * 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.overlay.arrow; - -import java.awt.Color; -import java.awt.image.BufferedImage; -import java.util.EnumSet; -import java.util.Set; -import lombok.Builder; -import lombok.Data; -import lombok.NonNull; -import net.runelite.api.Point; -import net.runelite.api.coords.WorldPoint; - -@Data -@Builder -public class ArrowPoint -{ - /** - * Can define a point to mark, or be used as a fallback when an object or an NPC is outside of the scene - */ - @NonNull - private WorldPoint worldPoint; - - private Set npcIDs; - - private Set objectIDs; - - /** - * The regions that an NPC or Object has to be in to be marked (eg. Exam Centre locked chest Hard Clue requires - * killing a Barbarian in Barbarian Village, or Konar Slayer requires killing monsters in a certain area) - */ - private Set region; - - private BufferedImage worldImage; - @Builder.Default - private Point worldImageOffset = new Point(0, 0); - @Builder.Default - private Color tileColor = Color.RED; - - private BufferedImage minimapImage; - @Builder.Default - private Point minimapImageOffset = new Point(0, 0); - /** - * Whether the minimap arrow should rotate. Set to false if not using an arrow - */ - @Builder.Default - private boolean minimapImagePointToTarget = true; - @Builder.Default - private int visibleRange = 128; - /** - * Whether the minimap arrow should use the fallback point. Useful when there are multiple locations that could be - * used as a fallback (eg. Seers' Village locked draw Medium Clue, where you need to kill any chicken for a key) - */ - @Builder.Default - private boolean minimapUseFallback = true; - - /** - * A Set of what arrows should be rendered - */ - @NonNull - EnumSet types; -} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowPointManager.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowPointManager.java deleted file mode 100644 index f43600c68d..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowPointManager.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2018, Morgan Lewis - * 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.overlay.arrow; - -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Multimap; -import javax.inject.Singleton; -import lombok.AccessLevel; -import lombok.Getter; -import net.runelite.client.plugins.Plugin; - -@Singleton -public class ArrowPointManager -{ - @Getter(AccessLevel.PACKAGE) - private final Multimap arrowPoints = HashMultimap.create(); - - public void add(Plugin plugin, ArrowPoint arrowPoint) - { - arrowPoints.put(plugin, arrowPoint); - } - - public void remove(Plugin plugin, ArrowPoint arrowPoint) - { - arrowPoints.remove(plugin, arrowPoint); - } - - public void clear(Plugin plugin) - { - arrowPoints.removeAll(plugin); - } - - public void clear() - { - arrowPoints.clear(); - } -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowType.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowType.java deleted file mode 100644 index 4e9fbda4ee..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowType.java +++ /dev/null @@ -1,10 +0,0 @@ -package net.runelite.client.ui.overlay.arrow; - -public enum ArrowType -{ - MINIMAP, - NPC, - OBJECT, - WORLD_MAP, - WORLD_POINT -} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowUtil.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowUtil.java deleted file mode 100644 index 3d3b864b51..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowUtil.java +++ /dev/null @@ -1,51 +0,0 @@ -package net.runelite.client.ui.overlay.arrow; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import net.runelite.api.Client; -import net.runelite.api.GameObject; -import net.runelite.api.ObjectDefinition; -import net.runelite.api.Scene; -import net.runelite.api.Tile; - -class ArrowUtil -{ - static List getObjects(final Client client, Set objectIDs) - { - final Scene scene = client.getScene(); - final Tile[][] tiles = scene.getTiles()[client.getPlane()]; - final ArrayList found = new ArrayList<>(); - - for (Tile[] tiles2 : tiles) - { - for (Tile tile : tiles2) - { - for (GameObject object : tile.getGameObjects()) - { - if (object == null) - { - continue; - } - - if (objectIDs.contains(object.getId())) - { - found.add(object); - continue; - } - - // Check impostors - final ObjectDefinition comp = client.getObjectDefinition(object.getId()); - final ObjectDefinition impostor = comp.getImpostorIds() != null ? comp.getImpostor() : comp; - - if (impostor != null && objectIDs.contains(impostor.getId())) - { - found.add(object); - } - } - } - } - - return found; - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowWorldOverlay.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowWorldOverlay.java deleted file mode 100644 index c8af180f00..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/arrow/ArrowWorldOverlay.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (c) 2018, Hydrox6 - * 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.overlay.arrow; - -import java.awt.Dimension; -import java.awt.Graphics2D; -import java.awt.Polygon; -import java.awt.image.BufferedImage; -import java.util.Collection; -import java.util.List; -import javax.inject.Inject; -import javax.inject.Singleton; -import lombok.extern.slf4j.Slf4j; -import net.runelite.api.Client; -import net.runelite.api.GameObject; -import net.runelite.api.NPC; -import net.runelite.api.Perspective; -import net.runelite.api.Point; -import net.runelite.api.coords.LocalPoint; -import net.runelite.api.coords.WorldPoint; -import net.runelite.client.ui.overlay.Overlay; -import net.runelite.client.ui.overlay.OverlayLayer; -import net.runelite.client.ui.overlay.OverlayPosition; -import net.runelite.client.ui.overlay.OverlayPriority; -import net.runelite.client.ui.overlay.OverlayUtil; - -@Singleton -@Slf4j -public class ArrowWorldOverlay extends Overlay -{ - private static final int Z_OFFSET = 20; - - private final ArrowPointManager arrowPointManager; - private final Client client; - - @Inject - private ArrowWorldOverlay(Client client, ArrowPointManager arrowPointManager) - { - this.client = client; - this.arrowPointManager = arrowPointManager; - setPosition(OverlayPosition.DYNAMIC); - setPriority(OverlayPriority.HIGHEST); - setLayer(OverlayLayer.UNDER_WIDGETS); - } - - @Override - public Dimension render(Graphics2D graphics) - { - final Collection points = arrowPointManager.getArrowPoints().values(); - - if (points.isEmpty()) - { - return null; - } - - WorldPoint playerLocation = client.getLocalPlayer().getWorldLocation(); - - for (ArrowPoint arrowPoint : points) - { - WorldPoint point = arrowPoint.getWorldPoint(); - - if (point.distanceTo(playerLocation) < client.getScene().getDrawDistance()) - { - LocalPoint fallBackPoint = LocalPoint.fromWorld(client, point); - if (arrowPoint.types.contains(ArrowType.NPC)) - { - boolean found = false; - for (NPC npc : client.getCachedNPCs()) - { - if (npc != null && arrowPoint.getNpcIDs().contains(npc.getId())) - { - found = true; - renderWorldArrow(graphics, arrowPoint, npc.getLocalLocation(), npc.getLogicalHeight() + Z_OFFSET); - } - } - if (found || fallBackPoint == null) - { - continue; - } - - renderWorldArrow(graphics, arrowPoint, fallBackPoint); - } - else if (arrowPoint.types.contains(ArrowType.OBJECT)) - { - - List objects = ArrowUtil.getObjects(client, arrowPoint.getObjectIDs()); - if (objects.isEmpty() && fallBackPoint != null) - { - renderWorldArrow(graphics, arrowPoint, fallBackPoint); - continue; - } - for (GameObject object : objects) - { - if (object.getEntity().getModel() == null) - { - renderWorldArrow(graphics, arrowPoint, object.getLocalLocation(), 0); - } - else - { - renderWorldArrow(graphics, arrowPoint, object.getLocalLocation(), object.getEntity().getModel().getModelHeight() + Z_OFFSET); - } - } - } - else if (arrowPoint.types.contains(ArrowType.WORLD_POINT)) - { - renderWorldArrow(graphics, arrowPoint, fallBackPoint); - } - } - } - return null; - } - - private void renderWorldArrow(Graphics2D graphics, ArrowPoint arrowPoint, LocalPoint localPoint) - { - renderWorldArrow(graphics, arrowPoint, localPoint, 0); - } - - private void renderWorldArrow(Graphics2D graphics, ArrowPoint arrowPoint, LocalPoint localPoint, int zOffset) - { - final BufferedImage worldImage = arrowPoint.getWorldImage(); - //Draw Tile - Polygon poly = Perspective.getCanvasTilePoly(client, localPoint); - if (poly != null) - { - OverlayUtil.renderPolygon(graphics, poly, arrowPoint.getTileColor()); - } - - Point worldIconOffset = arrowPoint.getWorldImageOffset(); - //Draw World Arrow - Point canvasPoint = Perspective.getCanvasImageLocation(client, localPoint, worldImage, zOffset); - if (canvasPoint != null) - { - graphics.drawImage(worldImage, canvasPoint.getX() + worldIconOffset.getX(), canvasPoint.getY() + worldIconOffset.getY(), null); - } - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/BackgroundComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/BackgroundComponent.java index c5fbdeea8c..4a7fae42ca 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/BackgroundComponent.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/BackgroundComponent.java @@ -83,7 +83,7 @@ public class BackgroundComponent implements RenderableEntity final Rectangle insideStroke = new Rectangle(); insideStroke.setLocation(rectangle.x + BORDER_OFFSET / 2, rectangle.y + BORDER_OFFSET / 2); insideStroke.setSize(rectangle.width - BORDER_OFFSET - BORDER_OFFSET / 2, - rectangle.height - BORDER_OFFSET - BORDER_OFFSET / 2); + rectangle.height - BORDER_OFFSET - BORDER_OFFSET / 2); graphics.setColor(insideStrokeColor); graphics.draw(insideStroke); diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/ImageComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/ImageComponent.java index f83b070155..39ec431697 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/ImageComponent.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/ImageComponent.java @@ -1,6 +1,5 @@ /* * Copyright (c) 2018, Tomas Slusny - * Copyright (c) 2019, ThatGamerBlue * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -45,9 +44,6 @@ public class ImageComponent implements LayoutableRenderableEntity private Point preferredLocation = new Point(); - private int xOffset = 0; - private int yOffset = 0; - @Override public Dimension render(Graphics2D graphics) { @@ -56,14 +52,10 @@ public class ImageComponent implements LayoutableRenderableEntity return null; } - preferredLocation.x += xOffset; - preferredLocation.y += yOffset; - graphics.drawImage(image, preferredLocation.x, preferredLocation.y, null); final Dimension dimension = new Dimension(image.getWidth(), image.getHeight()); bounds.setLocation(preferredLocation); bounds.setSize(dimension); - setPreferredSize(dimension); return dimension; } @@ -72,10 +64,4 @@ public class ImageComponent implements LayoutableRenderableEntity { // Just use image dimensions for now } - - public void translate(int x, int y) - { - xOffset = x; - yOffset = y; - } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/InfoBoxComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/InfoBoxComponent.java index 2e203ba5f8..2c777ec2ed 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/InfoBoxComponent.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/InfoBoxComponent.java @@ -68,6 +68,7 @@ public class InfoBoxComponent implements LayoutableRenderableEntity } graphics.setFont(getSize() < DEFAULT_SIZE ? FontManager.getRunescapeSmallFont() : FontManager.getRunescapeFont()); + final int baseX = preferredLocation.x; final int baseY = preferredLocation.y; diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/LayoutableRenderableEntity.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/LayoutableRenderableEntity.java index 9a820f6ba9..7a736686d0 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/LayoutableRenderableEntity.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/LayoutableRenderableEntity.java @@ -32,8 +32,6 @@ import net.runelite.client.ui.overlay.RenderableEntity; public interface LayoutableRenderableEntity extends RenderableEntity { Rectangle getBounds(); - void setPreferredLocation(Point position); - void setPreferredSize(Dimension dimension); } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/LineComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/LineComponent.java index 2f11f3b4b1..20638e7874 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/LineComponent.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/LineComponent.java @@ -24,6 +24,7 @@ */ package net.runelite.client.ui.overlay.components; +import com.google.common.base.MoreObjects; import com.google.common.base.Strings; import java.awt.Color; import java.awt.Dimension; @@ -31,11 +32,10 @@ import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; -import java.util.Objects; import lombok.Builder; import lombok.Getter; import lombok.Setter; -import net.runelite.api.util.Text; +import net.runelite.client.util.Text; @Setter @Builder @@ -64,8 +64,8 @@ public class LineComponent implements LayoutableRenderableEntity public Dimension render(Graphics2D graphics) { // Prevent NPEs - final String left = Objects.requireNonNullElse(this.left, ""); - final String right = Objects.requireNonNullElse(this.right, ""); + final String left = MoreObjects.firstNonNull(this.left, ""); + final String right = MoreObjects.firstNonNull(this.right, ""); final FontMetrics metrics = graphics.getFontMetrics(); final int baseX = preferredLocation.x; diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/PanelComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/PanelComponent.java index 9c5a4ac86e..41829f6dc1 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/PanelComponent.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/PanelComponent.java @@ -33,7 +33,6 @@ import java.util.ArrayList; import java.util.List; import lombok.Getter; import lombok.Setter; -import net.runelite.client.ui.overlay.components.table.TableComponent; public class PanelComponent implements LayoutableRenderableEntity { @@ -60,9 +59,6 @@ public class PanelComponent implements LayoutableRenderableEntity @Setter private boolean wrap = false; - @Setter - private boolean overrideResize = false; - @Setter private Rectangle border = new Rectangle( ComponentConstants.STANDARD_BORDER, @@ -78,8 +74,6 @@ public class PanelComponent implements LayoutableRenderableEntity @Override public Dimension render(Graphics2D graphics) { - children.removeIf(child -> child instanceof TableComponent && ((TableComponent) child).isEmpty()); - if (children.isEmpty()) { return null; @@ -133,11 +127,6 @@ public class PanelComponent implements LayoutableRenderableEntity } } - if (overrideResize) - { - child.setPreferredSize(childPreferredSize); - } - child.setPreferredLocation(new Point(x, y)); final Dimension childDimension = child.render(graphics); diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/TextComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/TextComponent.java index a8e7f215c4..3b10b3561b 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/TextComponent.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/TextComponent.java @@ -24,19 +24,15 @@ */ package net.runelite.client.ui.overlay.components; -import java.awt.AlphaComposite; import java.awt.Color; -import java.awt.Composite; import java.awt.Dimension; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Point; -import java.awt.Shape; -import java.awt.font.GlyphVector; import java.util.regex.Pattern; import lombok.Setter; -import net.runelite.api.util.Text; import net.runelite.client.ui.overlay.RenderableEntity; +import net.runelite.client.util.Text; @Setter public class TextComponent implements RenderableEntity @@ -48,89 +44,66 @@ public class TextComponent implements RenderableEntity private Point position = new Point(); private Color color = Color.WHITE; private boolean outline; - private boolean alpha; // Generates a lot of garbage! @Override public Dimension render(Graphics2D graphics) { final FontMetrics fontMetrics = graphics.getFontMetrics(); - final String[] parts = COL_TAG_PATTERN_W_LOOKAHEAD.split(text); - int x = position.x; - - for (String part : parts) + if (COL_TAG_PATTERN_W_LOOKAHEAD.matcher(text).find()) { - final String notags = Text.removeTags(part); - final Color col = part.equals(notags) ? color : getColor(part); + final String[] parts = COL_TAG_PATTERN_W_LOOKAHEAD.split(text); + int x = position.x; - if (alpha) + for (String textSplitOnCol : parts) { - drawAlpha(graphics, x, position.y, notags, col); + final String textWithoutCol = Text.removeTags(textSplitOnCol); + final String colColor = textSplitOnCol.substring(textSplitOnCol.indexOf("=") + 1, textSplitOnCol.indexOf(">")); + + graphics.setColor(Color.BLACK); + + if (outline) + { + graphics.drawString(textWithoutCol, x, position.y + 1); + graphics.drawString(textWithoutCol, x, position.y - 1); + graphics.drawString(textWithoutCol, x + 1, position.y); + graphics.drawString(textWithoutCol, x - 1, position.y); + } + else + { + // shadow + graphics.drawString(textWithoutCol, x + 1, position.y + 1); + } + + // actual text + graphics.setColor(Color.decode("#" + colColor)); + graphics.drawString(textWithoutCol, x, position.y); + + x += fontMetrics.stringWidth(textWithoutCol); } - else - { - drawString(graphics, x, position.y, notags, col); - } - - x += fontMetrics.stringWidth(notags); - } - - return new Dimension(fontMetrics.stringWidth(Text.removeTags(text)), fontMetrics.getHeight()); - } - - private void drawString(Graphics2D graphics, int x, int y, String text, Color color) - { - graphics.setColor(Color.BLACK); - - if (outline) - { - graphics.drawString(text, x, y + 1); - graphics.drawString(text, x, y - 1); - graphics.drawString(text, x + 1, y); - graphics.drawString(text, x - 1, y); } else { - // shadow - graphics.drawString(text, x + 1, y + 1); + graphics.setColor(Color.BLACK); + + if (outline) + { + graphics.drawString(text, position.x, position.y + 1); + graphics.drawString(text, position.x, position.y - 1); + graphics.drawString(text, position.x + 1, position.y); + graphics.drawString(text, position.x - 1, position.y); + } + else + { + // shadow + graphics.drawString(text, position.x + 1, position.y + 1); + } + + // actual text + graphics.setColor(color); + graphics.drawString(text, position.x, position.y); } - - // actual text - graphics.setColor(color); - graphics.drawString(text, x, y); - } - - private void drawAlpha(Graphics2D graphics, int x, int y, String text, Color color) - { - // remember previous composite - Composite originalComposite = graphics.getComposite(); - - // create a vector of the text - GlyphVector vector = graphics.getFont().createGlyphVector(graphics.getFontRenderContext(), text); - - // compute the text shape - Shape stroke = vector.getOutline(x + 1, y + 1); - Shape shape = vector.getOutline(x, y); - - // draw text border - graphics.setColor(Color.BLACK); - graphics.fill(stroke); - - // replace the pixels instead of overlaying - graphics.setComposite(AlphaComposite.Src); - - // draw actual text - graphics.setColor(color); - graphics.fill(shape); - - // reset composite to original - graphics.setComposite(originalComposite); - } - - private static Color getColor(String from) - { - final String colColor = from.substring(from.indexOf('=') + 1, from.indexOf('>')); - return Color.decode("#" + colColor); + return new Dimension(fontMetrics.stringWidth(text), fontMetrics.getHeight()); } } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java deleted file mode 100644 index 2e6fb2de63..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java +++ /dev/null @@ -1,465 +0,0 @@ -/* - * Copyright (c) 2018, Jordan Atwood - * Copyright (c) 2019, TheStonedTurtle - * 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.overlay.components.table; - -import java.awt.Color; -import java.awt.Dimension; -import java.awt.FontMetrics; -import java.awt.Graphics2D; -import java.awt.Point; -import java.awt.Rectangle; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import lombok.Getter; -import lombok.NonNull; -import lombok.Setter; -import net.runelite.api.util.Text; -import net.runelite.client.ui.overlay.components.ComponentConstants; -import net.runelite.client.ui.overlay.components.LayoutableRenderableEntity; -import net.runelite.client.ui.overlay.components.TextComponent; - -@Setter -public class TableComponent implements LayoutableRenderableEntity -{ - private static final TableElement EMPTY_ELEMENT = TableElement.builder().build(); - - @Getter - private final List columns = new ArrayList<>(); - @Getter - private final List rows = new ArrayList<>(); - - @Getter - private final Rectangle bounds = new Rectangle(); - - private TableAlignment defaultAlignment = TableAlignment.LEFT; - private Color defaultColor = Color.WHITE; - private Dimension gutter = new Dimension(3, 0); - private Point preferredLocation = new Point(); - private Dimension preferredSize = new Dimension(ComponentConstants.STANDARD_WIDTH, 0); - - @Override - public Dimension render(final Graphics2D graphics) - { - final FontMetrics metrics = graphics.getFontMetrics(); - final TableRow colRow = TableRow.builder().elements(this.columns).build(); - final int[] columnWidths = getColumnWidths(metrics, colRow); - - graphics.translate(preferredLocation.x, preferredLocation.y); - - // Display the columns first - int height = displayRow(graphics, colRow, 0, columnWidths, metrics); - - for (TableRow row : this.rows) - { - height = displayRow(graphics, row, height, columnWidths, metrics); - } - - graphics.translate(-preferredLocation.x, -preferredLocation.y); - - final Dimension dimension = new Dimension(preferredSize.width, height); - bounds.setLocation(preferredLocation); - bounds.setSize(dimension); - - return dimension; - } - - private int displayRow(Graphics2D graphics, TableRow row, int height, int[] columnWidths, FontMetrics metrics) - { - int x = 0; - int startingRowHeight = height; - - final List elements = row.getElements(); - for (int i = 0; i < elements.size(); i++) - { - int y = startingRowHeight; - final TableElement cell = elements.get(i); - - final String content = cell.getContent(); - if (content == null) - { - continue; - } - - final String[] lines = lineBreakText(content, columnWidths[i], metrics); - final TableAlignment alignment = getCellAlignment(row, i); - final Color color = getCellColor(row, i); - - for (String line : lines) - { - final int alignmentOffset = getAlignedPosition(line, alignment, columnWidths[i], metrics); - final TextComponent leftLineComponent = new TextComponent(); - y += metrics.getHeight(); - - leftLineComponent.setPosition(new Point(x + alignmentOffset, y)); - leftLineComponent.setText(line); - leftLineComponent.setColor(color); - leftLineComponent.render(graphics); - } - height = Math.max(height, y); - x += columnWidths[i] + gutter.width; - } - - return height + gutter.height; - } - - /** - * Returns the width that each column should take up - * Based on https://stackoverflow.com/questions/22206825/algorithm-for-calculating-variable-column-widths-for-set-table-width - * - * @param metrics - * @return int[] of column width - */ - private int[] getColumnWidths(final FontMetrics metrics, final TableRow columnRow) - { - int numCols = columns.size(); - for (final TableRow r : rows) - { - numCols = Math.max(r.getElements().size(), numCols); - } - - int[] maxtextw = new int[numCols]; // max text width over all rows - int[] maxwordw = new int[numCols]; // max width of longest word - boolean[] flex = new boolean[numCols]; // is column flexible? - boolean[] wrap = new boolean[numCols]; // can column be wrapped? - int[] finalcolw = new int[numCols]; // final width of columns - - final List rows = new ArrayList<>(this.rows); - rows.add(columnRow); - - for (final TableRow r : rows) - { - final List elements = r.getElements(); - for (int col = 0; col < elements.size(); col++) - { - final TableElement ele = elements.get(col); - final String cell = ele.getContent(); - if (cell == null) - { - continue; - } - - final int cellWidth = getTextWidth(metrics, cell); - - maxtextw[col] = Math.max(maxtextw[col], cellWidth); - for (String word : cell.split(" ")) - { - maxwordw[col] = Math.max(maxwordw[col], getTextWidth(metrics, word)); - } - - if (maxtextw[col] == cellWidth) - { - wrap[col] = cell.contains(" "); - } - } - } - - int left = preferredSize.width - (numCols - 1) * gutter.width; - final double avg = left / numCols; - int nflex = 0; - - // Determine whether columns should be flexible and assign width of non-flexible cells - for (int col = 0; col < numCols; col++) - { - // This limit can be adjusted as needed - final double maxNonFlexLimit = 1.5 * avg; - - flex[col] = maxtextw[col] > maxNonFlexLimit; - if (flex[col]) - { - nflex++; - } - else - { - finalcolw[col] = maxtextw[col]; - left -= finalcolw[col]; - } - } - - // If there is not enough space, make columns that could be word-wrapped flexible too - if (left < nflex * avg) - { - for (int col = 0; col < numCols; col++) - { - if (!flex[col] && wrap[col]) - { - left += finalcolw[col]; - finalcolw[col] = 0; - flex[col] = true; - nflex++; - } - } - } - - // Calculate weights for flexible columns. The max width is capped at the table width to - // treat columns that have to be wrapped more or less equal - int tot = 0; - for (int col = 0; col < numCols; col++) - { - if (flex[col]) - { - maxtextw[col] = Math.min(maxtextw[col], preferredSize.width); - tot += maxtextw[col]; - } - } - - // Now assign the actual width for flexible columns. Make sure that it is at least as long - // as the longest word length - for (int col = 0; col < numCols; col++) - { - if (flex[col]) - { - finalcolw[col] = left * maxtextw[col] / tot; - finalcolw[col] = Math.max(finalcolw[col], maxwordw[col]); - left -= finalcolw[col]; - } - } - - // When the sum of column widths is less than the total space available, distribute the - // extra space equally across all columns - final int extraPerCol = left / numCols; - for (int col = 0; col < numCols; col++) - { - finalcolw[col] += extraPerCol; - left -= extraPerCol; - } - // Add any remainder to the right-most column - finalcolw[finalcolw.length - 1] += left; - - return finalcolw; - } - - private static int getTextWidth(final FontMetrics metrics, final String cell) - { - return metrics.stringWidth(Text.removeTags(cell)); - } - - private static String[] lineBreakText(final String text, final int maxWidth, final FontMetrics metrics) - { - final String[] words = text.split(" "); - - if (words.length == 0) - { - return new String[0]; - } - - final StringBuilder wrapped = new StringBuilder(words[0]); - int spaceLeft = maxWidth - getTextWidth(metrics, wrapped.toString()); - - for (int i = 1; i < words.length; i++) - { - final String word = words[i]; - final int wordLen = getTextWidth(metrics, word); - final int spaceWidth = metrics.stringWidth(" "); - - if (wordLen + spaceWidth > spaceLeft) - { - wrapped.append("\n").append(word); - spaceLeft = maxWidth - wordLen; - } - else - { - wrapped.append(" ").append(word); - spaceLeft -= spaceWidth + wordLen; - } - } - - return wrapped.toString().split("\n"); - } - - public boolean isEmpty() - { - return columns.size() == 0 || rows.size() == 0; - } - - private void ensureColumnSize(final int size) - { - while (size > columns.size()) - { - columns.add(TableElement.builder().build()); - } - } - - private static int getAlignedPosition(final String str, final TableAlignment alignment, final int columnWidth, final FontMetrics metrics) - { - final int stringWidth = getTextWidth(metrics, str); - int offset = 0; - - switch (alignment) - { - case LEFT: - break; - case CENTER: - offset = (columnWidth / 2) - (stringWidth / 2); - break; - case RIGHT: - offset = columnWidth - stringWidth; - break; - } - return offset; - } - - /** - * Returns the color for the specified table element. - * Priority order: cell->row->column->default - * - * @param row TableRow element - * @param colIndex column index - */ - private Color getCellColor(final TableRow row, final int colIndex) - { - final List rowElements = row.getElements(); - final TableElement cell = colIndex < rowElements.size() ? rowElements.get(colIndex) : EMPTY_ELEMENT; - final TableElement column = colIndex < columns.size() ? columns.get(colIndex) : EMPTY_ELEMENT; - - return firstNonNull( - cell.getColor(), - row.getRowColor(), - column.getColor(), - defaultColor); - } - - private void setColumnAlignment(final int col, final TableAlignment alignment) - { - assert columns.size() > col; - columns.get(col).setAlignment(alignment); - } - - public void setColumnAlignments(@Nonnull final TableAlignment... alignments) - { - ensureColumnSize(alignments.length); - for (int i = 0; i < alignments.length; i++) - { - setColumnAlignment(i, alignments[i]); - } - } - - /** - * Returns the alignment for the specified table element. - * Priority order: cell->row->column->default - * - * @param row TableRow element - * @param colIndex column index - */ - private TableAlignment getCellAlignment(final TableRow row, final int colIndex) - { - final List rowElements = row.getElements(); - final TableElement cell = colIndex < rowElements.size() ? rowElements.get(colIndex) : EMPTY_ELEMENT; - final TableElement column = colIndex < columns.size() ? columns.get(colIndex) : EMPTY_ELEMENT; - - return firstNonNull( - cell.getAlignment(), - row.getRowAlignment(), - column.getAlignment(), - defaultAlignment); - } - - @SafeVarargs - private static T firstNonNull(@Nullable T... elements) - { - if (elements == null || elements.length == 0) - { - return null; - } - - int i = 0; - T cur = elements[0]; - while (cur == null && i < elements.length) - { - cur = elements[i]; - i++; - } - - return cur; - } - - // Helper functions for cleaner overlay code - public void addRow(@Nonnull final String... cells) - { - final List elements = new ArrayList<>(); - for (final String cell : cells) - { - elements.add(TableElement.builder().content(cell).build()); - } - - final TableRow row = TableRow.builder().build(); - row.setElements(elements); - - this.rows.add(row); - } - - private void addRows(@Nonnull final String[]... rows) - { - for (String[] row : rows) - { - addRow(row); - } - } - - public void addRows(@NonNull final TableRow... rows) - { - this.rows.addAll(Arrays.asList(rows)); - } - - public void setRows(@Nonnull final String[]... elements) - { - this.rows.clear(); - addRows(elements); - } - - public void setRows(@Nonnull final TableRow... elements) - { - this.rows.clear(); - this.rows.addAll(Arrays.asList(elements)); - } - - private void addColumn(@Nonnull final String col) - { - this.columns.add(TableElement.builder().content(col).build()); - } - - public void addColumns(@NonNull final TableElement... columns) - { - this.columns.addAll(Arrays.asList(columns)); - } - - public void setColumns(@Nonnull final TableElement... elements) - { - this.columns.clear(); - this.columns.addAll(Arrays.asList(elements)); - } - - public void setColumns(@Nonnull final String... columns) - { - this.columns.clear(); - for (String col : columns) - { - addColumn(col); - } - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxManager.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxManager.java index 420c6947ea..66a6d7d913 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxManager.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxManager.java @@ -24,6 +24,7 @@ */ package net.runelite.client.ui.overlay.infobox; +import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.collect.ComparisonChain; import java.awt.Graphics; @@ -33,7 +34,6 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -41,10 +41,11 @@ import javax.inject.Inject; import javax.inject.Singleton; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; -import net.runelite.api.MenuOpcode; +import net.runelite.api.MenuAction; import net.runelite.client.config.ConfigManager; import net.runelite.client.config.RuneLiteConfig; import net.runelite.client.eventbus.EventBus; +import net.runelite.client.eventbus.Subscribe; import net.runelite.client.events.ConfigChanged; import net.runelite.client.events.InfoBoxMenuClicked; import net.runelite.client.ui.overlay.OverlayManager; @@ -66,9 +67,9 @@ public class InfoBoxManager private static final String FLIP = "Flip"; private static final String DELETE = "Delete"; - private static final OverlayMenuEntry DETACH_ME = new OverlayMenuEntry(MenuOpcode.RUNELITE_INFOBOX, DETACH, "InfoBox"); - private static final OverlayMenuEntry FLIP_ME = new OverlayMenuEntry(MenuOpcode.RUNELITE_INFOBOX, FLIP, "InfoBox Group"); - private static final OverlayMenuEntry DELETE_ME = new OverlayMenuEntry(MenuOpcode.RUNELITE_INFOBOX, DELETE, "InfoBox Group"); + private static final OverlayMenuEntry DETACH_ME = new OverlayMenuEntry(MenuAction.RUNELITE_INFOBOX, DETACH, "InfoBox"); + private static final OverlayMenuEntry FLIP_ME = new OverlayMenuEntry(MenuAction.RUNELITE_INFOBOX, FLIP, "InfoBox Group"); + private static final OverlayMenuEntry DELETE_ME = new OverlayMenuEntry(MenuAction.RUNELITE_INFOBOX, DELETE, "InfoBox Group"); private final Map layers = new ConcurrentHashMap<>(); @@ -80,13 +81,13 @@ public class InfoBoxManager private final ConfigManager configManager; @Inject - private InfoBoxManager(final RuneLiteConfig runeLiteConfig, - final TooltipManager tooltipManager, - final Client client, - final EventBus eventBus, - final OverlayManager overlayManager, - final ConfigManager configManager, - final EventBus eventbus) + private InfoBoxManager( + final RuneLiteConfig runeLiteConfig, + final TooltipManager tooltipManager, + final Client client, + final EventBus eventBus, + final OverlayManager overlayManager, + final ConfigManager configManager) { this.runeLiteConfig = runeLiteConfig; this.tooltipManager = tooltipManager; @@ -94,21 +95,19 @@ public class InfoBoxManager this.eventBus = eventBus; this.overlayManager = overlayManager; this.configManager = configManager; - - eventbus.subscribe(ConfigChanged.class, this, this::onConfigChanged); - eventbus.subscribe(InfoBoxMenuClicked.class, this, this::onInfoBoxMenuClicked); } - private void onConfigChanged(ConfigChanged event) + @Subscribe + public void onConfigChanged(ConfigChanged event) { if (event.getGroup().equals("runelite") && event.getKey().equals("infoBoxSize")) { - layers.values().forEach(l -> l.getInfoBoxes().forEach(this::updateInfoBoxImage)); } } - private void onInfoBoxMenuClicked(InfoBoxMenuClicked event) + @Subscribe + public void onInfoBoxMenuClicked(InfoBoxMenuClicked event) { if (DETACH.equals(event.getEntry().getOption())) { @@ -135,7 +134,7 @@ public class InfoBoxManager public void addInfoBox(InfoBox infoBox) { - Objects.requireNonNull(infoBox); + Preconditions.checkNotNull(infoBox); log.debug("Adding InfoBox {}", infoBox); updateInfoBoxImage(infoBox); @@ -241,6 +240,7 @@ public class InfoBoxManager g.dispose(); resultImage = scaledImage; } + infoBox.setScaledImage(resultImage); } @@ -271,7 +271,7 @@ public class InfoBoxManager name, orientation); overlayManager.add(infoBoxOverlay); - + eventBus.register(infoBoxOverlay); return infoBoxOverlay; } @@ -381,7 +381,6 @@ public class InfoBoxManager /** * Find insertion point for the given key into the given sorted list. If key already exists in the list, * return the index after the last occurrence. - * * @param list * @param key * @param c diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxOverlay.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxOverlay.java index a6351e62a3..9e85df2edd 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxOverlay.java @@ -34,14 +34,14 @@ import java.awt.Rectangle; import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; -import javax.inject.Singleton; import lombok.Getter; import lombok.NonNull; import net.runelite.api.Client; -import net.runelite.api.MenuOpcode; +import net.runelite.api.MenuAction; import net.runelite.api.events.MenuOptionClicked; import net.runelite.client.config.RuneLiteConfig; import net.runelite.client.eventbus.EventBus; +import net.runelite.client.eventbus.Subscribe; import net.runelite.client.events.InfoBoxMenuClicked; import net.runelite.client.ui.overlay.Overlay; import net.runelite.client.ui.overlay.OverlayMenuEntry; @@ -53,7 +53,6 @@ import net.runelite.client.ui.overlay.components.LayoutableRenderableEntity; import net.runelite.client.ui.overlay.tooltip.Tooltip; import net.runelite.client.ui.overlay.tooltip.TooltipManager; -@Singleton public class InfoBoxOverlay extends OverlayPanel { private static final int GAP = 1; @@ -96,8 +95,6 @@ public class InfoBoxOverlay extends OverlayPanel panelComponent.setBackgroundColor(null); panelComponent.setBorder(new Rectangle()); panelComponent.setGap(new Point(GAP, GAP)); - - eventBus.subscribe(MenuOptionClicked.class, this, this::onMenuOptionClicked); } @Override @@ -190,17 +187,23 @@ public class InfoBoxOverlay extends OverlayPanel return hoveredComponent == null ? Collections.emptyList() : hoveredComponent.getInfoBox().getMenuEntries(); } + @Subscribe public void onMenuOptionClicked(MenuOptionClicked menuOptionClicked) { - if (menuOptionClicked.getMenuOpcode() != MenuOpcode.RUNELITE_INFOBOX || hoveredComponent == null) + if (menuOptionClicked.getMenuAction() != MenuAction.RUNELITE_INFOBOX || hoveredComponent == null) { return; } InfoBox infoBox = hoveredComponent.getInfoBox(); - infoBox.getMenuEntries().stream() - .filter(me -> me.getOption().equals(menuOptionClicked.getOption())) - .findAny().ifPresent(overlayMenuEntry -> eventBus.post(InfoBoxMenuClicked.class, new InfoBoxMenuClicked(overlayMenuEntry, infoBox))); + OverlayMenuEntry overlayMenuEntry = infoBox.getMenuEntries().stream() + .filter(me -> me.getOption().equals(menuOptionClicked.getMenuOption())) + .findAny() + .orElse(null); + if (overlayMenuEntry != null) + { + eventBus.post(new InfoBoxMenuClicked(overlayMenuEntry, infoBox)); + } } @Override diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/worldmap/WorldMapOverlay.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/worldmap/WorldMapOverlay.java index 51b719b505..7e301c8e61 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/worldmap/WorldMapOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/worldmap/WorldMapOverlay.java @@ -183,7 +183,6 @@ public class WorldMapOverlay extends Overlay /** * Get the screen coordinates for a WorldPoint on the world map - * * @param worldPoint WorldPoint to get screen coordinates of * @return Point of screen coordinates of the center of the world point */ @@ -196,7 +195,7 @@ public class WorldMapOverlay extends Overlay return null; } - float pixelsPerTile = ro.getWorldMapZoom(); + Float pixelsPerTile = ro.getWorldMapZoom(); Widget map = client.getWidget(WidgetInfo.WORLD_MAP_VIEW); if (map != null) @@ -233,8 +232,8 @@ public class WorldMapOverlay extends Overlay * Gets a clip area which excludes the area of widgets which overlay the world map. * * @param baseRectangle The base area to clip from - * @return An {@link Area} representing baseRectangle, with the area - * of visible widgets overlaying the world map clipped from it. + * @return An {@link Area} representing baseRectangle, with the area + * of visible widgets overlaying the world map clipped from it. */ private Area getWorldMapClipArea(Rectangle baseRectangle) { @@ -288,6 +287,7 @@ public class WorldMapOverlay extends Overlay graphics.setColor(JagexColors.TOOLTIP_BORDER); graphics.drawRect((int) tooltipRect.getX(), (int) tooltipRect.getY(), (int) tooltipRect.getWidth(), (int) tooltipRect.getHeight()); + graphics.setColor(JagexColors.TOOLTIP_TEXT); for (int i = 0; i < rows.size(); i++) { diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/worldmap/WorldMapPoint.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/worldmap/WorldMapPoint.java index 289a55d6dc..878b0125a0 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/worldmap/WorldMapPoint.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/worldmap/WorldMapPoint.java @@ -27,10 +27,10 @@ package net.runelite.client.ui.overlay.worldmap; import java.awt.Rectangle; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; -import javax.annotation.Nullable; import lombok.Data; import net.runelite.api.Point; import net.runelite.api.coords.WorldPoint; +import javax.annotation.Nullable; @Data public class WorldMapPoint diff --git a/runelite-client/src/main/java/net/runelite/client/ui/skin/ObsidianSkin.java b/runelite-client/src/main/java/net/runelite/client/ui/skin/ObsidianSkin.java index 83d59167cd..6cd6e140d4 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/skin/ObsidianSkin.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/skin/ObsidianSkin.java @@ -46,7 +46,7 @@ import org.pushingpixels.substance.api.painter.overlay.TopLineOverlayPainter; import org.pushingpixels.substance.api.shaper.ClassicButtonShaper; import org.pushingpixels.substance.internal.utils.SubstanceColorUtilities; -class ObsidianSkin extends SubstanceSkin +public class ObsidianSkin extends SubstanceSkin { /** * Display name for this skin. @@ -65,11 +65,10 @@ class ObsidianSkin extends SubstanceSkin final SubstanceColorSchemeBundle defaultSchemeBundle = new SubstanceColorSchemeBundle( activeScheme, enabledScheme, enabledScheme); - - defaultSchemeBundle.registerAlpha(0.6f, ComponentState.DISABLED_UNSELECTED); - defaultSchemeBundle.registerColorScheme(enabledScheme, ComponentState.DISABLED_UNSELECTED); - defaultSchemeBundle.registerAlpha(0.6f, ComponentState.DISABLED_SELECTED); - defaultSchemeBundle.registerColorScheme(activeScheme, ComponentState.DISABLED_SELECTED); + defaultSchemeBundle.registerColorScheme(enabledScheme, 0.6f, + ComponentState.DISABLED_UNSELECTED); + defaultSchemeBundle.registerColorScheme(activeScheme, 0.6f, + ComponentState.DISABLED_SELECTED); // borders final SubstanceColorScheme borderDisabledSelectedScheme = schemes @@ -83,8 +82,7 @@ class ObsidianSkin extends SubstanceSkin final SubstanceColorScheme markActiveScheme = schemes.get("RuneLite Mark Active"); defaultSchemeBundle.registerColorScheme(markActiveScheme, ColorSchemeAssociationKind.MARK, ComponentState.getActiveStates()); - defaultSchemeBundle.registerAlpha(0.6f, ComponentState.DISABLED_SELECTED, ComponentState.DISABLED_UNSELECTED); - defaultSchemeBundle.registerColorScheme(markActiveScheme, + defaultSchemeBundle.registerColorScheme(markActiveScheme, 0.6f, ColorSchemeAssociationKind.MARK, ComponentState.DISABLED_SELECTED, ComponentState.DISABLED_UNSELECTED); @@ -104,8 +102,8 @@ class ObsidianSkin extends SubstanceSkin final SubstanceColorSchemeBundle decorationsSchemeBundle = new SubstanceColorSchemeBundle( activeScheme, enabledScheme, enabledScheme); - decorationsSchemeBundle.registerAlpha(0.5f, ComponentState.DISABLED_UNSELECTED); - decorationsSchemeBundle.registerColorScheme(enabledScheme, ComponentState.DISABLED_UNSELECTED); + decorationsSchemeBundle.registerColorScheme(enabledScheme, 0.5f, + ComponentState.DISABLED_UNSELECTED); // borders decorationsSchemeBundle.registerColorScheme(borderDisabledSelectedScheme, @@ -131,8 +129,8 @@ class ObsidianSkin extends SubstanceSkin final SubstanceColorSchemeBundle headerSchemeBundle = new SubstanceColorSchemeBundle(activeScheme, enabledScheme, enabledScheme); - headerSchemeBundle.registerAlpha(0.5f, ComponentState.DISABLED_UNSELECTED); - headerSchemeBundle.registerColorScheme(enabledScheme, ComponentState.DISABLED_UNSELECTED); + headerSchemeBundle.registerColorScheme(enabledScheme, 0.5f, + ComponentState.DISABLED_UNSELECTED); // borders final SubstanceColorScheme headerBorderScheme = schemes.get("RuneLite Header Border"); @@ -144,15 +142,13 @@ class ObsidianSkin extends SubstanceSkin headerSchemeBundle.registerColorScheme(markActiveScheme, ColorSchemeAssociationKind.MARK, ComponentState.getActiveStates()); - headerSchemeBundle.registerAlpha(0.7f, ComponentState.ROLLOVER_UNSELECTED, - ComponentState.ROLLOVER_ARMED, ComponentState.ARMED); - headerSchemeBundle.registerHighlightColorScheme(activeScheme, + headerSchemeBundle.registerHighlightColorScheme(activeScheme, 0.7f, ComponentState.ROLLOVER_UNSELECTED, ComponentState.ROLLOVER_ARMED, ComponentState.ARMED); - headerSchemeBundle.registerAlpha(0.8f, ComponentState.SELECTED); - headerSchemeBundle.registerHighlightColorScheme(activeScheme, ComponentState.SELECTED); - headerSchemeBundle.registerAlpha(1.0f, ComponentState.ROLLOVER_SELECTED); - headerSchemeBundle.registerHighlightColorScheme(activeScheme, ComponentState.ROLLOVER_SELECTED); + headerSchemeBundle.registerHighlightColorScheme(activeScheme, 0.8f, + ComponentState.SELECTED); + headerSchemeBundle.registerHighlightColorScheme(activeScheme, 1.0f, + ComponentState.ROLLOVER_SELECTED); final SubstanceColorScheme headerWatermarkScheme = schemes.get("RuneLite Header Watermark"); diff --git a/runelite-client/src/main/java/net/runelite/client/util/ColorUtil.java b/runelite-client/src/main/java/net/runelite/client/util/ColorUtil.java index 56cb75ca7d..c0a0102ee1 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/ColorUtil.java +++ b/runelite-client/src/main/java/net/runelite/client/util/ColorUtil.java @@ -24,6 +24,7 @@ */ package net.runelite.client.util; +import com.google.common.primitives.Ints; import java.awt.Color; import java.util.regex.Pattern; import javax.annotation.Nonnull; @@ -42,7 +43,7 @@ public class ColorUtil * Creates a color tag from the given color. * * @param color The Color to create a tag from. - * @return A string of the color tag for the given color. + * @return A string of the color tag for the given color. */ public static String colorTag(Color color) { @@ -54,7 +55,7 @@ public class ColorUtil * * @param str The string to be colorized. * @param color The color to be used in the color tag. - * @return The passed str with a prepended color tag. + * @return The passed str with a prepended color tag. */ public static String prependColorTag(final String str, final Color color) { @@ -66,7 +67,7 @@ public class ColorUtil * * @param str The string to be colorized. * @param color The color to be used in the color tag. - * @return The passed str wrapped with opening and closing color tags. + * @return The passed str wrapped with opening and closing color tags. */ public static String wrapWithColorTag(final String str, final Color color) { @@ -77,7 +78,7 @@ public class ColorUtil * Converts a given color to it's hexadecimal equivalent. * * @param color Color to get hexadecimal string from. - * @return Hexadecimal string representing the given color, in the form "#abcdef". + * @return Hexadecimal string representing the given color, in the form "#abcdef". */ public static String toHexColor(final Color color) { @@ -90,7 +91,7 @@ public class ColorUtil * @param a first color * @param b second color * @param t factor - * @return interpolated color + * @return interpolated color */ public static Color colorLerp(final Color a, final Color b, final double t) { @@ -112,7 +113,7 @@ public class ColorUtil * Gets the RGB hex color code of the passed color. * * @param color The color to get a hex code from. - * @return A lower-cased string of the RGB hex code of color. + * @return A lower-cased string of the RGB hex code of color. */ public static String colorToHexCode(final Color color) { @@ -123,28 +124,18 @@ public class ColorUtil * Gets the ARGB hex color code of the passed color. * * @param color The color to get a hex code from. - * @return A lower-cased string of the ARGB hex code of color. + * @return A lower-cased string of the ARGB hex code of color. */ public static String colorToAlphaHexCode(final Color color) { return String.format("%08x", color.getRGB()); } - static boolean isFullyTransparent(final Color color) - { - return color.getAlpha() == 0; - } - - static boolean isNotFullyTransparent(final Color color) - { - return !isFullyTransparent(color); - } - /** * Determines if the passed hex string is an alpha hex color. * * @param hex The hex to test. - * @return boolean + * @return boolean */ public static boolean isAlphaHex(String hex) { @@ -155,7 +146,7 @@ public class ColorUtil * Determines if the passed hex string is a hex color. * * @param hex The hex to test. - * @return boolean + * @return boolean */ public static boolean isHex(String hex) { @@ -166,18 +157,18 @@ public class ColorUtil * Limits an int to the rgba value range (0-255) * * @param value The value for the r, g, b, or a. - * @return An int between 0 - 255. + * @return An int between 0 - 255. */ public static int constrainValue(int value) { - return MiscUtils.clamp(value, MIN_RGB_VALUE, MAX_RGB_VALUE); + return Ints.constrainToRange(value, MIN_RGB_VALUE, MAX_RGB_VALUE); } /** * Gets the Color from the passed int string. * * @param string The int to get a Color object from. - * @return A Color of the int of color. + * @return A Color of the int of color. */ public static Color fromString(String string) { @@ -196,7 +187,7 @@ public class ColorUtil * Gets the Color from the passed hex string. * * @param hex The hex to get a Color object from. - * @return A Color of the hex code of color. + * @return A Color of the hex code of color. */ public static Color fromHex(String hex) { @@ -229,7 +220,6 @@ public class ColorUtil /** * Creates color from passed object hash code - * * @param object object with hashCode * @return color */ @@ -239,93 +229,4 @@ public class ColorUtil float h = (i % 360) / 360f; return Color.getHSBColor(h, 1, 1); } - - /** - * Modifies the alpha component on a Color - * - * @param color The color to set the alpha value on - * @param alpha The alpha value to set on the color - * @return color - */ - public static int setAlphaComponent(Color color, int alpha) - { - return setAlphaComponent(color.getRGB(), alpha); - } - - /** - * Modifies the alpha component on a Color - * - * @param color The color to set the alpha value on - * @param alpha The alpha value to set on the color - * @return color - */ - public static int setAlphaComponent(int color, int alpha) - { - if (alpha < 0 || alpha > 255) - { - throw new IllegalArgumentException("alpha must be between 0 and 255."); - } - return (color & 0x00ffffff) | (alpha << 24); - } - - /** - * Returns the color a ' (level-xxx)' would have, based on - * the difference between your and their level. - * This method is the same as in rs-client. - * - * @param theirLvl The level you're comparing against - * @param yourLvl Your level - */ - public static String getLevelColorString(final int theirLvl, final int yourLvl) - { - final int diff = yourLvl - theirLvl; - - if (diff < -9) - { - return colorStartTag(0xff0000); - } - else if (diff < -6) - { - return colorStartTag(0xff3000); - } - else if (diff < -3) - { - return colorStartTag(0xff7000); - } - else if (diff < 0) - { - return colorStartTag(0xffb000); - } - else if (diff > 9) - { - return colorStartTag(0x00ff00); - } - else if (diff > 6) - { - return colorStartTag(0x40ff00); - } - else if (diff > 3) - { - return colorStartTag(0x80ff00); - } - else if (diff > 0) - { - return colorStartTag(0xc0ff00); - } - else - { - return colorStartTag(0xffff00); - } - } - - /** - * Creates a color start tag from a rgb color as a int value - * - * @param rgb the int value of a rgb color - * @return a color tag which can be put in front of stuff - */ - public static String colorStartTag(final int rgb) - { - return ""; - } } diff --git a/runelite-client/src/main/java/net/runelite/client/util/Clipboard.java b/runelite-client/src/main/java/net/runelite/client/util/CountingInputStream.java similarity index 60% rename from runelite-client/src/main/java/net/runelite/client/util/Clipboard.java rename to runelite-client/src/main/java/net/runelite/client/util/CountingInputStream.java index 37170d0fa6..8543d65295 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/Clipboard.java +++ b/runelite-client/src/main/java/net/runelite/client/util/CountingInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Connor + * Copyright (c) 2019 Abex * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,41 +22,60 @@ * (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.util; -import java.awt.Toolkit; -import java.awt.datatransfer.DataFlavor; -import java.awt.datatransfer.StringSelection; -import java.awt.datatransfer.Transferable; -import java.awt.datatransfer.UnsupportedFlavorException; +import java.io.FilterInputStream; import java.io.IOException; +import java.io.InputStream; +import java.util.function.IntConsumer; -public class Clipboard +public class CountingInputStream extends FilterInputStream { - public static String retrieve() + private final IntConsumer changed; + + public CountingInputStream(InputStream in, IntConsumer changed) { - Transferable contents = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null); - - if (contents == null || !contents.isDataFlavorSupported(DataFlavor.stringFlavor)) - { - return null; - } - - try - { - return (String) contents.getTransferData(DataFlavor.stringFlavor); - } - catch (UnsupportedFlavorException | IOException ex) - { - return null; - } + super(in); + this.changed = changed; } - public static void store(String contents) - { - final StringSelection selection = new StringSelection(contents); + private int read = 0; - Toolkit.getDefaultToolkit().getSystemClipboard().setContents(selection, null); + @Override + public int read(byte[] b, int off, int len) throws IOException + { + int thisRead = super.read(b, off, len); + if (thisRead > 0) + { + this.read += thisRead; + } + changed.accept(this.read); + return thisRead; + } + + @Override + public int read() throws IOException + { + int val = super.read(); + if (val != -1) + { + this.read++; + } + return val; + } + + @Override + public long skip(long n) throws IOException + { + long thisRead = in.skip(n); + this.read += thisRead; + changed.accept(this.read); + return thisRead; + } + + @Override + public boolean markSupported() + { + return false; } } diff --git a/runelite-client/src/main/java/net/runelite/client/util/DeferredDocumentChangedListener.java b/runelite-client/src/main/java/net/runelite/client/util/DeferredDocumentChangedListener.java deleted file mode 100644 index f8f321a1a6..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/util/DeferredDocumentChangedListener.java +++ /dev/null @@ -1,58 +0,0 @@ -package net.runelite.client.util; - -import java.util.ArrayList; -import java.util.List; -import javax.swing.Timer; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; - -public class DeferredDocumentChangedListener implements DocumentListener -{ - private final Timer timer; - private final List listeners; - - public DeferredDocumentChangedListener() - { - listeners = new ArrayList<>(25); - timer = new Timer(200, e -> fireStateChanged()); - timer.setRepeats(false); - } - - public void addChangeListener(ChangeListener listener) - { - listeners.add(listener); - } - - private void fireStateChanged() - { - if (!listeners.isEmpty()) - { - ChangeEvent evt = new ChangeEvent(this); - for (ChangeListener listener : listeners) - { - listener.stateChanged(evt); - } - } - } - - @Override - public void insertUpdate(DocumentEvent e) - { - timer.restart(); - } - - @Override - public void removeUpdate(DocumentEvent e) - { - timer.restart(); - } - - @Override - public void changedUpdate(DocumentEvent e) - { - timer.restart(); - } - -} diff --git a/runelite-client/src/main/java/net/runelite/client/util/DeferredEventBus.java b/runelite-client/src/main/java/net/runelite/client/util/DeferredEventBus.java index 176a4a36ef..183fce738b 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/DeferredEventBus.java +++ b/runelite-client/src/main/java/net/runelite/client/util/DeferredEventBus.java @@ -24,21 +24,17 @@ */ package net.runelite.client.util; -import io.reactivex.rxjava3.annotations.NonNull; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import javax.inject.Inject; import javax.inject.Singleton; -import net.runelite.api.events.Event; import net.runelite.client.eventbus.EventBus; -import org.apache.commons.lang3.tuple.ImmutablePair; -import org.apache.commons.lang3.tuple.Pair; @Singleton public class DeferredEventBus extends EventBus { private final EventBus eventBus; - private final Queue> pendingEvents = new ConcurrentLinkedQueue<>(); + private final Queue pendingEvents = new ConcurrentLinkedQueue<>(); @Inject private DeferredEventBus(EventBus eventBus) @@ -47,22 +43,30 @@ public class DeferredEventBus extends EventBus } @Override - public void post(Class eventClass, @NonNull T event) + public void register(Object object) { - pendingEvents.add(new ImmutablePair<>(eventClass, event)); + eventBus.register(object); + } + + @Override + public void unregister(Object object) + { + eventBus.unregister(object); + } + + @Override + public void post(Object object) + { + pendingEvents.add(object); } - @SuppressWarnings("unchecked") public void replay() { int size = pendingEvents.size(); while (size-- > 0) { - Pair eventPair = pendingEvents.poll(); - if (eventPair != null) - { - eventBus.post(eventPair.getKey(), eventPair.getValue()); - } + Object object = pendingEvents.poll(); + eventBus.post(object); } } } diff --git a/runelite-client/src/main/java/net/runelite/client/util/ExecutorServiceExceptionLogger.java b/runelite-client/src/main/java/net/runelite/client/util/ExecutorServiceExceptionLogger.java index 165425210d..fe8902d663 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/ExecutorServiceExceptionLogger.java +++ b/runelite-client/src/main/java/net/runelite/client/util/ExecutorServiceExceptionLogger.java @@ -34,7 +34,6 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import lombok.RequiredArgsConstructor; -import org.jetbrains.annotations.NotNull; /** * Wrapper for ${@link ScheduledExecutorService} that will log all uncaught exceptions as warning to console @@ -54,29 +53,26 @@ public class ExecutorServiceExceptionLogger implements ScheduledExecutorService return CallableExceptionLogger.wrap(command); } - @NotNull @Override - public Future submit(@NotNull Callable task) + public Future submit(Callable task) { return service.submit(monitor(task)); } - @NotNull @Override - public Future submit(@NotNull Runnable task, T result) + public Future submit(Runnable task, T result) { return service.submit(monitor(task), result); } - @NotNull @Override - public Future submit(@NotNull Runnable task) + public Future submit(Runnable task) { return service.submit(monitor(task)); } @Override - public void execute(@NotNull Runnable command) + public void execute(Runnable command) { service.execute(monitor(command)); } @@ -89,7 +85,6 @@ public class ExecutorServiceExceptionLogger implements ScheduledExecutorService service.shutdown(); } - @NotNull @Override public List shutdownNow() { @@ -109,62 +104,55 @@ public class ExecutorServiceExceptionLogger implements ScheduledExecutorService } @Override - public boolean awaitTermination(long timeout, @NotNull TimeUnit unit) throws InterruptedException + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { return service.awaitTermination(timeout, unit); } - @NotNull @Override - public List> invokeAll(@NotNull Collection> tasks) throws InterruptedException + public List> invokeAll(Collection> tasks) throws InterruptedException { return service.invokeAll(tasks); } - @NotNull @Override - public List> invokeAll(@NotNull Collection> tasks, long timeout, @NotNull TimeUnit unit) throws InterruptedException + public List> invokeAll(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException { return service.invokeAll(tasks, timeout, unit); } - @NotNull @Override - public T invokeAny(@NotNull Collection> tasks) throws InterruptedException, ExecutionException + public T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException { return service.invokeAny(tasks); } @Override - public T invokeAny(@NotNull Collection> tasks, long timeout, @NotNull TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException + public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return service.invokeAny(tasks, timeout, unit); } - @NotNull @Override - public ScheduledFuture schedule(@NotNull Runnable command, long delay, @NotNull TimeUnit unit) + public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) { return service.schedule(command, delay, unit); } - @NotNull @Override - public ScheduledFuture schedule(@NotNull Callable callable, long delay, @NotNull TimeUnit unit) + public ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit) { return service.schedule(callable, delay, unit); } - @NotNull @Override - public ScheduledFuture scheduleAtFixedRate(@NotNull Runnable command, long initialDelay, long period, @NotNull TimeUnit unit) + public ScheduledFuture scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { return service.scheduleAtFixedRate(command, initialDelay, period, unit); } - @NotNull @Override - public ScheduledFuture scheduleWithFixedDelay(@NotNull Runnable command, long initialDelay, long delay, @NotNull TimeUnit unit) + public ScheduledFuture scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { return service.scheduleWithFixedDelay(command, initialDelay, delay, unit); } diff --git a/runelite-client/src/main/java/net/runelite/client/util/GameEventManager.java b/runelite-client/src/main/java/net/runelite/client/util/GameEventManager.java index bc2ab91b3d..31bf351af4 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/GameEventManager.java +++ b/runelite-client/src/main/java/net/runelite/client/util/GameEventManager.java @@ -33,6 +33,7 @@ import javax.inject.Singleton; import net.runelite.api.Client; import net.runelite.api.Constants; import net.runelite.api.GameState; +import net.runelite.api.TileItem; import net.runelite.api.InventoryID; import net.runelite.api.ItemContainer; import net.runelite.api.NPC; @@ -40,7 +41,6 @@ import net.runelite.api.Node; import net.runelite.api.Player; import net.runelite.api.Scene; import net.runelite.api.Tile; -import net.runelite.api.TileItem; import net.runelite.api.events.DecorativeObjectSpawned; import net.runelite.api.events.GameObjectSpawned; import net.runelite.api.events.GroundObjectSpawned; @@ -110,7 +110,7 @@ public class GameEventManager clientThread.invoke(() -> { - // eventBus.register(subscriber); + eventBus.register(subscriber); for (final InventoryID inventory : InventoryID.values()) { @@ -118,7 +118,7 @@ public class GameEventManager if (itemContainer != null) { - eventBus.post(ItemContainerChanged.class, new ItemContainerChanged(inventory.getId(), itemContainer)); + eventBus.post(new ItemContainerChanged(inventory.getId(), itemContainer)); } } @@ -127,7 +127,7 @@ public class GameEventManager if (npc != null) { final NpcSpawned npcSpawned = new NpcSpawned(npc); - eventBus.post(NpcSpawned.class, npcSpawned); + eventBus.post(npcSpawned); } } @@ -136,7 +136,7 @@ public class GameEventManager if (player != null) { final PlayerSpawned playerSpawned = new PlayerSpawned(player); - eventBus.post(PlayerSpawned.class, playerSpawned); + eventBus.post(playerSpawned); } } @@ -147,7 +147,7 @@ public class GameEventManager final WallObjectSpawned objectSpawned = new WallObjectSpawned(); objectSpawned.setTile(tile); objectSpawned.setWallObject(object); - eventBus.post(WallObjectSpawned.class, objectSpawned); + eventBus.post(objectSpawned); }); Optional.ofNullable(tile.getDecorativeObject()).ifPresent(object -> @@ -155,7 +155,7 @@ public class GameEventManager final DecorativeObjectSpawned objectSpawned = new DecorativeObjectSpawned(); objectSpawned.setTile(tile); objectSpawned.setDecorativeObject(object); - eventBus.post(DecorativeObjectSpawned.class, objectSpawned); + eventBus.post(objectSpawned); }); Optional.ofNullable(tile.getGroundObject()).ifPresent(object -> @@ -163,15 +163,17 @@ public class GameEventManager final GroundObjectSpawned objectSpawned = new GroundObjectSpawned(); objectSpawned.setTile(tile); objectSpawned.setGroundObject(object); - eventBus.post(GroundObjectSpawned.class, objectSpawned); + eventBus.post(objectSpawned); }); Arrays.stream(tile.getGameObjects()) .filter(Objects::nonNull) .forEach(object -> { - final GameObjectSpawned objectSpawned = new GameObjectSpawned(tile, object); - eventBus.post(GameObjectSpawned.class, objectSpawned); + final GameObjectSpawned objectSpawned = new GameObjectSpawned(); + objectSpawned.setTile(tile); + objectSpawned.setGameObject(object); + eventBus.post(objectSpawned); }); Optional.ofNullable(tile.getItemLayer()).ifPresent(itemLayer -> @@ -185,10 +187,12 @@ public class GameEventManager current = current.getNext(); final ItemSpawned itemSpawned = new ItemSpawned(tile, item); - eventBus.post(ItemSpawned.class, itemSpawned); + eventBus.post(itemSpawned); } }); }); + + eventBus.unregister(subscriber); }); } } diff --git a/runelite-client/src/main/java/net/runelite/client/util/Graceful.java b/runelite-client/src/main/java/net/runelite/client/util/Graceful.java deleted file mode 100644 index f9a16247a7..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/util/Graceful.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (c) 2018 raiyni - * 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.util; - -import com.google.common.collect.ImmutableSet; -import net.runelite.api.EquipmentInventorySlot; -import net.runelite.api.Item; -import net.runelite.api.ItemContainer; -import static net.runelite.api.ItemID.*; - -public enum Graceful -{ - // TODO: It would be nice if we have the IDs for just the equipped variants of the Graceful set items. - HOOD( - GRACEFUL_HOOD_11851, GRACEFUL_HOOD_13579, GRACEFUL_HOOD_13580, GRACEFUL_HOOD_13591, GRACEFUL_HOOD_13592, - GRACEFUL_HOOD_13603, GRACEFUL_HOOD_13604, GRACEFUL_HOOD_13615, GRACEFUL_HOOD_13616, GRACEFUL_HOOD_13627, - GRACEFUL_HOOD_13628, GRACEFUL_HOOD_13667, GRACEFUL_HOOD_13668, GRACEFUL_HOOD_21061, GRACEFUL_HOOD_21063, - GRACEFUL_HOOD_24743, GRACEFUL_HOOD_24745 - ), - - TOP( - GRACEFUL_TOP_11855, GRACEFUL_TOP_13583, GRACEFUL_TOP_13584, GRACEFUL_TOP_13595, GRACEFUL_TOP_13596, - GRACEFUL_TOP_13607, GRACEFUL_TOP_13608, GRACEFUL_TOP_13619, GRACEFUL_TOP_13620, GRACEFUL_TOP_13631, - GRACEFUL_TOP_13632, GRACEFUL_TOP_13671, GRACEFUL_TOP_13672, GRACEFUL_TOP_21067, GRACEFUL_TOP_21069, - GRACEFUL_TOP_24749, GRACEFUL_TOP_24751 - ), - - LEGS( - GRACEFUL_LEGS_11857, GRACEFUL_LEGS_13585, GRACEFUL_LEGS_13586, GRACEFUL_LEGS_13597, GRACEFUL_LEGS_13598, - GRACEFUL_LEGS_13609, GRACEFUL_LEGS_13610, GRACEFUL_LEGS_13621, GRACEFUL_LEGS_13622, GRACEFUL_LEGS_13633, - GRACEFUL_LEGS_13634, GRACEFUL_LEGS_13673, GRACEFUL_LEGS_13674, GRACEFUL_LEGS_21070, GRACEFUL_LEGS_21072, - GRACEFUL_LEGS_24752, GRACEFUL_LEGS_24754 - ), - - GLOVES( - GRACEFUL_GLOVES_11859, GRACEFUL_GLOVES_13587, GRACEFUL_GLOVES_13588, GRACEFUL_GLOVES_13599, GRACEFUL_GLOVES_13600, - GRACEFUL_GLOVES_13611, GRACEFUL_GLOVES_13612, GRACEFUL_GLOVES_13623, GRACEFUL_GLOVES_13624, GRACEFUL_GLOVES_13635, - GRACEFUL_GLOVES_13636, GRACEFUL_GLOVES_13675, GRACEFUL_GLOVES_13676, GRACEFUL_GLOVES_21073, GRACEFUL_GLOVES_21075, - GRACEFUL_GLOVES_24755, GRACEFUL_GLOVES_24757 - ), - - BOOTS( - GRACEFUL_BOOTS_11861, GRACEFUL_BOOTS_13589, GRACEFUL_BOOTS_13590, GRACEFUL_BOOTS_13601, GRACEFUL_BOOTS_13602, - GRACEFUL_BOOTS_13613, GRACEFUL_BOOTS_13614, GRACEFUL_BOOTS_13625, GRACEFUL_BOOTS_13626, GRACEFUL_BOOTS_13637, - GRACEFUL_BOOTS_13638, GRACEFUL_BOOTS_13677, GRACEFUL_BOOTS_13678, GRACEFUL_BOOTS_21076, GRACEFUL_BOOTS_21078, - GRACEFUL_BOOTS_24758, GRACEFUL_BOOTS_24760 - ), - - // Agility skill capes and the non-cosmetic Max capes also count for the Graceful set effect - CAPE( - GRACEFUL_CAPE_11853, GRACEFUL_CAPE_13581, GRACEFUL_CAPE_13582, GRACEFUL_CAPE_13593, GRACEFUL_CAPE_13594, - GRACEFUL_CAPE_13605, GRACEFUL_CAPE_13606, GRACEFUL_CAPE_13617, GRACEFUL_CAPE_13618, GRACEFUL_CAPE_13629, - GRACEFUL_CAPE_13630, GRACEFUL_CAPE_13669, GRACEFUL_CAPE_13670, GRACEFUL_CAPE_21064, GRACEFUL_CAPE_21066, - GRACEFUL_CAPE_24746, GRACEFUL_CAPE_24748, AGILITY_CAPE, AGILITY_CAPET, MAX_CAPE - ); - - private final ImmutableSet ids; - - Graceful(Integer... ids) - { - this.ids = ImmutableSet.copyOf(ids); - } - - public static double calculateRecoveryRate(final ItemContainer equipment) - { - double rate = 1; - - if (equipment == null) - { - return rate; - } - - final Item[] items = equipment.getItems(); - - if (hasFullSet(items)) - { - return 1.3; - } - - if (hasHood(items)) - { - rate += 0.03; - } - - if (hasTop(items)) - { - rate += 0.04; - } - - if (hasLegs(items)) - { - rate += 0.04; - } - - if (hasGloves(items)) - { - rate += 0.03; - } - - if (hasBoots(items)) - { - rate += 0.03; - } - - if (hasCape(items)) - { - rate += 0.03; - } - - return rate; - } - - public static boolean hasHood(final Item[] items) - { - if (items.length <= EquipmentInventorySlot.HEAD.getSlotIdx()) - { - return false; - } - - return HOOD.ids.contains(items[EquipmentInventorySlot.HEAD.getSlotIdx()].getId()); - } - - public static boolean hasTop(final Item[] items) - { - if (items.length <= EquipmentInventorySlot.BODY.getSlotIdx()) - { - return false; - } - - return TOP.ids.contains(items[EquipmentInventorySlot.BODY.getSlotIdx()].getId()); - } - - public static boolean hasLegs(final Item[] items) - { - if (items.length <= EquipmentInventorySlot.LEGS.getSlotIdx()) - { - return false; - } - - return LEGS.ids.contains(items[EquipmentInventorySlot.LEGS.getSlotIdx()].getId()); - } - - public static boolean hasGloves(final Item[] items) - { - if (items.length <= EquipmentInventorySlot.GLOVES.getSlotIdx()) - { - return false; - } - - return GLOVES.ids.contains(items[EquipmentInventorySlot.GLOVES.getSlotIdx()].getId()); - } - - public static boolean hasBoots(final Item[] items) - { - if (items.length <= EquipmentInventorySlot.BOOTS.getSlotIdx()) - { - return false; - } - - return BOOTS.ids.contains(items[EquipmentInventorySlot.BOOTS.getSlotIdx()].getId()); - } - - public static boolean hasCape(final Item[] items) - { - if (items.length <= EquipmentInventorySlot.CAPE.getSlotIdx()) - { - return false; - } - - return CAPE.ids.contains(items[EquipmentInventorySlot.CAPE.getSlotIdx()].getId()); - } - - public static boolean hasFullSet(final Item[] items) - { - if (items.length <= EquipmentInventorySlot.BOOTS.getSlotIdx()) - { - return false; - } - - return HOOD.ids.contains(items[EquipmentInventorySlot.HEAD.getSlotIdx()].getId()) - && TOP.ids.contains(items[EquipmentInventorySlot.BODY.getSlotIdx()].getId()) - && LEGS.ids.contains(items[EquipmentInventorySlot.LEGS.getSlotIdx()].getId()) - && GLOVES.ids.contains(items[EquipmentInventorySlot.GLOVES.getSlotIdx()].getId()) - && BOOTS.ids.contains(items[EquipmentInventorySlot.BOOTS.getSlotIdx()].getId()) - && CAPE.ids.contains(items[EquipmentInventorySlot.CAPE.getSlotIdx()].getId()); - } -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/util/Groups.java b/runelite-client/src/main/java/net/runelite/client/util/Groups.java deleted file mode 100644 index d6e603f79f..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/util/Groups.java +++ /dev/null @@ -1,172 +0,0 @@ -package net.runelite.client.util; - -import io.reactivex.rxjava3.subjects.PublishSubject; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Future; -import javax.inject.Inject; -import javax.inject.Singleton; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; -import net.runelite.client.RuneLite; -import net.runelite.client.config.OpenOSRSConfig; -import net.runelite.client.eventbus.EventBus; -import net.runelite.client.events.ClientShutdown; -import net.runelite.client.events.ConfigChanged; -import net.runelite.client.ui.RuneLiteSplashScreen; -import org.jgroups.Address; -import org.jgroups.JChannel; -import org.jgroups.Message; -import org.jgroups.ObjectMessage; -import org.jgroups.Receiver; -import org.jgroups.View; -import org.jgroups.util.Util; - -@Slf4j -@Singleton -public class Groups implements Receiver -{ - private final OpenOSRSConfig openOSRSConfig; - private final JChannel channel; - - @Getter(AccessLevel.PUBLIC) - private int instanceCount; - @Getter(AccessLevel.PUBLIC) - private List
members; - @Getter(AccessLevel.PUBLIC) - private final Map> messageMap = new HashMap<>(); - @Getter(AccessLevel.PUBLIC) - private final PublishSubject messageStringSubject = PublishSubject.create(); - @Getter(AccessLevel.PUBLIC) - private final PublishSubject messageObjectSubject = PublishSubject.create(); - - @Inject - public Groups(OpenOSRSConfig openOSRSConfig, EventBus eventBus) throws Exception - { - this.openOSRSConfig = openOSRSConfig; - - try (final InputStream is = RuneLite.class.getResourceAsStream("/udp-openosrs.xml")) - { - this.channel = new JChannel(is) - .setName(RuneLite.uuid) - .setReceiver(this) - .setDiscardOwnMessages(true) - .connect("openosrs"); - } - - eventBus.subscribe(ClientShutdown.class, this, (e) -> { - Future f = close(); - e.waitFor(f); - }); - } - - public void broadcastSring(String command) - { - send(null, command); - } - - public void sendConfig(Address destination, ConfigChanged configChanged) - { - if (!openOSRSConfig.localSync() || RuneLiteSplashScreen.showing() || instanceCount < 2) - { - return; - } - - try - { - byte[] buffer = Util.objectToByteBuffer(configChanged); - Message message = new ObjectMessage() - .setDest(destination) - .setObject(buffer); - - channel.send(message); - } - catch (Exception e) - { - e.printStackTrace(); - } - } - - public void sendString(String command) - { - String[] messageObject = command.split(";"); - String pluginId = messageObject[1]; - - messageMap.put(pluginId, new ArrayList<>()); - - for (Address member : channel.getView().getMembers()) - { - if (member.toString().equals(RuneLite.uuid)) - { - continue; - } - - messageMap.get(pluginId).add(member); - send(member, command); - } - } - - public void send(Address destination, String command) - { - if (!openOSRSConfig.localSync() || RuneLiteSplashScreen.showing() || instanceCount < 2) - { - return; - } - - try - { - channel.send(new ObjectMessage(destination, command)); - } - catch (Exception e) - { - e.printStackTrace(); - } - } - - @Override - public void viewAccepted(View view) - { - members = view.getMembers(); - instanceCount = members.size(); - } - - @Override - public void receive(Message message) - { - if (RuneLiteSplashScreen.showing()) - { - return; - } - - if (message.getObject() instanceof String) - { - messageStringSubject.onNext(message); - } - else - { - messageObjectSubject.onNext(message); - } - - } - - private CompletableFuture close() - { - CompletableFuture future = new CompletableFuture<>(); - try - { - channel.close(); - future.complete(null); - } - catch (Exception ex) - { - future.completeExceptionally(ex); - } - - return future; - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/util/HotkeyListener.java b/runelite-client/src/main/java/net/runelite/client/util/HotkeyListener.java index be9c938f6f..2099564846 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/HotkeyListener.java +++ b/runelite-client/src/main/java/net/runelite/client/util/HotkeyListener.java @@ -83,20 +83,12 @@ public abstract class HotkeyListener implements KeyListener { if (keybind.get().matches(e)) { - if (isPressed) - { - hotkeyReleased(); - } isPressed = false; isConsumingTyped = false; } } - protected void hotkeyPressed() - { - } - - protected void hotkeyReleased() + public void hotkeyPressed() { } } diff --git a/runelite-client/src/main/java/net/runelite/client/util/ImageCapture.java b/runelite-client/src/main/java/net/runelite/client/util/ImageCapture.java index 4754a67713..ab9f660781 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/ImageCapture.java +++ b/runelite-client/src/main/java/net/runelite/client/util/ImageCapture.java @@ -106,10 +106,6 @@ public class ImageCapture final EnumSet worldTypes = client.getWorldType(); String playerDir = client.getLocalPlayer().getName(); - if (playerDir.startsWith("#")) - { - playerDir = "Username not set"; - } if (worldTypes.contains(WorldType.DEADMAN)) { playerDir += "-Deadman"; diff --git a/runelite-client/src/main/java/net/runelite/client/util/ImageUploadStyle.java b/runelite-client/src/main/java/net/runelite/client/util/ImageUploadStyle.java index 6d5a5c1c98..95c75536a4 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/ImageUploadStyle.java +++ b/runelite-client/src/main/java/net/runelite/client/util/ImageUploadStyle.java @@ -30,4 +30,4 @@ public enum ImageUploadStyle NEITHER, IMGUR, CLIPBOARD -} \ No newline at end of file +} diff --git a/runelite-client/src/main/java/net/runelite/client/util/ImageUtil.java b/runelite-client/src/main/java/net/runelite/client/util/ImageUtil.java index adb8479cf1..66e9930ab5 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/ImageUtil.java +++ b/runelite-client/src/main/java/net/runelite/client/util/ImageUtil.java @@ -1,732 +1,518 @@ -/* - * Copyright (c) 2018, Jordan Atwood - * 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.util; - -import com.google.common.primitives.Ints; -import java.awt.Color; -import java.awt.Graphics2D; -import java.awt.Image; -import java.awt.geom.AffineTransform; -import java.awt.image.AffineTransformOp; -import java.awt.image.BufferedImage; -import java.awt.image.DirectColorModel; -import java.awt.image.PixelGrabber; -import java.awt.image.RescaleOp; -import java.awt.image.WritableRaster; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import javax.imageio.ImageIO; -import javax.swing.GrayFilter; -import java.util.function.Predicate; -import lombok.extern.slf4j.Slf4j; -import net.runelite.api.Client; -import net.runelite.api.IndexedSprite; -import net.runelite.api.Sprite; - -/** - * Various Image/BufferedImage utilities. - */ -@Slf4j -public class ImageUtil -{ - static - { - ImageIO.setUseCache(false); - } - - /** - * Creates a {@link BufferedImage} from an {@link Image}. - * - * @param image An Image to be converted to a BufferedImage. - * @return A BufferedImage instance of the same given image. - */ - public static BufferedImage bufferedImageFromImage(final Image image) - { - if (image instanceof BufferedImage) - { - return (BufferedImage) image; - } - - return toARGB(image); - } - - /** - * Creates an ARGB {@link BufferedImage} from an {@link Image}. - */ - public static BufferedImage toARGB(final Image image) - { - if (image instanceof BufferedImage && ((BufferedImage) image).getType() == BufferedImage.TYPE_INT_ARGB) - { - return (BufferedImage) image; - } - - BufferedImage out = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB); - Graphics2D g2d = out.createGraphics(); - g2d.drawImage(image, 0, 0, null); - g2d.dispose(); - return out; - } - - /** - * Offsets an image's luminance by a given value. - * - * @param rawImg The image to be darkened or brightened. - * @param offset A signed 8-bit integer value to brighten or darken the image with. - * Values above 0 will brighten, and values below 0 will darken. - * @return The given image with its brightness adjusted by the given offset. - */ - public static BufferedImage luminanceOffset(final Image rawImg, final int offset) - { - BufferedImage image = toARGB(rawImg); - final float offsetFloat = (float) offset; - final int numComponents = image.getColorModel().getNumComponents(); - final float[] scales = new float[numComponents]; - final float[] offsets = new float[numComponents]; - - Arrays.fill(scales, 1f); - for (int i = 0; i < numComponents; i++) - { - offsets[i] = offsetFloat; - } - // Set alpha to not offset - offsets[numComponents - 1] = 0f; - - return offset(image, scales, offsets); - } - - /** - * Changes an images luminance by a scaling factor - * - * @param rawImg The image to be darkened or brightened. - * @param percentage The ratio to darken or brighten the given image. - * Values above 1 will brighten, and values below 1 will darken. - * @return The given image with its brightness scaled by the given percentage. - */ - public static BufferedImage luminanceScale(final Image rawImg, final float percentage) - { - BufferedImage image = toARGB(rawImg); - final int numComponents = image.getColorModel().getNumComponents(); - final float[] scales = new float[numComponents]; - final float[] offsets = new float[numComponents]; - - Arrays.fill(offsets, 0f); - for (int i = 0; i < numComponents; i++) - { - scales[i] = percentage; - } - // Set alpha to not scale - scales[numComponents - 1] = 1f; - - return offset(image, scales, offsets); - } - - /** - * Offsets an image's alpha component by a given offset. - * - * @param rawImg The image to be made more or less transparent. - * @param offset A signed 8-bit integer value to modify the image's alpha component with. - * Values above 0 will increase transparency, and values below 0 will decrease - * transparency. - * @return The given image with its alpha component adjusted by the given offset. - */ - public static BufferedImage alphaOffset(final Image rawImg, final int offset) - { - BufferedImage image = toARGB(rawImg); - final float offsetFloat = (float) offset; - final int numComponents = image.getColorModel().getNumComponents(); - final float[] scales = new float[numComponents]; - final float[] offsets = new float[numComponents]; - - Arrays.fill(scales, 1f); - Arrays.fill(offsets, 0f); - offsets[numComponents - 1] = offsetFloat; - return offset(image, scales, offsets); - } - - /** - * Offsets an image's alpha component by a given percentage. - * - * @param rawImg The image to be made more or less transparent. - * @param percentage The ratio to modify the image's alpha component with. - * Values above 1 will increase transparency, and values below 1 will decrease - * transparency. - * @return The given image with its alpha component scaled by the given percentage. - */ - public static BufferedImage alphaOffset(final Image rawImg, final float percentage) - { - BufferedImage image = toARGB(rawImg); - final int numComponents = image.getColorModel().getNumComponents(); - final float[] scales = new float[numComponents]; - final float[] offsets = new float[numComponents]; - - Arrays.fill(scales, 1f); - Arrays.fill(offsets, 0f); - scales[numComponents - 1] = percentage; - return offset(image, scales, offsets); - } - - /** - * Creates a grayscale image from the given image. - * - * @param image The source image to be converted. - * @return A copy of the given imnage, with colors converted to grayscale. - */ - public static BufferedImage grayscaleImage(final BufferedImage image) - { - final Image grayImage = GrayFilter.createDisabledImage(image); - return ImageUtil.bufferedImageFromImage(grayImage); - } - - /** - * Re-size a BufferedImage to the given dimensions. - * - * @param image the BufferedImage. - * @param newWidth The width to set the BufferedImage to. - * @param newHeight The height to set the BufferedImage to. - * @return The BufferedImage with the specified dimensions - */ - public static BufferedImage resizeImage(final BufferedImage image, final int newWidth, final int newHeight) - { - final Image resized = image.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH); - return ImageUtil.bufferedImageFromImage(resized); - } - - /** - * Re-size a BufferedImage's canvas to the given dimensions. - * - * @param image The image whose canvas should be re-sized. - * @param newWidth The width to set the BufferedImage to. - * @param newHeight The height to set the BufferedImage to. - * @return The BufferedImage centered within canvas of given dimensions. - */ - public static BufferedImage resizeCanvas(final BufferedImage image, final int newWidth, final int newHeight) - { - final BufferedImage dimg = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB); - final int centeredX = newWidth / 2 - image.getWidth() / 2; - final int centeredY = newHeight / 2 - image.getHeight() / 2; - - final Graphics2D g2d = dimg.createGraphics(); - g2d.drawImage(image, centeredX, centeredY, null); - g2d.dispose(); - return dimg; - } - - /** - * Rotates an image around its center by a given number of radians. - * - * @param image The image to be rotated. - * @param theta The number of radians to rotate the image. - * @return The given image, rotated by the given theta. - */ - public static BufferedImage rotateImage(final BufferedImage image, final double theta) - { - AffineTransform transform = new AffineTransform(); - transform.rotate(theta, image.getWidth() / 2.0, image.getHeight() / 2.0); - AffineTransformOp transformOp = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR); - return transformOp.filter(image, null); - } - - /** - * Flips an image horizontally and/or vertically. - * - * @param image The image to be flipped. - * @param horizontal Whether the image should be flipped horizontally. - * @param vertical Whether the image should be flipped vertically. - * @return The given image, flipped horizontally and/or vertically. - */ - public static BufferedImage flipImage(final BufferedImage image, final boolean horizontal, final boolean vertical) - { - int x = 0; - int y = 0; - int w = image.getWidth(); - int h = image.getHeight(); - - final BufferedImage out = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); - final Graphics2D g2d = out.createGraphics(); - - if (horizontal) - { - x = w; - w *= -1; - } - - if (vertical) - { - y = h; - h *= -1; - } - - g2d.drawImage(image, x, y, w, h, null); - g2d.dispose(); - - return out; - } - - /** - * Outlines non-transparent pixels of a BufferedImage with the given color. - * - * @param image The image to be outlined. - * @param color The color to use for the outline. - * @return The BufferedImage with its edges outlined with the given color. - */ - public static BufferedImage outlineImage(final BufferedImage image, final Color color) - { - return outlineImage(image, color, ColorUtil::isNotFullyTransparent, false); - } - - /** - * Outlines pixels of a BufferedImage with the given color, using a given predicate to colorize - * the given image for outlining. - * - * @param image The image to be outlined. - * @param color The color to use for the outline. - * @param fillCondition The predicate to be consumed by {@link #fillImage(BufferedImage, Color, Predicate) fillImage(BufferedImage, Color, Predicate)} - * @return The BufferedImage with its edges outlined with the given color. - */ - public static BufferedImage outlineImage(final BufferedImage image, final Color color, final Predicate fillCondition) - { - return outlineImage(image, color, fillCondition, false); - } - - /** - * Outlines non-transparent pixels of a BufferedImage with the given color. Optionally outlines - * corners in addition to edges. - * - * @param image The image to be outlined. - * @param color The color to use for the outline. - * @param outlineCorners Whether to draw an outline around corners, or only around edges. - * @return The BufferedImage with its edges--and optionally, corners--outlined - * with the given color. - */ - public static BufferedImage outlineImage(final BufferedImage image, final Color color, final Boolean outlineCorners) - { - return outlineImage(image, color, ColorUtil::isNotFullyTransparent, outlineCorners); - } - - /** - * Outlines pixels of a BufferedImage with the given color, using a given predicate to colorize - * the given image for outlining. Optionally outlines corners in addition to edges. - * - * @param image The image to be outlined. - * @param color The color to use for the outline. - * @param fillCondition The predicate to be consumed by {@link #fillImage(BufferedImage, Color, Predicate) fillImage(BufferedImage, Color, Predicate)} - * @param outlineCorners Whether to draw an outline around corners, or only around edges. - * @return The BufferedImage with its edges--and optionally, corners--outlined - * with the given color. - */ - public static BufferedImage outlineImage(final BufferedImage image, final Color color, final Predicate fillCondition, final Boolean outlineCorners) - { - final BufferedImage filledImage = fillImage(image, color, fillCondition); - final BufferedImage outlinedImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB); - - final Graphics2D g2d = outlinedImage.createGraphics(); - for (int x = -1; x <= 1; x++) - { - for (int y = -1; y <= 1; y++) - { - if ((x == 0 && y == 0) - || (!outlineCorners && Math.abs(x) + Math.abs(y) != 1)) - { - continue; - } - - g2d.drawImage(filledImage, x, y, null); - } - } - g2d.drawImage(image, 0, 0, null); - g2d.dispose(); - - return outlinedImage; - } - - /** - * Reads an image resource from a given path relative to a given class. - * This method is primarily shorthand for the synchronization and error handling required for - * loading image resources from classes. - * - * @param c The class to be referenced for resource path. - * @param path The path, relative to the given class. - * @return A {@link BufferedImage} of the loaded image resource from the given path. - */ - public static BufferedImage getResourceStreamFromClass(final Class c, final String path) - { - try - { - synchronized (ImageIO.class) - { - return ImageIO.read(c.getResourceAsStream(path)); - } - } - catch (IllegalArgumentException e) - { - throw new IllegalArgumentException(path, e); - } - catch (IOException e) - { - throw new RuntimeException(path, e); - } - } - - /** - * Fills all non-transparent pixels of the given image with the given color. - * - * @param image The image which should have its non-transparent pixels filled. - * @param color The color with which to fill pixels. - * @return The given image with all non-transparent pixels set to the given color. - */ - public static BufferedImage fillImage(final BufferedImage image, final Color color) - { - return fillImage(image, color, ColorUtil::isNotFullyTransparent); - } - - /** - * Fills pixels of the given image with the given color based on a given fill condition - * predicate. - * - * @param image The image which should have its non-transparent pixels filled. - * @param color The color with which to fill pixels. - * @param fillCondition The condition on which to fill pixels with the given color. - * @return The given image with all pixels fulfilling the fill condition predicate - * set to the given color. - */ - static BufferedImage fillImage(final BufferedImage image, final Color color, final Predicate fillCondition) - { - final BufferedImage filledImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB); - for (int x = 0; x < filledImage.getWidth(); x++) - { - for (int y = 0; y < filledImage.getHeight(); y++) - { - final Color pixelColor = new Color(image.getRGB(x, y), true); - if (!fillCondition.test(pixelColor)) - { - continue; - } - - filledImage.setRGB(x, y, color.getRGB()); - } - } - return filledImage; - } - - /** - * Recolors pixels of the given image with the given color based on a given recolor condition - * predicate. - * - * @param image The image which should have its non-transparent pixels recolored. - * @param color The color with which to recolor pixels. - * @param recolorCondition The condition on which to recolor pixels with the given color. - * @return The given image with all pixels fulfilling the recolor condition predicate - * set to the given color. - */ - public static BufferedImage recolorImage(final BufferedImage image, final Color color, final Predicate recolorCondition) - { - final BufferedImage recoloredImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB); - for (int x = 0; x < recoloredImage.getWidth(); x++) - { - for (int y = 0; y < recoloredImage.getHeight(); y++) - { - final Color pixelColor = new Color(image.getRGB(x, y), true); - if (!recolorCondition.test(pixelColor)) - { - recoloredImage.setRGB(x, y, image.getRGB(x, y)); - continue; - } - - recoloredImage.setRGB(x, y, color.getRGB()); - } - } - return recoloredImage; - } - - public static BufferedImage recolorImage(BufferedImage image, final Color color) - { - int width = image.getWidth(); - int height = image.getHeight(); - WritableRaster raster = image.getRaster(); - - for (int xx = 0; xx < width; xx++) - { - for (int yy = 0; yy < height; yy++) - { - int[] pixels = raster.getPixel(xx, yy, (int[]) null); - pixels[0] = color.getRed(); - pixels[1] = color.getGreen(); - pixels[2] = color.getBlue(); - raster.setPixel(xx, yy, pixels); - } - } - return image; - } - - /** - * Performs a rescale operation on the image's color components. - * - * @param image The image to be adjusted. - * @param scales An array of scale operations to be performed on the image's color components. - * @param offsets An array of offset operations to be performed on the image's color components. - * @return The modified image after applying the given adjustments. - */ - private static BufferedImage offset(final BufferedImage image, final float[] scales, final float[] offsets) - { - return new RescaleOp(scales, offsets, null).filter(image, null); - } - - - /** - * Converts the buffered image into a sprite image and returns it - * @param image The image to be converted - * @param client Current client instance - * @return The buffered image as a sprite image - */ - public static Sprite getImageSprite(BufferedImage image, Client client) - { - int[] pixels = new int[image.getWidth() * image.getHeight()]; - - try - { - PixelGrabber g = new PixelGrabber(image, 0, 0, image.getWidth(), image.getHeight(), pixels, 0, image.getWidth()); - g.setColorModel(new DirectColorModel(32, 0xff0000, 0xff00, 0xff, 0xff000000)); - g.grabPixels(); - - // Make any fully transparent pixels fully black, because the sprite draw routines - // check for == 0, not actual transparency - for (int i = 0; i < pixels.length; i++) - { - if ((pixels[i] & 0xFF000000) == 0) - { - pixels[i] = 0; - } - } - } - catch (InterruptedException ex) - { - log.debug("PixelGrabber was interrupted: ", ex); - } - - return client.createSprite(pixels, image.getWidth(), image.getHeight()); - } - - /** - * Converts an image into an {@code IndexedSprite} instance. - * - * The passed in image can only have at max 255 different colors. - * - * @param image The image to be converted - * @param client Current client instance - * @return The image as an {@code IndexedSprite} - */ - public static IndexedSprite getImageIndexedSprite(BufferedImage image, Client client) - { - final byte[] pixels = new byte[image.getWidth() * image.getHeight()]; - final List palette = new ArrayList<>(); - /* - When drawing the indexed sprite, palette idx 0 is seen as fully transparent, - so pad the palette out so that our colors start at idx 1. - */ - palette.add(0); - - final int[] sourcePixels = image.getRGB(0, 0, - image.getWidth(), image.getHeight(), - null, 0, image.getWidth()); - - /* - Build a color palette and assign the pixels to positions in the palette. - */ - for (int j = 0; j < sourcePixels.length; j++) - { - final int argb = sourcePixels[j]; - final int a = (argb >> 24) & 0xFF; - final int rgb = argb & 0xFF_FF_FF; - - // Default to not drawing the pixel. - int paletteIdx = 0; - - // If the pixel is fully opaque, draw it. - if (a == 0xFF) - { - paletteIdx = palette.indexOf(rgb); - - if (paletteIdx == -1) - { - paletteIdx = palette.size(); - palette.add(rgb); - } - } - - pixels[j] = (byte) paletteIdx; - } - - if (palette.size() > 256) - { - throw new RuntimeException("Passed in image had " + (palette.size() - 1) - + " different colors, exceeding the max of 255."); - } - - final IndexedSprite sprite = client.createIndexedSprite(); - - sprite.setPixels(pixels); - sprite.setPalette(Ints.toArray(palette)); - sprite.setWidth(image.getWidth()); - sprite.setHeight(image.getHeight()); - sprite.setOriginalWidth(image.getWidth()); - sprite.setOriginalHeight(image.getHeight()); - sprite.setOffsetX(0); - sprite.setOffsetY(0); - - return sprite; - } - - /** - * Resize Sprite sprite to given width (newW) and height (newH) - */ - public static Sprite resizeSprite(final Client client, final Sprite sprite, int newW, int newH) - { - assert newW > 0 && newH > 0; - - final int oldW = sprite.getWidth(); - final int oldH = sprite.getHeight(); - - if (oldW == newW && oldH == newH) - { - return sprite; - } - - final int[] canvas = new int[newW * newH]; - final int[] pixels = sprite.getPixels(); - - final Sprite result = client.createSprite(canvas, newW, newH); - - int pixelX = 0; - int pixelY = 0; - - final int oldMaxW = sprite.getMaxWidth(); - final int oldMaxH = sprite.getMaxHeight(); - - final int pixelW = (oldMaxW << 16) / newW; - final int pixelH = (oldMaxH << 16) / newH; - - int xOffset = 0; - int yOffset = 0; - - int canvasIdx; - if (sprite.getOffsetX() > 0) - { - canvasIdx = (pixelW + (sprite.getOffsetX() << 16) - 1) / pixelW; - xOffset += canvasIdx; - pixelX += canvasIdx * pixelW - (sprite.getOffsetX() << 16); - } - - if (sprite.getOffsetY() > 0) - { - canvasIdx = (pixelH + (sprite.getOffsetY() << 16) - 1) / pixelH; - yOffset += canvasIdx; - pixelY += canvasIdx * pixelH - (sprite.getOffsetY() << 16); - } - - if (oldW < oldMaxW) - { - newW = (pixelW + ((oldW << 16) - pixelX) - 1) / pixelW; - } - - if (oldH < oldMaxH) - { - newH = (pixelH + ((oldH << 16) - pixelY) - 1) / pixelH; - } - - canvasIdx = xOffset + yOffset * newW; - int canvasOffset = 0; - if (yOffset + newH > newH) - { - newH -= yOffset + newH - newH; - } - - int tmp; - if (yOffset < 0) - { - tmp = -yOffset; - newH -= tmp; - canvasIdx += tmp * newW; - pixelY += pixelH * tmp; - } - - if (newW + xOffset > newW) - { - tmp = newW + xOffset - newW; - newW -= tmp; - canvasOffset += tmp; - } - - if (xOffset < 0) - { - tmp = -xOffset; - newW -= tmp; - canvasIdx += tmp; - pixelX += pixelW * tmp; - canvasOffset += tmp; - } - - client.scaleSprite(canvas, pixels, 0, pixelX, pixelY, canvasIdx, canvasOffset, newW, newH, pixelW, pixelH, oldW); - - return result; - } - - /** - * Draw fg centered on top of bg - */ - public static Sprite mergeSprites(final Client client, final Sprite bg, final Sprite fg) - { - assert fg.getHeight() <= bg.getHeight() && fg.getWidth() <= bg.getWidth() : "Background has to be larger than foreground"; - - final int[] canvas = Arrays.copyOf(bg.getPixels(), bg.getWidth() * bg.getHeight()); - final Sprite result = client.createSprite(canvas, bg.getWidth(), bg.getHeight()); - - final int bgWid = bg.getWidth(); - final int fgHgt = fg.getHeight(); - final int fgWid = fg.getWidth(); - - final int xOffset = (bgWid - fgWid) / 2; - final int yOffset = (bg.getHeight() - fgHgt) / 2; - - final int[] fgPixels = fg.getPixels(); - - for (int y1 = yOffset, y2 = 0; y2 < fgHgt; y1++, y2++) - { - int i1 = y1 * bgWid + xOffset; - int i2 = y2 * fgWid; - - for (int x = 0; x < fgWid; x++, i1++, i2++) - { - if (fgPixels[i2] > 0) - { - canvas[i1] = fgPixels[i2]; - } - } - } - - return result; - } -} \ No newline at end of file +/* + * Copyright (c) 2018, Jordan Atwood + * 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.util; + +import com.google.common.primitives.Ints; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.geom.AffineTransform; +import java.awt.image.AffineTransformOp; +import java.awt.image.BufferedImage; +import java.awt.image.DirectColorModel; +import java.awt.image.PixelGrabber; +import java.awt.image.RescaleOp; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import javax.imageio.ImageIO; +import javax.swing.GrayFilter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.IndexedSprite; +import net.runelite.api.SpritePixels; + +/** + * Various Image/BufferedImage utilities. + */ +@Slf4j +public class ImageUtil +{ + static + { + ImageIO.setUseCache(false); + } + + /** + * Creates a {@link BufferedImage} from an {@link Image}. + * + * @param image An Image to be converted to a BufferedImage. + * @return A BufferedImage instance of the same given image. + */ + public static BufferedImage bufferedImageFromImage(final Image image) + { + if (image instanceof BufferedImage) + { + return (BufferedImage) image; + } + + return toARGB(image); + } + + /** + * Creates an ARGB {@link BufferedImage} from an {@link Image}. + */ + public static BufferedImage toARGB(final Image image) + { + if (image instanceof BufferedImage && ((BufferedImage) image).getType() == BufferedImage.TYPE_INT_ARGB) + { + return (BufferedImage) image; + } + + BufferedImage out = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB); + Graphics2D g2d = out.createGraphics(); + g2d.drawImage(image, 0, 0, null); + g2d.dispose(); + return out; + } + + /** + * Offsets an image's luminance by a given value. + * + * @param rawImg The image to be darkened or brightened. + * @param offset A signed 8-bit integer value to brighten or darken the image with. + * Values above 0 will brighten, and values below 0 will darken. + * @return The given image with its brightness adjusted by the given offset. + */ + public static BufferedImage luminanceOffset(final Image rawImg, final int offset) + { + BufferedImage image = toARGB(rawImg); + final float offsetFloat = (float) offset; + final int numComponents = image.getColorModel().getNumComponents(); + final float[] scales = new float[numComponents]; + final float[] offsets = new float[numComponents]; + + Arrays.fill(scales, 1f); + for (int i = 0; i < numComponents; i++) + { + offsets[i] = offsetFloat; + } + // Set alpha to not offset + offsets[numComponents - 1] = 0f; + + return offset(image, scales, offsets); + } + + /** + * Changes an images luminance by a scaling factor + * + * @param rawImg The image to be darkened or brightened. + * @param percentage The ratio to darken or brighten the given image. + * Values above 1 will brighten, and values below 1 will darken. + * @return The given image with its brightness scaled by the given percentage. + */ + public static BufferedImage luminanceScale(final Image rawImg, final float percentage) + { + BufferedImage image = toARGB(rawImg); + final int numComponents = image.getColorModel().getNumComponents(); + final float[] scales = new float[numComponents]; + final float[] offsets = new float[numComponents]; + + Arrays.fill(offsets, 0f); + for (int i = 0; i < numComponents; i++) + { + scales[i] = percentage; + } + // Set alpha to not scale + scales[numComponents - 1] = 1f; + + return offset(image, scales, offsets); + } + + /** + * Offsets an image's alpha component by a given offset. + * + * @param rawImg The image to be made more or less transparent. + * @param offset A signed 8-bit integer value to modify the image's alpha component with. + * Values above 0 will increase transparency, and values below 0 will decrease + * transparency. + * @return The given image with its alpha component adjusted by the given offset. + */ + public static BufferedImage alphaOffset(final Image rawImg, final int offset) + { + BufferedImage image = toARGB(rawImg); + final float offsetFloat = (float) offset; + final int numComponents = image.getColorModel().getNumComponents(); + final float[] scales = new float[numComponents]; + final float[] offsets = new float[numComponents]; + + Arrays.fill(scales, 1f); + Arrays.fill(offsets, 0f); + offsets[numComponents - 1] = offsetFloat; + return offset(image, scales, offsets); + } + + /** + * Offsets an image's alpha component by a given percentage. + * + * @param rawImg The image to be made more or less transparent. + * @param percentage The ratio to modify the image's alpha component with. + * Values above 1 will increase transparency, and values below 1 will decrease + * transparency. + * @return The given image with its alpha component scaled by the given percentage. + */ + public static BufferedImage alphaOffset(final Image rawImg, final float percentage) + { + BufferedImage image = toARGB(rawImg); + final int numComponents = image.getColorModel().getNumComponents(); + final float[] scales = new float[numComponents]; + final float[] offsets = new float[numComponents]; + + Arrays.fill(scales, 1f); + Arrays.fill(offsets, 0f); + scales[numComponents - 1] = percentage; + return offset(image, scales, offsets); + } + + /** + * Creates a grayscale image from the given image. + * + * @param image The source image to be converted. + * @return A copy of the given imnage, with colors converted to grayscale. + */ + public static BufferedImage grayscaleImage(final BufferedImage image) + { + final Image grayImage = GrayFilter.createDisabledImage(image); + return ImageUtil.bufferedImageFromImage(grayImage); + } + + /** + * Re-size a BufferedImage to the given dimensions. + * + * @param image the BufferedImage. + * @param newWidth The width to set the BufferedImage to. + * @param newHeight The height to set the BufferedImage to. + * @return The BufferedImage with the specified dimensions + */ + public static BufferedImage resizeImage(final BufferedImage image, final int newWidth, final int newHeight) + { + final Image resized = image.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH); + return ImageUtil.bufferedImageFromImage(resized); + } + + /** + * Re-size a BufferedImage's canvas to the given dimensions. + * + * @param image The image whose canvas should be re-sized. + * @param newWidth The width to set the BufferedImage to. + * @param newHeight The height to set the BufferedImage to. + * @return The BufferedImage centered within canvas of given dimensions. + */ + public static BufferedImage resizeCanvas(final BufferedImage image, final int newWidth, final int newHeight) + { + final BufferedImage dimg = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB); + final int centeredX = newWidth / 2 - image.getWidth() / 2; + final int centeredY = newHeight / 2 - image.getHeight() / 2; + + final Graphics2D g2d = dimg.createGraphics(); + g2d.drawImage(image, centeredX, centeredY, null); + g2d.dispose(); + return dimg; + } + + /** + * Rotates an image around its center by a given number of radians. + * + * @param image The image to be rotated. + * @param theta The number of radians to rotate the image. + * @return The given image, rotated by the given theta. + */ + public static BufferedImage rotateImage(final BufferedImage image, final double theta) + { + AffineTransform transform = new AffineTransform(); + transform.rotate(theta, image.getWidth() / 2.0, image.getHeight() / 2.0); + AffineTransformOp transformOp = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR); + return transformOp.filter(image, null); + } + + /** + * Flips an image horizontally and/or vertically. + * + * @param image The image to be flipped. + * @param horizontal Whether the image should be flipped horizontally. + * @param vertical Whether the image should be flipped vertically. + * @return The given image, flipped horizontally and/or vertically. + */ + public static BufferedImage flipImage(final BufferedImage image, final boolean horizontal, final boolean vertical) + { + int x = 0; + int y = 0; + int w = image.getWidth(); + int h = image.getHeight(); + + final BufferedImage out = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); + final Graphics2D g2d = out.createGraphics(); + + if (horizontal) + { + x = w; + w *= -1; + } + + if (vertical) + { + y = h; + h *= -1; + } + + g2d.drawImage(image, x, y, w, h, null); + g2d.dispose(); + + return out; + } + + /** + * Outlines non-transparent pixels of a BufferedImage with the given color. + * + * @param image The image to be outlined. + * @param color The color to use for the outline. + * @return The BufferedImage with its edges outlined with the given color. + */ + public static BufferedImage outlineImage(final BufferedImage image, final Color color) + { + return outlineImage(image, color, false); + } + + /** + * Outlines non-transparent pixels of a BufferedImage with the given color. Optionally outlines + * corners in addition to edges. + * + * @param image The image to be outlined. + * @param color The color to use for the outline. + * @param outlineCorners Whether to draw an outline around corners, or only around edges. + * @return The BufferedImage with its edges--and optionally, corners--outlined + * with the given color. + */ + public static BufferedImage outlineImage(final BufferedImage image, final Color color, final Boolean outlineCorners) + { + final BufferedImage filledImage = fillImage(image, color); + final BufferedImage outlinedImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB); + + final Graphics2D g2d = outlinedImage.createGraphics(); + for (int x = -1; x <= 1; x++) + { + for (int y = -1; y <= 1; y++) + { + if ((x == 0 && y == 0) + || (!outlineCorners && Math.abs(x) + Math.abs(y) != 1)) + { + continue; + } + + g2d.drawImage(filledImage, x, y, null); + } + } + g2d.drawImage(image, 0, 0, null); + g2d.dispose(); + + return outlinedImage; + } + + /** + * Reads an image resource from a given path relative to a given class. + * This method is primarily shorthand for the synchronization and error handling required for + * loading image resources from classes. + * + * @param c The class to be referenced for resource path. + * @param path The path, relative to the given class. + * @return A {@link BufferedImage} of the loaded image resource from the given path. + */ + public static BufferedImage getResourceStreamFromClass(final Class c, final String path) + { + try + { + synchronized (ImageIO.class) + { + return ImageIO.read(c.getResourceAsStream(path)); + } + } + catch (IllegalArgumentException e) + { + final String filePath; + + if (path.startsWith("/")) + { + filePath = path; + } + else + { + filePath = c.getPackage().getName().replace(".", "/") + "/" + path; + } + + log.warn("Failed to load image from class: {}, path: {}", c.getName(), filePath); + + throw new IllegalArgumentException(path, e); + } + catch (IOException e) + { + throw new RuntimeException(path, e); + } + } + + /** + * Fills all non-transparent pixels of the given image with the given color. + * + * @param image The image which should have its non-transparent pixels filled. + * @param color The color with which to fill pixels. + * @return The given image with all non-transparent pixels set to the given color. + */ + public static BufferedImage fillImage(final BufferedImage image, final Color color) + { + final BufferedImage filledImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB); + for (int x = 0; x < filledImage.getWidth(); x++) + { + for (int y = 0; y < filledImage.getHeight(); y++) + { + final Color pixelColor = new Color(image.getRGB(x, y), true); + if (pixelColor.getAlpha() == 0) + { + continue; + } + + filledImage.setRGB(x, y, color.getRGB()); + } + } + return filledImage; + } + + /** + * Performs a rescale operation on the image's color components. + * + * @param image The image to be adjusted. + * @param scales An array of scale operations to be performed on the image's color components. + * @param offsets An array of offset operations to be performed on the image's color components. + * @return The modified image after applying the given adjustments. + */ + private static BufferedImage offset(final BufferedImage image, final float[] scales, final float[] offsets) + { + return new RescaleOp(scales, offsets, null).filter(image, null); + } + + + /** + * Converts the buffered image into a sprite image and returns it + * @param image The image to be converted + * @param client Current client instance + * @return The buffered image as a sprite image + */ + public static SpritePixels getImageSpritePixels(BufferedImage image, Client client) + { + int[] pixels = new int[image.getWidth() * image.getHeight()]; + + try + { + PixelGrabber g = new PixelGrabber(image, 0, 0, image.getWidth(), image.getHeight(), pixels, 0, image.getWidth()); + g.setColorModel(new DirectColorModel(32, 0xff0000, 0xff00, 0xff, 0xff000000)); + g.grabPixels(); + + // Make any fully transparent pixels fully black, because the sprite draw routines + // check for == 0, not actual transparency + for (int i = 0; i < pixels.length; i++) + { + if ((pixels[i] & 0xFF000000) == 0) + { + pixels[i] = 0; + } + } + } + catch (InterruptedException ex) + { + log.debug("PixelGrabber was interrupted: ", ex); + } + + return client.createSpritePixels(pixels, image.getWidth(), image.getHeight()); + } + + /** + * Converts an image into an {@code IndexedSprite} instance. + * + * The passed in image can only have at max 255 different colors. + * + * @param image The image to be converted + * @param client Current client instance + * @return The image as an {@code IndexedSprite} + */ + public static IndexedSprite getImageIndexedSprite(BufferedImage image, Client client) + { + final byte[] pixels = new byte[image.getWidth() * image.getHeight()]; + final List palette = new ArrayList<>(); + /* + When drawing the indexed sprite, palette idx 0 is seen as fully transparent, + so pad the palette out so that our colors start at idx 1. + */ + palette.add(0); + + final int[] sourcePixels = image.getRGB(0, 0, + image.getWidth(), image.getHeight(), + null, 0, image.getWidth()); + + /* + Build a color palette and assign the pixels to positions in the palette. + */ + for (int j = 0; j < sourcePixels.length; j++) + { + final int argb = sourcePixels[j]; + final int a = (argb >> 24) & 0xFF; + final int rgb = argb & 0xFF_FF_FF; + + // Default to not drawing the pixel. + int paletteIdx = 0; + + // If the pixel is fully opaque, draw it. + if (a == 0xFF) + { + paletteIdx = palette.indexOf(rgb); + + if (paletteIdx == -1) + { + paletteIdx = palette.size(); + palette.add(rgb); + } + } + + pixels[j] = (byte) paletteIdx; + } + + if (palette.size() > 256) + { + throw new RuntimeException("Passed in image had " + (palette.size() - 1) + + " different colors, exceeding the max of 255."); + } + + final IndexedSprite sprite = client.createIndexedSprite(); + + sprite.setPixels(pixels); + sprite.setPalette(Ints.toArray(palette)); + sprite.setWidth(image.getWidth()); + sprite.setHeight(image.getHeight()); + sprite.setOriginalWidth(image.getWidth()); + sprite.setOriginalHeight(image.getHeight()); + sprite.setOffsetX(0); + sprite.setOffsetY(0); + + return sprite; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/util/ItemFinder.java b/runelite-client/src/main/java/net/runelite/client/util/ItemFinder.java deleted file mode 100644 index 4f62062c97..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/util/ItemFinder.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2018, Shaun Dreclin - * 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.util; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import javax.inject.Inject; -import net.runelite.api.Client; -import net.runelite.api.InventoryID; -import net.runelite.api.Item; -import net.runelite.api.ItemContainer; - -public class ItemFinder -{ - @Inject - private Client client; - - public List find(Container container, Match match, Integer... itemIDs) - { - ItemContainer inventory = client.getItemContainer(InventoryID.INVENTORY); - ItemContainer equipment = client.getItemContainer(InventoryID.EQUIPMENT); - List inputIDs = new ArrayList<>(Arrays.asList(itemIDs)); - List foundIDs = new ArrayList<>(); - List foundItems = new ArrayList<>(); - List items = new ArrayList<>(); - - if (inventory != null && equipment != null) - { - if (container == Container.INVENTORY || container == Container.INVENTORY_AND_EQUIPMENT) - { - items.addAll(Arrays.asList(inventory.getItems())); - } - - if (container == Container.EQUIPMENT || container == Container.INVENTORY_AND_EQUIPMENT) - { - items.addAll(Arrays.asList(equipment.getItems())); - } - - for (Item item : items) - { - if (inputIDs.contains(item.getId())) - { - foundIDs.add(item.getId()); - foundItems.add(item); - } - } - - //todo: make this work with multiples of the same item in inputIDs - if (match == Match.ALL_ITEMS && !foundIDs.containsAll(inputIDs)) - { - foundItems = Collections.emptyList(); - } - } - - return foundItems; - } - - public enum Container - { - INVENTORY, - EQUIPMENT, - INVENTORY_AND_EQUIPMENT - } - - public enum Match - { - ALL_ITEMS, - ANY_ITEM - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/util/ItemUtil.java b/runelite-client/src/main/java/net/runelite/client/util/ItemUtil.java deleted file mode 100644 index d279b6f087..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/util/ItemUtil.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (c) 2018, TheStonedTurtle - * 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.util; - -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import javax.annotation.Nullable; -import net.runelite.api.Item; -import net.runelite.http.api.loottracker.GameItem; - -/** - * Utility class for Item arrays. - */ -public class ItemUtil -{ - /** - * Converts itemArray into a GameItem collection limited to the passed ids - * Consolidates all matching Item's qty, by id - * - * @param itemArray item array to work on - * @param ids item ids to include in result - * @return Map of GameItem by item id - */ - public static Map toGameItemMap(Item[] itemArray, @Nullable Set ids) - { - final Map map = new HashMap<>(); - for (Item i : itemArray) - { - final int id = i.getId(); - if (ids == null || ids.contains(id)) - { - int qty = i.getQuantity(); - if (map.containsKey(id)) - { - qty += map.get(id).getQty(); - } - map.put(id, new GameItem(id, qty)); - } - } - - return map; - } - - /** - * Converts itemArray into a GameItem collection - * Consolidates all matching Item's qty, by id - * - * @param itemArray item array to work on - * @return Map of GameItem by item id - */ - public static Map toGameItemMap(Item[] itemArray) - { - return toGameItemMap(itemArray, null); - } - - /** - * Checks that the itemArray contains the all of the requested ids - * - * @param itemArray item array to work on - * @param ids item ids to check for - * @return true if itemArray contains all requested item ids - */ - public static boolean containsAllItemIds(Item[] itemArray, Set ids) - { - final Set found = new HashSet<>(); - for (Item i : itemArray) - { - final int id = i.getId(); - if (ids.contains(id)) - { - found.add(id); - // Early break if possible - if (found.size() == ids.size()) - { - return true; - } - } - - } - - return found.size() == ids.size(); - } - - /** - * Checks if itemArray contains any item id that also exists in ids - * - * @param itemArray item array to work on - * @param ids item ids to check for - * @return true if any item in itemArray contains any id from ids - */ - public static boolean containsAnyItemId(Item[] itemArray, Set ids) - { - for (Item i : itemArray) - { - final int id = i.getId(); - if (ids.contains(id)) - { - return true; - } - } - - return false; - } - - /** - * Checks if any item id in itemArray matches itemID - * - * @param itemArray item array to work on - * @param itemID item id to check for - * @return true if any item in itemArray matches itemID - */ - public static boolean containsItemId(Item[] itemArray, int itemID) - { - for (Item i : itemArray) - { - if (i.getId() == itemID) - { - return true; - } - } - - return false; - } - - /** - * Returns null if index is bigger than array otherwise array content. - * - * @param itemArray item array to check - * @param index index to grab - * @return null if index is bigger than array otherwise array content - */ - public static Item safeGetItemAtIndex(Item[] itemArray, int index) - { - if (itemArray.length <= index) - { - return null; - } - - return itemArray[index]; - } - - /** - * Returns -1 if index is bigger than array otherwise array contents `id` value - * - * @param itemArray item array to check - * @param index index to grab - * @return -1 if index is bigger than array otherwise array contents `id` value - */ - public static Integer safeGetItemIdAtIndex(Item[] itemArray, int index) - { - if (itemArray.length <= index) - { - return -1; - } - - return itemArray[index].getId(); - } - - /** - * Checks that the itemArray contains all requestedItems. Must contain >= the requested qty for each id - * Uses GameItem to represent the requestedItems since we can't easily create Item instances - * - * @param itemArray item array to check against - * @param requestedItems Collection of GameItems which contain the item id & minimum qty - * @return true if contains all requestedItems - */ - public static boolean containsAllGameItems(Item[] itemArray, Collection requestedItems) - { - final Map map = toGameItemMap(itemArray, null); - for (GameItem i : requestedItems) - { - final int id = i.getId(); - GameItem item = map.get(id); - if (item == null) - { - return false; - } - - if (item.getQty() < i.getQty()) - { - return false; - } - } - - return true; - } - - /** - * Checks that the itemArray contains all requestedItems. Must contain >= the requested qty for each id - * Uses GameItem to represent the requestedItems since we can't easily create Item instances - * - * @param itemArray item array to check against - * @param requestedItems GameItem(s) to request - * @return true if contains all requestedItems - */ - public static boolean containsAllGameItems(Item[] itemArray, GameItem... requestedItems) - { - return containsAllGameItems(itemArray, Arrays.asList(requestedItems)); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableElement.java b/runelite-client/src/main/java/net/runelite/client/util/JagexPrintableCharMatcher.java similarity index 79% rename from runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableElement.java rename to runelite-client/src/main/java/net/runelite/client/util/JagexPrintableCharMatcher.java index 8229f9533c..41bc771e9d 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableElement.java +++ b/runelite-client/src/main/java/net/runelite/client/util/JagexPrintableCharMatcher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, TheStonedTurtle + * Copyright (c) 2019, Adam * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,17 +22,18 @@ * (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.overlay.components.table; +package net.runelite.client.util; -import java.awt.Color; -import lombok.Builder; -import lombok.Data; +import com.google.common.base.CharMatcher; -@Data -@Builder -public class TableElement +class JagexPrintableCharMatcher extends CharMatcher { - TableAlignment alignment; - Color color; - String content; + @Override + public boolean matches(char c) + { + // Characters which are printable + return (c >= 32 && c <= 126) + || c == 128 + || (c >= 160 && c <= 255); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/util/LinkBrowser.java b/runelite-client/src/main/java/net/runelite/client/util/LinkBrowser.java index fc40a970ea..e8965029c1 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/LinkBrowser.java +++ b/runelite-client/src/main/java/net/runelite/client/util/LinkBrowser.java @@ -36,7 +36,6 @@ import javax.inject.Singleton; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import lombok.extern.slf4j.Slf4j; -import net.runelite.client.ui.ClientUI; /** * Utility class used for web and file browser navigation @@ -72,7 +71,7 @@ public class LinkBrowser log.debug("Opened url through xdg-open to {}", url); return; } - + log.warn("LinkBrowser.browse() could not open {}", url); showMessageBox("Unable to open link. Press 'OK' and the link will be copied to your clipboard.", url); }).start(); @@ -80,7 +79,6 @@ public class LinkBrowser /** * Tries to open a directory in the OS native file manager. - * * @param directory directory to open */ public static void open(final String directory) @@ -104,7 +102,7 @@ public class LinkBrowser log.debug("Opened directory through xdg-open to {}", directory); return; } - + log.warn("LinkBrowser.open() could not open {}", directory); showMessageBox("Unable to open folder. Press 'OK' and the folder directory will be copied to your clipboard.", directory); }).start(); @@ -191,66 +189,15 @@ public class LinkBrowser } } - /** - * Tries to open the specified {@code File} with the systems default text editor. If operation fails - * an error message is displayed with the option to copy the absolute file path to clipboard. - * - * @param file the File instance of the log file - * @return did the file open successfully? - */ - public static boolean openLocalFile(final File file) - { - if (file == null || !file.exists()) - { - return false; - } - - if (attemptOpenLocalFile(file)) - { - log.debug("Opened log file through Desktop#edit to {}", file); - return true; - } - - showMessageBox("Unable to open log file. Press 'OK' and the file path will be copied to your clipboard", file.getAbsolutePath()); - return false; - } - - private static boolean attemptOpenLocalFile(final File file) - { - if (!Desktop.isDesktopSupported()) - { - return false; - } - - final Desktop desktop = Desktop.getDesktop(); - - if (!desktop.isSupported(Desktop.Action.OPEN)) - { - return false; - } - - try - { - desktop.open(file); - return true; - } - catch (IOException ex) - { - log.warn("Failed to open Desktop#edit {}", file, ex); - return false; - } - } - /** * Open swing message box with specified message and copy data to clipboard - * * @param message message to show */ private static void showMessageBox(final String message, final String data) { SwingUtilities.invokeLater(() -> { - final int result = JOptionPane.showConfirmDialog(ClientUI.getFrame(), message, "Message", + final int result = JOptionPane.showConfirmDialog(null, message, "Message", JOptionPane.OK_CANCEL_OPTION); if (result == JOptionPane.OK_OPTION) @@ -260,4 +207,4 @@ public class LinkBrowser } }); } -} \ No newline at end of file +} diff --git a/runelite-client/src/main/java/net/runelite/client/util/MenuUtil.java b/runelite-client/src/main/java/net/runelite/client/util/MenuUtil.java deleted file mode 100644 index e7b6984a5b..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/util/MenuUtil.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (c) 2019, Lucas - * 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.util; - -import com.google.common.collect.Lists; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import java.util.stream.Stream; -import lombok.extern.slf4j.Slf4j; -import net.runelite.api.Client; -import net.runelite.api.MenuEntry; -import net.runelite.api.util.Text; - -@Deprecated -@Slf4j -public class MenuUtil -{ - private static void swap(Client client, MenuEntry entry1, MenuEntry entry2) - { - List entries = Arrays.asList(client.getMenuEntries()); - - if (!entries.contains(entry1) || !entries.contains(entry2)) - { - log.warn("Can't swap {} with {} as one or both menuentries aren't present", entry1, entry2); - return; - } - - int idxA = entries.indexOf(entry1); - int idxB = entries.indexOf(entry2); - - entries.set(idxA, entry2); - entries.set(idxB, entry1); - - client.setMenuEntries(entries.toArray(new MenuEntry[0])); - } - - public static void swap(Client client, String option1, String option2, String target1, String target2, boolean strict) - { - if (strict) - { - swap(client, option1, option2, target1, target2); - } - else - { - swapNotStrict(client, option1, option2, target1, target2); - } - } - - private static void swap(Client client, String option1, String option2, String target1, String target2) - { - MenuEntry entry1 = findOptionTarget(getMenuStream(client), Text.standardize(option1), Text.standardize(target1)); - MenuEntry entry2 = findOptionTarget(getMenuStream(client), Text.standardize(option2), Text.standardize(target2)); - - if (entry1 != null && entry2 != null) - { - swap(client, entry1, entry2); - } - } - - private static void swapNotStrict(Client client, String option1, String option2, String target1, String target2) - { - MenuEntry entry1 = findOptionTargetLenient(getMenuStream(client), Text.standardize(option1), Text.standardize(target1)); - MenuEntry entry2 = findOptionTargetLenient(getMenuStream(client), Text.standardize(option2), Text.standardize(target2)); - - if (entry1 != null && entry2 != null) - { - swap(client, entry1, entry2); - } - } - - public static void swap(Client client, String option1, String option2, String targetforboth) - { - swap(client, option1, option2, targetforboth, targetforboth); - } - - public static void swap(Client client, String option1, String option2, String targetforboth, boolean strict) - { - if (strict) - { - swap(client, option1, option2, targetforboth, targetforboth); - } - else - { - swapNotStrict(client, option1, option2, targetforboth, targetforboth); - } - } - - private static MenuEntry findOptionTarget(Stream stream, String option, String target) - { - Optional maybeEntry = stream.filter(e -> Text.standardize(e.getOption()).equals(option) && - Text.standardize(e.getTarget()).equals(target)) - /* autism */.findFirst(); - - return maybeEntry.orElse(null); - } - - private static MenuEntry findOptionTargetLenient(Stream stream, String option, String target) - { - Optional maybeEntry = stream.filter(e -> Text.standardize(e.getOption()).contains(option) && - Text.standardize(e.getTarget()).contains(target)) - /* autism */.findFirst(); - - return maybeEntry.orElse(null); - } - - private static Stream getMenuStream(Client client) - { - List entries = Arrays.asList(client.getMenuEntries()); - return Lists.reverse(entries).stream(); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/util/MiscUtils.java b/runelite-client/src/main/java/net/runelite/client/util/MiscUtils.java deleted file mode 100644 index 181e2644dc..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/util/MiscUtils.java +++ /dev/null @@ -1,202 +0,0 @@ -package net.runelite.client.util; - -import net.runelite.api.Client; -import net.runelite.api.Player; -import net.runelite.api.WorldType; -import net.runelite.api.coords.WorldPoint; -import java.awt.Polygon; -import java.net.URL; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.time.Duration; -import java.time.temporal.ChronoUnit; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class MiscUtils -{ - private static final int[] abovePointsX = {2944, 3392, 3392, 2944}; - private static final int[] abovePointsY = {3523, 3523, 3971, 3971}; - private static final int[] belowPointsX = {2944, 2944, 3264, 3264}; - private static final int[] belowPointsY = {9918, 10360, 10360, 9918}; - - private static final Polygon abovePoly = new Polygon(abovePointsX, abovePointsY, abovePointsX.length); - private static final Polygon belowPoly = new Polygon(belowPointsX, belowPointsY, belowPointsX.length); - - private static final ChronoUnit[] ORDERED_CHRONOS = new ChronoUnit[] - { - ChronoUnit.YEARS, - ChronoUnit.MONTHS, - ChronoUnit.WEEKS, - ChronoUnit.DAYS, - ChronoUnit.HOURS, - ChronoUnit.MINUTES, - ChronoUnit.SECONDS - }; - - //test replacement so private for now - private static boolean inWildy(WorldPoint point) - { - if (point == null) - { - return false; - } - - return abovePoly.contains(point.getX(), point.getY()) || belowPoly.contains(point.getX(), point.getY()); - } - - public static int getWildernessLevelFrom(Client client, WorldPoint point) - { - if (client == null) - { - return 0; - } - - if (point == null) - { - return 0; - } - - int x = point.getX(); - - if (point.getPlane() == 0 && (x < 2940 || x > 3391)) - { - return 0; - } - - int y = point.getY(); - //v underground //v above ground - int wildernessLevel = clamp(y > 6400 ? ((y - 9920) / 8) + 1 : ((y - 3520) / 8) + 1, 0, 56); - - if (point.getPlane() > 0 && y < 9920) - { - wildernessLevel = 0; - } - - if (client.getWorldType().stream().anyMatch(worldType -> worldType == WorldType.PVP || worldType == WorldType.HIGH_RISK)) - { - wildernessLevel += 15; - } - - return Math.max(0, wildernessLevel); - } - - public static int clamp(int val, int min, int max) - { - return Math.max(min, Math.min(max, val)); - } - - public static float clamp(float val, float min, float max) - { - return Math.max(min, Math.min(max, val)); - } - - public static boolean inWilderness(Client client) - { - Player localPlayer = client.getLocalPlayer(); - - if (localPlayer == null) - { - return false; - } - - return inWildy(localPlayer.getWorldLocation()); - - //return getWildernessLevelFrom(client, localPlayer.getWorldLocation()) > 0; - } - - public static String formatTimeAgo(Duration dur) - { - long dA = 0, dB = 0, rm; - ChronoUnit cA = null, cB = null; - for (int i = 0; i < ORDERED_CHRONOS.length; i++) - { - cA = ORDERED_CHRONOS[i]; - dA = dur.getSeconds() / cA.getDuration().getSeconds(); - rm = dur.getSeconds() % cA.getDuration().getSeconds(); - if (dA <= 0) - { - cA = null; - continue; - } - - if (i + 1 < ORDERED_CHRONOS.length) - { - cB = ORDERED_CHRONOS[i + 1]; - dB = rm / cB.getDuration().getSeconds(); - - if (dB <= 0) - { - cB = null; - } - } - - break; - } - - if (cA == null) - { - return "just now."; - } - - String str = formatUnit(cA, dA); - - if (cB != null) - { - str += " and " + formatUnit(cB, dB); - } - - return str + " ago."; - } - - private static String formatUnit(ChronoUnit chrono, long val) - { - boolean multiple = val != 1; - String str; - if (multiple) - { - str = val + " "; - } - else - { - str = "a" + (chrono == ChronoUnit.HOURS ? "n " : " "); - } - str += chrono.name().toLowerCase(); - if (!multiple) - { - if (str.charAt(str.length() - 1) == 's') - { - str = str.substring(0, str.length() - 1); - } - } - else if (str.charAt(str.length() - 1) != 's') - { - str += "s"; - } - return str; - } - - /** - * Mostly stolen from {@link java.net.URLStreamHandler#toExternalForm(URL)} - * - * @param url URL to encode - * @return URL, with path, query and ref encoded - */ - public static String urlToStringEncoded(URL url) - { - String s; - String path = url.getPath() != null ? Stream.of(url.getPath().split("/")) - .map(s2 -> URLEncoder.encode(s2, StandardCharsets.UTF_8)).collect(Collectors.joining("/")) : ""; - return url.getProtocol() - + ':' - + (((s = url.getAuthority()) != null && s.length() > 0) ? "//" + s : "") - + (path) - + (((s = url.getQuery()) != null) ? '?' + urlEncode(s) : "") - + (((s = url.getRef()) != null) ? '#' + urlEncode(s) : ""); - } - - private static String urlEncode(String s) - { - return URLEncoder.encode(s, StandardCharsets.UTF_8); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/util/NonScheduledExecutorServiceExceptionLogger.java b/runelite-client/src/main/java/net/runelite/client/util/NonScheduledExecutorServiceExceptionLogger.java deleted file mode 100644 index 002969bf95..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/util/NonScheduledExecutorServiceExceptionLogger.java +++ /dev/null @@ -1,109 +0,0 @@ -package net.runelite.client.util; - -import java.util.Collection; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import static net.runelite.client.util.RunnableExceptionLogger.wrap; -import org.jetbrains.annotations.NotNull; - -// Awkward name because plugins already referenced the ExecutorServiceExceptionLogger -// (which only handles ScheduledExecutorServices) before this class was introduced -public class NonScheduledExecutorServiceExceptionLogger implements ExecutorService -{ - private final ExecutorService service; - - public NonScheduledExecutorServiceExceptionLogger(ExecutorService service) - { - this.service = service; - } - - @Override - public void shutdown() - { - service.shutdown(); - } - - @NotNull - @Override - public List shutdownNow() - { - return service.shutdownNow(); - } - - @Override - public boolean isShutdown() - { - return service.isShutdown(); - } - - @Override - public boolean isTerminated() - { - return service.isTerminated(); - } - - @Override - public boolean awaitTermination(long timeout, @NotNull TimeUnit unit) throws InterruptedException - { - return service.awaitTermination(timeout, unit); - } - - @Override - public void execute(@NotNull Runnable command) - { - service.execute(wrap(command)); - } - - @NotNull - @Override - public Future submit(@NotNull Callable task) - { - return service.submit(CallableExceptionLogger.wrap(task)); - } - - @NotNull - @Override - public Future submit(@NotNull Runnable task, T result) - { - return service.submit(wrap(task), result); - } - - @NotNull - @Override - public Future submit(@NotNull Runnable task) - { - return service.submit(wrap(task)); - } - - @NotNull - @Override - public List> invokeAll(@NotNull Collection> tasks) throws InterruptedException - { - return service.invokeAll(tasks); - } - - @NotNull - @Override - public List> invokeAll(@NotNull Collection> tasks, long timeout, @NotNull TimeUnit unit) throws InterruptedException - { - return service.invokeAll(tasks, timeout, unit); - } - - @NotNull - @Override - public T invokeAny(@NotNull Collection> tasks) throws InterruptedException, ExecutionException - { - return service.invokeAny(tasks); - } - - @Override - public T invokeAny(@NotNull Collection> tasks, long timeout, @NotNull TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException - { - return service.invokeAny(tasks, timeout, unit); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/util/PvPUtil.java b/runelite-client/src/main/java/net/runelite/client/util/PvPUtil.java deleted file mode 100644 index 935f8b953d..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/util/PvPUtil.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (c) 2019. PKLite - All Rights Reserved - * Unauthorized modification, distribution, or possession of this source file, via any medium is strictly prohibited. - * Proprietary and confidential. Refer to PKLite License file for more information on - * full terms of this copyright and to determine what constitutes authorized use. - * Written by PKLite(ST0NEWALL, others) , 2019 - * - */ -package net.runelite.client.util; - -import net.runelite.api.Client; -import net.runelite.api.InventoryID; -import net.runelite.api.Item; -import net.runelite.api.ItemDefinition; -import net.runelite.api.Player; -import net.runelite.api.Varbits; -import net.runelite.api.WorldType; -import net.runelite.api.coords.WorldPoint; -import net.runelite.api.geometry.Cuboid; -import net.runelite.client.game.ItemManager; -import org.apache.commons.lang3.ArrayUtils; -import java.awt.Polygon; -import java.util.Comparator; -import java.util.Objects; -import java.util.TreeMap; - -public class PvPUtil -{ - private static final Polygon NOT_WILDERNESS_BLACK_KNIGHTS = new Polygon( // this is black knights castle - new int[]{2994, 2995, 2996, 2996, 2994, 2994, 2997, 2998, 2998, 2999, 3000, 3001, 3002, 3003, 3004, 3005, 3005, - 3005, 3019, 3020, 3022, 3023, 3024, 3025, 3026, 3026, 3027, 3027, 3028, 3028, 3029, 3029, 3030, 3030, 3031, - 3031, 3032, 3033, 3034, 3035, 3036, 3037, 3037}, - new int[]{3525, 3526, 3527, 3529, 3529, 3534, 3534, 3535, 3536, 3537, 3538, 3539, 3540, 3541, 3542, 3543, 3544, - 3545, 3545, 3546, 3546, 3545, 3544, 3543, 3543, 3542, 3541, 3540, 3539, 3537, 3536, 3535, 3534, 3533, 3532, - 3531, 3530, 3529, 3528, 3527, 3526, 3526, 3525}, - 43 - ); - private static final Cuboid MAIN_WILDERNESS_CUBOID = new Cuboid(2944, 3525, 0, 3391, 4351, 3); - private static final Cuboid GOD_WARS_WILDERNESS_CUBOID = new Cuboid(3008, 10112, 0, 3071, 10175, 3); - private static final Cuboid WILDERNESS_UNDERGROUND_CUBOID = new Cuboid(2944, 9920, 0, 3391, 10879, 3); - - /** - * Gets the wilderness level based on a world point - * Java reimplementation of clientscript 384 [proc,wilderness_level] - * - * @param point the point in the world to get the wilderness level for - * @return the int representing the wilderness level - */ - public static int getWildernessLevelFrom(WorldPoint point) - { - if (MAIN_WILDERNESS_CUBOID.contains(point)) - { - if (NOT_WILDERNESS_BLACK_KNIGHTS.contains(point.getX(), point.getY())) - { - return 0; - } - - return ((point.getY() - 3520) / 8) + 1; // calc(((coordz(coord) - (55 * 64)) / 8) + 1) - } - else if (GOD_WARS_WILDERNESS_CUBOID.contains(point)) - { - return ((point.getY() - 9920) / 8) - 1; // calc(((coordz(coord) - (155 * 64)) / 8) - 1) - } - else if (WILDERNESS_UNDERGROUND_CUBOID.contains(point)) - { - return ((point.getY() - 9920) / 8) + 1; // calc(((coordz(coord) - (155 * 64)) / 8) + 1) - } - return 0; - } - - /** - * Determines if another player is attackable based off of wilderness level and combat levels - * - * @param client The client of the local player - * @param player the player to determine attackability - * @return returns true if the player is attackable, false otherwise - */ - public static boolean isAttackable(Client client, Player player) - { - int wildernessLevel = 0; - - if (WorldType.isDeadmanWorld(client.getWorldType())) - { - return true; - } - if (WorldType.isPvpWorld(client.getWorldType())) - { - wildernessLevel += 15; - } - if (client.getVar(Varbits.IN_WILDERNESS) == 1) - { - wildernessLevel += getWildernessLevelFrom(client.getLocalPlayer().getWorldLocation()); - } - return wildernessLevel != 0 && Math.abs(client.getLocalPlayer().getCombatLevel() - player.getCombatLevel()) <= wildernessLevel; - } - - public static int calculateRisk(Client client, ItemManager itemManager) - { - if (client.getItemContainer(InventoryID.EQUIPMENT) == null) - { - return 0; - } - if (client.getItemContainer(InventoryID.INVENTORY).getItems() == null) - { - return 0; - } - Item[] items = ArrayUtils.addAll(Objects.requireNonNull(client.getItemContainer(InventoryID.EQUIPMENT)).getItems(), - Objects.requireNonNull(client.getItemContainer(InventoryID.INVENTORY)).getItems()); - TreeMap priceMap = new TreeMap<>(Comparator.comparingInt(Integer::intValue)); - int wealth = 0; - for (Item i : items) - { - int value = (itemManager.getItemPrice(i.getId()) * i.getQuantity()); - - final ItemDefinition itemComposition = itemManager.getItemDefinition(i.getId()); - if (!itemComposition.isTradeable() && value == 0) - { - value = itemComposition.getPrice() * i.getQuantity(); - priceMap.put(value, i); - } - else - { - value = itemManager.getItemPrice(i.getId()) * i.getQuantity(); - if (i.getId() > 0 && value > 0) - { - priceMap.put(value, i); - } - } - wealth += value; - } - return Integer.parseInt(QuantityFormatter.quantityToRSDecimalStack(priceMap.keySet().stream().mapToInt(Integer::intValue).sum())); - - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/util/QuantityFormatter.java b/runelite-client/src/main/java/net/runelite/client/util/QuantityFormatter.java index b63c739a88..49823b5cca 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/QuantityFormatter.java +++ b/runelite-client/src/main/java/net/runelite/client/util/QuantityFormatter.java @@ -28,13 +28,9 @@ import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.NumberFormat; import java.text.ParseException; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.time.format.FormatStyle; import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; -import net.runelite.client.RuneLite; /** * A set of utility functions to use when formatting quantities @@ -63,21 +59,9 @@ public class QuantityFormatter DecimalFormatSymbols.getInstance(Locale.ENGLISH) ); - /** - * Get a localized DateTimeFormatter for use. - * - * @param formatStyle The format style to use for the formatter - * @return The localized DateTimeFormatter - */ - public static DateTimeFormatter getLocalizedDateTimeFormatter(FormatStyle formatStyle) - { - return DateTimeFormatter.ofLocalizedTime(formatStyle).withLocale(RuneLite.SYSTEM_LOCALE); - } - - /** * Convert a quantity to a short, comma separated, SI-prefix style string - *

+ * * example: {@code 9,450}, {@code 2.14B}, {@code 100K} * * @param quantity The quantity to convert. @@ -123,10 +107,10 @@ public class QuantityFormatter /** * Convert a quantity to a short SI-prefix style string, possibly with a decimal, * with K after 100,000 and M after 10,000,000 - *

+ * * example: {@code 9,450}, {@code 2.1B}, {@code 100K} - * - * @see #quantityToRSDecimalStack(int, boolean) + * + * @see #quantityToRSDecimalStack(int, boolean) */ public static String quantityToRSDecimalStack(int quantity) { @@ -136,7 +120,7 @@ public class QuantityFormatter /** * Convert a quantity to a short SI-prefix style string, possibly with decimals, * with K after 100,000 and M after 10,000,000 - *

+ * * example without {@code precise}: {@code 9,450}, {@code 2.1B}, {@code 8.4M} * example with {@code precise}: {@code 9,450}, {@code 2.147B}, {@code 8.32M} * @@ -177,7 +161,7 @@ public class QuantityFormatter /** * Formats a number to be comma delimited. No suffixes are given - *

+ * * example: {@code 10,123,351}, {@code 5} */ public static synchronized String formatNumber(final long number) @@ -189,7 +173,7 @@ public class QuantityFormatter /** * Formats a number to be comma delimited. No suffixes are given. Has at * most 3 decimal places - *

+ * * example: {@code 10,123,351}, {@code 5.612} */ public static synchronized String formatNumber(double number) @@ -235,21 +219,4 @@ public class QuantityFormatter return 1; } } - - /** - * Attempts to call the platform to get a localized time string based on - * the users preferences. Falls back on using locale default if it is on a - * platform that has no consistent way of obtaining this information. - * - * @param localDateTime The LocalDateTime object to format as a string - * @return The formatted string. - */ - public static String getPlatformTimeStringFromLocalDateTime(LocalDateTime localDateTime) - { - if (OSType.getOSType() == OSType.Windows) - { - return WinApi.getTimeFormatString(localDateTime); - } - return QuantityFormatter.getLocalizedDateTimeFormatter(FormatStyle.SHORT).format(localDateTime.toLocalTime()); - } } diff --git a/runelite-client/src/main/java/net/runelite/client/util/ReflectUtil.java b/runelite-client/src/main/java/net/runelite/client/util/ReflectUtil.java new file mode 100644 index 0000000000..60b3b305f1 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/util/ReflectUtil.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2018, Tomas Slusny + * Copyright (c) 2018, 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.util; + +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class ReflectUtil +{ + private ReflectUtil() + { + } + + public static MethodHandles.Lookup privateLookupIn(Class clazz) + { + try + { + // Java 9+ has privateLookupIn method on MethodHandles, but since we are shipping and using Java 8 + // we need to access it via reflection. This is preferred way because it's Java 9+ public api and is + // likely to not change + final Method privateLookupIn = MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class); + return (MethodHandles.Lookup) privateLookupIn.invoke(null, clazz, MethodHandles.lookup()); + } + catch (InvocationTargetException | IllegalAccessException e) + { + throw new RuntimeException(e); + } + catch (NoSuchMethodException e) + { + try + { + // In Java 8 we first do standard lookupIn class + final MethodHandles.Lookup lookupIn = MethodHandles.lookup().in(clazz); + + // and then we mark it as trusted for private lookup via reflection on private field + final Field modes = MethodHandles.Lookup.class.getDeclaredField("allowedModes"); + modes.setAccessible(true); + modes.setInt(lookupIn, -1); // -1 == TRUSTED + return lookupIn; + } + catch (ReflectiveOperationException ex) + { + throw new RuntimeException(ex); // NOPMD: PreserveStackTrace: ignore e + } + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/util/StringFileUtils.java b/runelite-client/src/main/java/net/runelite/client/util/StringFileUtils.java deleted file mode 100644 index be4b56199b..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/util/StringFileUtils.java +++ /dev/null @@ -1,53 +0,0 @@ -package net.runelite.client.util; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.PrintWriter; -import java.nio.file.Files; -import java.nio.file.Paths; - -public class StringFileUtils -{ - //Read file content into string with - Files.readAllBytes(Path path) - - public static String readStringFromFile(String filePath) - { - String content = ""; - - try - { - content = new String(Files.readAllBytes(Paths.get(filePath))); - } - catch (IOException e) - { - e.printStackTrace(); - } - - return content; - } - - public static void writeStringToFile(String file, String string) - { - File f = new File(file); - if (!f.exists()) - { - try - { - f.createNewFile(); - } - catch (IOException e) - { - e.printStackTrace(); - } - } - try (PrintWriter out = new PrintWriter(file)) - { - out.println(string); - } - catch (FileNotFoundException e) - { - e.printStackTrace(); - } - } -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/util/SwingUtil.java b/runelite-client/src/main/java/net/runelite/client/util/SwingUtil.java index 9fe4d59cc5..90723d9ade 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/SwingUtil.java +++ b/runelite-client/src/main/java/net/runelite/client/util/SwingUtil.java @@ -25,11 +25,9 @@ package net.runelite.client.util; import java.awt.AWTException; -import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Container; -import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Font; import java.awt.Frame; @@ -42,40 +40,25 @@ import java.awt.TrayIcon; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; -import java.lang.reflect.InvocationTargetException; import java.util.Enumeration; import java.util.function.BiConsumer; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.swing.AbstractButton; -import javax.swing.ButtonModel; import javax.swing.ImageIcon; import javax.swing.JButton; -import javax.swing.JComponent; -import javax.swing.JDialog; -import javax.swing.JLabel; import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.LookAndFeel; import javax.swing.SwingUtilities; import javax.swing.ToolTipManager; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; -import javax.swing.border.Border; -import javax.swing.border.CompoundBorder; -import javax.swing.border.EmptyBorder; -import javax.swing.border.MatteBorder; import javax.swing.plaf.FontUIResource; -import javax.swing.plaf.basic.BasicProgressBarUI; import lombok.extern.slf4j.Slf4j; import net.runelite.client.ui.ColorScheme; -import net.runelite.client.ui.FontManager; import net.runelite.client.ui.NavigationButton; import net.runelite.client.ui.components.CustomScrollBarUI; -import net.runelite.client.ui.components.SliderUI; -import net.runelite.client.ui.skin.SubstanceRuneLiteLookAndFeel; import org.pushingpixels.substance.internal.SubstanceSynapse; /** @@ -84,13 +67,11 @@ import org.pushingpixels.substance.internal.SubstanceSynapse; @Slf4j public class SwingUtil { - private static boolean lookAndFeelIsSet = false; - /** * Sets some sensible defaults for swing. * IMPORTANT! Needs to be called before main frame creation */ - private static void setupDefaults() + public static void setupDefaults() { // Force heavy-weight popups/tooltips. // Prevents them from being obscured by the game applet. @@ -102,21 +83,12 @@ public class SwingUtil UIManager.put("MenuItem.foreground", Color.WHITE); UIManager.put("Panel.background", ColorScheme.DARK_GRAY_COLOR); UIManager.put("ScrollBarUI", CustomScrollBarUI.class.getName()); - UIManager.put("TextField.selectionBackground", ColorScheme.BRAND_BLUE_TRANSPARENT); + UIManager.put("TextField.selectionBackground", ColorScheme.BRAND_ORANGE_TRANSPARENT); UIManager.put("TextField.selectionForeground", Color.WHITE); - UIManager.put("FormattedTextField.selectionBackground", ColorScheme.BRAND_BLUE_TRANSPARENT); + UIManager.put("FormattedTextField.selectionBackground", ColorScheme.BRAND_ORANGE_TRANSPARENT); UIManager.put("FormattedTextField.selectionForeground", Color.WHITE); - UIManager.put("TextArea.selectionBackground", ColorScheme.BRAND_BLUE_TRANSPARENT); + UIManager.put("TextArea.selectionBackground", ColorScheme.BRAND_ORANGE_TRANSPARENT); UIManager.put("TextArea.selectionForeground", Color.WHITE); - UIManager.put("ProgressBar.background", ColorScheme.BRAND_BLUE_TRANSPARENT.darker()); - UIManager.put("ProgressBar.foreground", ColorScheme.BRAND_BLUE); - UIManager.put("ProgressBar.selectionBackground", ColorScheme.BRAND_BLUE); - UIManager.put("ProgressBar.selectionForeground", Color.BLACK); - UIManager.put("ProgressBar.border", new EmptyBorder(0, 0, 0, 0)); - UIManager.put("ProgressBar.verticalSize", new Dimension(12, 10)); - UIManager.put("ProgressBar.horizontalSize", new Dimension(10, 12)); - UIManager.put("ProgressBarUI", BasicProgressBarUI.class.getName()); - UIManager.put("SliderUI", SliderUI.class.getName()); // Do not render shadows under popups/tooltips. // Fixes black boxes under popups that are above the game applet. @@ -132,7 +104,7 @@ public class SwingUtil * * @param laf the swing look and feel */ - private static void setTheme(@Nonnull final LookAndFeel laf) + public static void setTheme(@Nonnull final LookAndFeel laf) { try { @@ -150,7 +122,7 @@ public class SwingUtil * * @param font the new font to use */ - private static void setFont(@Nonnull final Font font) + public static void setFont(@Nonnull final Font font) { final FontUIResource f = new FontUIResource(font); final Enumeration keys = UIManager.getDefaults().keys(); @@ -266,160 +238,6 @@ public class SwingUtil return button; } - /** - * Creates a custom {@link JButton} with a flat design for use inside {@link JOptionPane}. - * The button will display the passed {@code text} and set the value of the pane to {@code buttonOption} on click - * - * @param text text to be displayed inside the button - * @param buttonOption the code to be set via {@link JOptionPane#setValue(Object)} - * @return newly created {@link JButton} - */ - public static JButton createFlatButton(final String text, final int buttonOption) - { - final Border BUTTON_BORDER = new EmptyBorder(5, 17, 5, 17); - final Border BORDERED_BUTTON_BORDER = new CompoundBorder( - new MatteBorder(1, 1, 1, 1, Color.BLACK), - new EmptyBorder(4, 16, 4, 16) - ); - - final JButton button = new JButton(text); - button.setForeground(Color.WHITE); - button.setBackground(Color.BLACK); - button.setFont(FontManager.getRunescapeFont()); - button.setBorder(BUTTON_BORDER); - - button.setBorderPainted(false); - button.setFocusPainted(false); - button.setContentAreaFilled(false); - button.setOpaque(true); - - // Selecting the button option requires us to determine which parent element is the JOptionPane - button.addActionListener(e -> - { - JComponent component = (JComponent) e.getSource(); - while (component != null) - { - if (component instanceof JOptionPane) - { - ((JOptionPane) component).setValue(buttonOption); - component = null; - } - else - { - component = component.getParent() == null ? null : (JComponent) component.getParent(); - } - } - }); - - // Use change listener instead of mouse listener for buttons - button.getModel().addChangeListener(e -> - { - final ButtonModel model = (ButtonModel) e.getSource(); - button.setBackground(model.isRollover() ? ColorScheme.DARKER_GRAY_HOVER_COLOR : Color.BLACK); - button.setBorderPainted(model.isPressed()); - button.setBorder(model.isPressed() ? BORDERED_BUTTON_BORDER : BUTTON_BORDER); - }); - - return button; - } - - /** - * Opens a {@link JDialog} with a stylized {@link JOptionPane} ignoring UIManager defaults. - * The buttons should be created via the {@link #createFlatButton(String, int)} function to look correctly - * - * @param component The frame the dialog should be attached to. nullable - * @param content The string content to be added to the content pane - * @param optionType The JOptionPane option type of dialog pane to create - * @param buttons Buttons to display, created via {@link #createFlatButton(String, int)} - * @return The Integer value representing the button selected - */ - public static int showRuneLiteOptionPane(final JComponent component, final String content, final int optionType, final JButton[] buttons) - { - final JLabel contentLabel = new JLabel(content); - contentLabel.setFont(FontManager.getRunescapeFont()); - contentLabel.setForeground(Color.WHITE); - contentLabel.setBackground(ColorScheme.DARKER_GRAY_COLOR); - - final JPanel p = new JPanel(new BorderLayout()); - p.setBackground(ColorScheme.DARKER_GRAY_COLOR); - p.setForeground(Color.WHITE); - p.add(contentLabel, BorderLayout.NORTH); - - final JOptionPane pane = new JOptionPane(p, - JOptionPane.ERROR_MESSAGE, - optionType, - null, - buttons, - buttons[1]); - pane.setBackground(ColorScheme.DARKER_GRAY_COLOR); - pane.setForeground(Color.WHITE); - stylizeJPanels(pane); - - final Frame frame = component == null ? JOptionPane.getRootFrame() : JOptionPane.getFrameForComponent(component); - final JDialog dialog = new JDialog(frame, "OpenOSRS Error", true); - dialog.setContentPane(pane); - dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); - dialog.setAlwaysOnTop(true); - dialog.setAutoRequestFocus(true); - dialog.setLocationRelativeTo(null); - dialog.setIconImage(ImageUtil.getResourceStreamFromClass(SwingUtil.class, "/openosrs.png")); - - // Listen for value changes and close dialog when necessary - pane.addPropertyChangeListener(e -> - { - String prop = e.getPropertyName(); - - if (dialog.isVisible() - && (e.getSource() == pane) - && (prop.equals(JOptionPane.VALUE_PROPERTY))) - { - dialog.setVisible(false); - } - }); - - dialog.pack(); - // Try to center dialog based on its size - dialog.setLocation(dialog.getX() - dialog.getSize().width / 2, dialog.getY() - dialog.getSize().height / 2); - dialog.setVisible(true); - - return (Integer) pane.getValue(); - } - - private static void stylizeJPanels(final JComponent component) - { - for (final Component c : component.getComponents()) - { - if (c instanceof JPanel) - { - c.setBackground(ColorScheme.DARKER_GRAY_COLOR); - c.setForeground(Color.WHITE); - stylizeJPanels((JComponent) c); - } - } - } - - /** - * Sets up the RuneLite look and feel. Checks to see if the look and feel - * was already set up before running in case the splash screen has already - * set up the theme. - * This must be run inside the Swing Event Dispatch thread. - */ - public static void setupRuneLiteLookAndFeel() - { - if (!lookAndFeelIsSet) - { - lookAndFeelIsSet = true; - // Set some sensible swing defaults - SwingUtil.setupDefaults(); - // Use substance look and feel - SwingUtil.setTheme(new SubstanceRuneLiteLookAndFeel()); - // Use custom UI font - //TODO : SUPPORT CUSTOM FONT? - //SwingUtil.setFont(FontManager.getFontOrDefault(config.clientFont())); - SwingUtil.setFont(FontManager.getRunescapeFont()); - } - } - public static void removeButtonDecorations(AbstractButton button) { button.setBorderPainted(false); @@ -435,18 +253,6 @@ public class SwingUtil button.addItemListener(l -> button.setToolTipText(button.isSelected() ? on : off)); } - public static void syncExec(final Runnable r) throws InvocationTargetException, InterruptedException - { - if (EventQueue.isDispatchThread()) - { - r.run(); - } - else - { - EventQueue.invokeAndWait(r); - } - } - /** * Removes all of a component's children faster than calling removeAll() on it in many cases */ diff --git a/runelite-client/src/main/java/net/runelite/client/util/Text.java b/runelite-client/src/main/java/net/runelite/client/util/Text.java new file mode 100644 index 0000000000..3050554a71 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/util/Text.java @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2018, Joshua Filby + * Copyright (c) 2018, Jordan Atwood + * 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.util; + +import com.google.common.base.CharMatcher; +import com.google.common.base.Joiner; +import com.google.common.base.Splitter; +import java.util.Collection; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.apache.commons.text.WordUtils; +import org.apache.commons.text.similarity.JaroWinklerDistance; + +/** + * A set of utilities to use when dealing with text. + */ +public class Text +{ + private static final JaroWinklerDistance DISTANCE = new JaroWinklerDistance(); + private static final Pattern TAG_REGEXP = Pattern.compile("<[^>]*>"); + private static final Splitter COMMA_SPLITTER = Splitter + .on(",") + .omitEmptyStrings() + .trimResults(); + + private static final Joiner COMMA_JOINER = Joiner.on(",").skipNulls(); + + public static final CharMatcher JAGEX_PRINTABLE_CHAR_MATCHER = new JagexPrintableCharMatcher(); + + /** + * Splits comma separated values to list of strings + * + * @param input input + * @return list of values + */ + public static List fromCSV(final String input) + { + return COMMA_SPLITTER.splitToList(input); + } + + /** + * Joins collection of strings as comma separated values + * + * @param input collection + * @return comma separated value string + */ + public static String toCSV(final Collection input) + { + return COMMA_JOINER.join(input); + } + + /** + * Removes all tags from the given string. + * + * @param str The string to remove tags from. + * @return The given string with all tags removed from it. + */ + public static String removeTags(String str) + { + return TAG_REGEXP.matcher(str).replaceAll(""); + } + + /** + * Remove tags from the given string, except for <lt> and <gt> + * + * @param str The string to remove formatting tags from. + * @return The given string with all formatting tags removed from it. + */ + public static String removeFormattingTags(String str) + { + StringBuffer stringBuffer = new StringBuffer(); + Matcher matcher = TAG_REGEXP.matcher(str); + while (matcher.find()) + { + matcher.appendReplacement(stringBuffer, ""); + String match = matcher.group(0); + switch (match) + { + case "": + case "": + stringBuffer.append(match); + break; + } + } + matcher.appendTail(stringBuffer); + return stringBuffer.toString(); + } + + /** + * In addition to removing all tags, replaces nbsp with space, trims string and lowercases it + * @param str The string to standardize + * + * @return The given `str` that is standardized + */ + public static String standardize(String str) + { + return removeTags(str).replace('\u00A0', ' ').trim().toLowerCase(); + } + + /** + * Convert a string into Jagex username format + * Remove all non-ascii characters, replace nbsp with space, replace _- with spaces, and trim + * + * @param str The string to standardize + * @return The given `str` that is in Jagex name format + */ + public static String toJagexName(String str) + { + return CharMatcher.ascii().retainFrom(str.replace('\u00A0', ' ')).replaceAll("[_-]+", " ").trim(); + } + + /** + * In addition to removing all tags, replaces all <br> delimited text with spaces and all multiple continuous + * spaces with single space + * + * @param str The string to sanitize + * @return sanitized string + */ + public static String sanitizeMultilineText(String str) + { + return removeTags(str + .replaceAll("-
", "-") + .replaceAll("
", " ") + .replaceAll("[ ]+", " ")); + } + + /** + * Escapes a string for widgets, replacing < and > with their escaped counterparts + */ + public static String escapeJagex(String str) + { + StringBuilder out = new StringBuilder(str.length()); + + for (int i = 0; i < str.length(); i++) + { + char c = str.charAt(i); + if (c == '<') + { + out.append(""); + } + else if (c == '>') + { + out.append(""); + } + else if (c == '\n') + { + out.append("
"); + } + else if (c != '\r') + { + out.append(c); + } + } + + return out.toString(); + } + + /** + * Cleans the ironman status icon from playername string if present and + * corrects spaces. + * + * @param name Playername to lookup. + * @return Cleaned playername. + */ + public static String sanitize(String name) + { + String cleaned = name.contains("') + 1) : name; + return cleaned.replace('\u00A0', ' '); + } + + /** + * If passed in enum doesn't implement its own toString, + * converts enum name format from THIS_FORMAT to This Format. + * + * @param o an enum + * @return the enum's name in title case, + * or if it overrides toString, + * the value returned by toString + */ + public static String titleCase(Enum o) + { + String toString = o.toString(); + + // .toString() returns the value of .name() if not overridden + if (o.name().equals(toString)) + { + return WordUtils + .capitalize(toString.toLowerCase(), '_') + .replace("_", " "); + } + + return toString; + } + + /** + * Checks if all the search terms in the given list matches at least one keyword. + * + * @return true if all search terms matches at least one keyword, or false if otherwise. + */ + public static boolean matchesSearchTerms(Iterable searchTerms, final Collection keywords) + { + for (String term : searchTerms) + { + if (keywords.stream().noneMatch((t) -> t.contains(term) || + DISTANCE.apply(t, term) > 0.9)) + { + return false; + } + } + return true; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/util/TransferableBufferedImage.java b/runelite-client/src/main/java/net/runelite/client/util/TransferableBufferedImage.java index d29aace857..f87e52564b 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/TransferableBufferedImage.java +++ b/runelite-client/src/main/java/net/runelite/client/util/TransferableBufferedImage.java @@ -32,7 +32,6 @@ import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.image.BufferedImage; import lombok.AllArgsConstructor; import lombok.NonNull; -import org.jetbrains.annotations.NotNull; @AllArgsConstructor class TransferableBufferedImage implements Transferable @@ -40,7 +39,6 @@ class TransferableBufferedImage implements Transferable @NonNull private final BufferedImage image; - @NotNull @Override public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException { diff --git a/runelite-client/src/main/java/net/runelite/client/util/WeaponMap.java b/runelite-client/src/main/java/net/runelite/client/util/WeaponMap.java deleted file mode 100644 index 929618747f..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/util/WeaponMap.java +++ /dev/null @@ -1,832 +0,0 @@ -package net.runelite.client.util; - -import java.util.HashMap; -import net.runelite.api.ItemID; - -public class WeaponMap -{ - public static HashMap StyleMap = new HashMap<>(); - - static - { - //Melee - StyleMap.put(ItemID._3RD_AGE_AXE, WeaponStyle.MELEE); - StyleMap.put(ItemID._3RD_AGE_LONGSWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID._3RD_AGE_PICKAXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.ABYSSAL_BLUDGEON, WeaponStyle.MELEE); - StyleMap.put(ItemID.ABYSSAL_DAGGER, WeaponStyle.MELEE); - StyleMap.put(ItemID.ABYSSAL_DAGGER_P, WeaponStyle.MELEE); - StyleMap.put(ItemID.ABYSSAL_DAGGER_P_13269, WeaponStyle.MELEE); - StyleMap.put(ItemID.ABYSSAL_DAGGER_P_13271, WeaponStyle.MELEE); - StyleMap.put(ItemID.ABYSSAL_TENTACLE, WeaponStyle.MELEE); - StyleMap.put(ItemID.ABYSSAL_WHIP, WeaponStyle.MELEE); - StyleMap.put(ItemID.ABYSSAL_WHIP_20405, WeaponStyle.MELEE); - StyleMap.put(ItemID.ADAMANT_2H_SWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.ADAMANT_AXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.ADAMANT_BATTLEAXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.ADAMANT_CANE, WeaponStyle.MELEE); - StyleMap.put(ItemID.ADAMANT_CLAWS, WeaponStyle.MELEE); - StyleMap.put(ItemID.ADAMANT_DAGGER, WeaponStyle.MELEE); - StyleMap.put(ItemID.ADAMANT_DAGGERP, WeaponStyle.MELEE); - StyleMap.put(ItemID.ADAMANT_DAGGERP_5676, WeaponStyle.MELEE); - StyleMap.put(ItemID.ADAMANT_DAGGERP_5694, WeaponStyle.MELEE); - StyleMap.put(ItemID.ADAMANT_HALBERD, WeaponStyle.MELEE); - StyleMap.put(ItemID.ADAMANT_HASTA, WeaponStyle.MELEE); - StyleMap.put(ItemID.ADAMANT_LONGSWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.ADAMANT_MACE, WeaponStyle.MELEE); - StyleMap.put(ItemID.ADAMANT_PICKAXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.ADAMANT_SCIMITAR, WeaponStyle.MELEE); - StyleMap.put(ItemID.ADAMANT_SPEAR, WeaponStyle.MELEE); - StyleMap.put(ItemID.ADAMANT_SPEARP, WeaponStyle.MELEE); - StyleMap.put(ItemID.ADAMANT_SPEARP_5712, WeaponStyle.MELEE); - StyleMap.put(ItemID.ADAMANT_SPEARP_5726, WeaponStyle.MELEE); - StyleMap.put(ItemID.ADAMANT_SWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.ADAMANT_WARHAMMER, WeaponStyle.MELEE); - StyleMap.put(ItemID.ALE_OF_THE_GODS, WeaponStyle.MELEE); - StyleMap.put(ItemID.ANCIENT_MACE, WeaponStyle.MELEE); - StyleMap.put(ItemID.ANGER_BATTLEAXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.ANGER_MACE, WeaponStyle.MELEE); - StyleMap.put(ItemID.ANGER_SPEAR, WeaponStyle.MELEE); - StyleMap.put(ItemID.ANGER_SWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.AMYS_SAW, WeaponStyle.MELEE); - StyleMap.put(ItemID.ARCEUUS_BANNER, WeaponStyle.MELEE); - StyleMap.put(ItemID.ARCLIGHT, WeaponStyle.MELEE); - StyleMap.put(ItemID.ARMADYL_GODSWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.ARMADYL_GODSWORD_20593, WeaponStyle.MELEE); - StyleMap.put(ItemID.ARMADYL_GODSWORD_22665, WeaponStyle.MELEE); - StyleMap.put(ItemID.ARMADYL_GODSWORD_OR, WeaponStyle.MELEE); - StyleMap.put(ItemID.ASSORTED_FLOWERS, WeaponStyle.MELEE); - StyleMap.put(ItemID.BANDOS_GODSWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.BANDOS_GODSWORD_20782, WeaponStyle.MELEE); - StyleMap.put(ItemID.BANDOS_GODSWORD_21060, WeaponStyle.MELEE); - StyleMap.put(ItemID.BANDOS_GODSWORD_OR, WeaponStyle.MELEE); - StyleMap.put(ItemID.BARBTAIL_HARPOON, WeaponStyle.MELEE); - StyleMap.put(ItemID.BARRELCHEST_ANCHOR, WeaponStyle.MELEE); - StyleMap.put(ItemID.BEACH_BOXING_GLOVES, WeaponStyle.MELEE); - StyleMap.put(ItemID.BEACH_BOXING_GLOVES_11706, WeaponStyle.MELEE); - StyleMap.put(ItemID.BIRTHDAY_BALLOONS, WeaponStyle.MELEE); - StyleMap.put(ItemID.BIRTHDAY_CAKE, WeaponStyle.MELEE); - StyleMap.put(ItemID.BLACK_2H_SWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.BLACK_AXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.BLACK_BATTLEAXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.BLACK_CANE, WeaponStyle.MELEE); - StyleMap.put(ItemID.BLACK_CLAWS, WeaponStyle.MELEE); - StyleMap.put(ItemID.BLACK_DAGGER, WeaponStyle.MELEE); - StyleMap.put(ItemID.BLACK_DAGGERP, WeaponStyle.MELEE); - StyleMap.put(ItemID.BLACK_DAGGERP_5682, WeaponStyle.MELEE); - StyleMap.put(ItemID.BLACK_DAGGERP_5700, WeaponStyle.MELEE); - StyleMap.put(ItemID.BLACK_FLOWERS, WeaponStyle.MELEE); - StyleMap.put(ItemID.BLACK_HALBERD, WeaponStyle.MELEE); - StyleMap.put(ItemID.BLACK_LONGSWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.BLACK_MACE, WeaponStyle.MELEE); - StyleMap.put(ItemID.BLACK_PICKAXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.BLACK_SALAMANDER, WeaponStyle.MELEE); - StyleMap.put(ItemID.BLACK_SCIMITAR, WeaponStyle.MELEE); - StyleMap.put(ItemID.BLACK_SPEAR, WeaponStyle.MELEE); - StyleMap.put(ItemID.BLACK_SPEARP, WeaponStyle.MELEE); - StyleMap.put(ItemID.BLACK_SPEARP_5734, WeaponStyle.MELEE); - StyleMap.put(ItemID.BLACK_SPEARP_5736, WeaponStyle.MELEE); - StyleMap.put(ItemID.BLACK_SWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.BLACK_WARHAMMER, WeaponStyle.MELEE); - StyleMap.put(ItemID.BLADE_OF_SAELDOR, WeaponStyle.MELEE); - StyleMap.put(ItemID.BLADE_OF_SAELDOR_C, WeaponStyle.MELEE); - StyleMap.put(ItemID.BLADE_OF_SAELDOR_INACTIVE, WeaponStyle.MELEE); - StyleMap.put(ItemID.BLESSED_AXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.BLISTERWOOD_SICKLE, WeaponStyle.MELEE); - StyleMap.put(ItemID.BLUE_FLOWERS, WeaponStyle.MELEE); - StyleMap.put(ItemID.BLUE_FLOWERS_8936, WeaponStyle.MELEE); - StyleMap.put(ItemID.BLURITE_SWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.BONE_CLUB, WeaponStyle.MELEE); - StyleMap.put(ItemID.BONE_DAGGER, WeaponStyle.MELEE); - StyleMap.put(ItemID.BONE_DAGGER_P, WeaponStyle.MELEE); - StyleMap.put(ItemID.BONE_DAGGER_P_8876, WeaponStyle.MELEE); - StyleMap.put(ItemID.BONE_DAGGER_P_8878, WeaponStyle.MELEE); - StyleMap.put(ItemID.BONE_SPEAR, WeaponStyle.MELEE); - StyleMap.put(ItemID.BOXING_GLOVES, WeaponStyle.MELEE); - StyleMap.put(ItemID.BOXING_GLOVES_7673, WeaponStyle.MELEE); - StyleMap.put(ItemID.BRINE_SABRE, WeaponStyle.MELEE); - StyleMap.put(ItemID.BRONZE_2H_SWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.BRONZE_AXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.BRONZE_BATTLEAXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.BRONZE_CLAWS, WeaponStyle.MELEE); - StyleMap.put(ItemID.BRONZE_DAGGER, WeaponStyle.MELEE); - StyleMap.put(ItemID.BRONZE_DAGGERP, WeaponStyle.MELEE); - StyleMap.put(ItemID.BRONZE_DAGGERP_5670, WeaponStyle.MELEE); - StyleMap.put(ItemID.BRONZE_DAGGERP_5688, WeaponStyle.MELEE); - StyleMap.put(ItemID.BRONZE_HALBERD, WeaponStyle.MELEE); - StyleMap.put(ItemID.BRONZE_HASTA, WeaponStyle.MELEE); - StyleMap.put(ItemID.BRONZE_LONGSWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.BRONZE_MACE, WeaponStyle.MELEE); - StyleMap.put(ItemID.BRONZE_PICKAXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.BRONZE_SCIMITAR, WeaponStyle.MELEE); - StyleMap.put(ItemID.BRONZE_SPEAR, WeaponStyle.MELEE); - StyleMap.put(ItemID.BRONZE_SPEARP, WeaponStyle.MELEE); - StyleMap.put(ItemID.BRONZE_SPEARP_5704, WeaponStyle.MELEE); - StyleMap.put(ItemID.BRONZE_SPEARP_5718, WeaponStyle.MELEE); - StyleMap.put(ItemID.BRONZE_SWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.BRONZE_WARHAMMER, WeaponStyle.MELEE); - StyleMap.put(ItemID.BRUMA_TORCH, WeaponStyle.MELEE); - StyleMap.put(ItemID.BUTTERFLY_NET, WeaponStyle.MELEE); - StyleMap.put(ItemID.CANDY_CANE, WeaponStyle.MELEE); - StyleMap.put(ItemID.CATTLEPROD, WeaponStyle.MELEE); - StyleMap.put(ItemID.CHAOTIC_HANDEGG, WeaponStyle.MELEE); - StyleMap.put(ItemID.CLEAVER, WeaponStyle.MELEE); - StyleMap.put(ItemID.CORRUPTED_AXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.CORRUPTED_HALBERD_ATTUNED, WeaponStyle.MELEE); - StyleMap.put(ItemID.CORRUPTED_HALBERD_BASIC, WeaponStyle.MELEE); - StyleMap.put(ItemID.CORRUPTED_HALBERD_PERFECTED, WeaponStyle.MELEE); - StyleMap.put(ItemID.CORRUPTED_HARPOON, WeaponStyle.MELEE); - StyleMap.put(ItemID.CORRUPTED_PICKAXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.CORRUPTED_SCEPTRE, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRIER_BELL, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_AXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_AXE_23862, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_AXE_INACTIVE, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_HALBERD, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_HALBERD_110, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_HALBERD_110_I, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_HALBERD_210, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_HALBERD_210_I, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_HALBERD_24125, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_HALBERD_310, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_HALBERD_310_I, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_HALBERD_410, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_HALBERD_410_I, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_HALBERD_510, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_HALBERD_510_I, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_HALBERD_610, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_HALBERD_610_I, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_HALBERD_710, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_HALBERD_710_I, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_HALBERD_810, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_HALBERD_810_I, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_HALBERD_910, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_HALBERD_910_I, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_HALBERD_ATTUNED, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_HALBERD_BASIC, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_HALBERD_FULL, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_HALBERD_FULL_I, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_HALBERD_INACTIVE, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_HALBERD_PERFECTED, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_HARPOON, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_HARPOON_23864, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_HARPOON_INACTIVE, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_PICKAXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_PICKAXE_23863, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_PICKAXE_INACTIVE, WeaponStyle.MELEE); - StyleMap.put(ItemID.CRYSTAL_SCEPTRE, WeaponStyle.MELEE); - StyleMap.put(ItemID.CURSED_GOBLIN_HAMMER, WeaponStyle.MELEE); - StyleMap.put(ItemID.DARKLIGHT, WeaponStyle.MELEE); - StyleMap.put(ItemID.DARK_DAGGER, WeaponStyle.MELEE); - StyleMap.put(ItemID.DECORATIVE_SWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.DECORATIVE_SWORD_4503, WeaponStyle.MELEE); - StyleMap.put(ItemID.DECORATIVE_SWORD_4508, WeaponStyle.MELEE); - StyleMap.put(ItemID.DHAROKS_GREATAXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.DHAROKS_GREATAXE_0, WeaponStyle.MELEE); - StyleMap.put(ItemID.DHAROKS_GREATAXE_100, WeaponStyle.MELEE); - StyleMap.put(ItemID.DHAROKS_GREATAXE_25, WeaponStyle.MELEE); - StyleMap.put(ItemID.DHAROKS_GREATAXE_50, WeaponStyle.MELEE); - StyleMap.put(ItemID.DHAROKS_GREATAXE_75, WeaponStyle.MELEE); - StyleMap.put(ItemID.DINHS_BULWARK, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_2H_SWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_2H_SWORD_20559, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_AXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_BATTLEAXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_CANE, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_CLAWS, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_CLAWS_20784, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_DAGGER, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_DAGGER_20407, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_DAGGERP, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_DAGGERP_5680, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_DAGGERP_5698, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_HALBERD, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_HARPOON, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_HASTA, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_HASTAKP, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_HASTAP, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_HASTAP_22737, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_HASTAP_22740, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_HUNTER_LANCE, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_LONGSWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_MACE, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_PICKAXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_PICKAXE_12797, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_PICKAXE_OR, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_SCIMITAR, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_SCIMITAR_20406, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_SCIMITAR_OR, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_SPEAR, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_SPEARP, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_SPEARP_5716, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_SPEARP_5730, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_SWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_WARHAMMER, WeaponStyle.MELEE); - StyleMap.put(ItemID.DRAGON_WARHAMMER_20785, WeaponStyle.MELEE); - StyleMap.put(ItemID.EASTER_BASKET, WeaponStyle.MELEE); - StyleMap.put(ItemID.EGG_WHISK, WeaponStyle.MELEE); - StyleMap.put(ItemID.ELDER_MAUL, WeaponStyle.MELEE); - StyleMap.put(ItemID.ELDER_MAUL_21205, WeaponStyle.MELEE); - StyleMap.put(ItemID.ENCHANTED_LYRE, WeaponStyle.MELEE); - StyleMap.put(ItemID.ENCHANTED_LYRE1, WeaponStyle.MELEE); - StyleMap.put(ItemID.ENCHANTED_LYRE2, WeaponStyle.MELEE); - StyleMap.put(ItemID.ENCHANTED_LYRE3, WeaponStyle.MELEE); - StyleMap.put(ItemID.ENCHANTED_LYRE4, WeaponStyle.MELEE); - StyleMap.put(ItemID.ENCHANTED_LYRE5, WeaponStyle.MELEE); - StyleMap.put(ItemID.EVENT_RPG, WeaponStyle.MELEE); - StyleMap.put(ItemID.EXCALIBUR, WeaponStyle.MELEE); - StyleMap.put(ItemID.EXCALIBUR_8280, WeaponStyle.MELEE); - StyleMap.put(ItemID.FLAMTAER_HAMMER, WeaponStyle.MELEE); - StyleMap.put(ItemID.FREMENNIK_BLADE, WeaponStyle.MELEE); - StyleMap.put(ItemID.FROZEN_ABYSSAL_WHIP, WeaponStyle.MELEE); - StyleMap.put(ItemID.GADDERHAMMER, WeaponStyle.MELEE); - StyleMap.put(ItemID.GHRAZI_RAPIER, WeaponStyle.MELEE); - StyleMap.put(ItemID.GHRAZI_RAPIER_23628, WeaponStyle.MELEE); - StyleMap.put(ItemID.GILDED_2H_SWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.GILDED_HASTA, WeaponStyle.MELEE); - StyleMap.put(ItemID.GILDED_SCIMITAR, WeaponStyle.MELEE); - StyleMap.put(ItemID.GILDED_SPEAR, WeaponStyle.MELEE); - StyleMap.put(ItemID.GLOWING_DAGGER, WeaponStyle.MELEE); - StyleMap.put(ItemID.GOLDEN_TENCH, WeaponStyle.MELEE); - StyleMap.put(ItemID.GRANITE_HAMMER, WeaponStyle.MELEE); - StyleMap.put(ItemID.GRANITE_LONGSWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.GRANITE_MAUL, WeaponStyle.MELEE); - StyleMap.put(ItemID.GRANITE_MAUL_12848, WeaponStyle.MELEE); - StyleMap.put(ItemID.GRANITE_MAUL_20557, WeaponStyle.MELEE); - StyleMap.put(ItemID.GRANITE_MAUL_24225, WeaponStyle.MELEE); - StyleMap.put(ItemID.GRANITE_MAUL_24227, WeaponStyle.MELEE); - StyleMap.put(ItemID.GREEN_BANNER, WeaponStyle.MELEE); - StyleMap.put(ItemID.GUTHANS_WARSPEAR, WeaponStyle.MELEE); - StyleMap.put(ItemID.GUTHANS_WARSPEAR_0, WeaponStyle.MELEE); - StyleMap.put(ItemID.GUTHANS_WARSPEAR_100, WeaponStyle.MELEE); - StyleMap.put(ItemID.GUTHANS_WARSPEAR_25, WeaponStyle.MELEE); - StyleMap.put(ItemID.GUTHANS_WARSPEAR_50, WeaponStyle.MELEE); - StyleMap.put(ItemID.GUTHANS_WARSPEAR_75, WeaponStyle.MELEE); - StyleMap.put(ItemID.HALBERD, WeaponStyle.MELEE); - StyleMap.put(ItemID.HARRYS_CUTLASS, WeaponStyle.MELEE); - StyleMap.put(ItemID.HAM_JOINT, WeaponStyle.MELEE); - StyleMap.put(ItemID.HAND_FAN, WeaponStyle.MELEE); - StyleMap.put(ItemID.HILL_GIANT_CLUB, WeaponStyle.MELEE); - StyleMap.put(ItemID.HOLY_HANDEGG, WeaponStyle.MELEE); - StyleMap.put(ItemID.HOSIDIUS_BANNER, WeaponStyle.MELEE); - StyleMap.put(ItemID.INFERNAL_AXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.INFERNAL_AXE_UNCHARGED, WeaponStyle.MELEE); - StyleMap.put(ItemID.INFERNAL_HARPOON, WeaponStyle.MELEE); - StyleMap.put(ItemID.INFERNAL_HARPOON_UNCHARGED, WeaponStyle.MELEE); - StyleMap.put(ItemID.INFERNAL_PICKAXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.INFERNAL_PICKAXE_UNCHARGED, WeaponStyle.MELEE); - StyleMap.put(ItemID.INQUISITORS_MACE, WeaponStyle.MELEE); - StyleMap.put(ItemID.IRON_2H_SWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.IRON_AXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.IRON_BATTLEAXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.IRON_CLAWS, WeaponStyle.MELEE); - StyleMap.put(ItemID.IRON_DAGGER, WeaponStyle.MELEE); - StyleMap.put(ItemID.IRON_DAGGERP, WeaponStyle.MELEE); - StyleMap.put(ItemID.IRON_DAGGERP_5668, WeaponStyle.MELEE); - StyleMap.put(ItemID.IRON_DAGGERP_5686, WeaponStyle.MELEE); - StyleMap.put(ItemID.IRON_HALBERD, WeaponStyle.MELEE); - StyleMap.put(ItemID.IRON_HASTA, WeaponStyle.MELEE); - StyleMap.put(ItemID.IRON_LONGSWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.IRON_MACE, WeaponStyle.MELEE); - StyleMap.put(ItemID.IRON_PICKAXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.IRON_SCIMITAR, WeaponStyle.MELEE); - StyleMap.put(ItemID.IRON_SPEAR, WeaponStyle.MELEE); - StyleMap.put(ItemID.IRON_SPEARP, WeaponStyle.MELEE); - StyleMap.put(ItemID.IRON_SPEARP_5706, WeaponStyle.MELEE); - StyleMap.put(ItemID.IRON_SPEARP_5720, WeaponStyle.MELEE); - StyleMap.put(ItemID.IRON_SWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.IRON_WARHAMMER, WeaponStyle.MELEE); - StyleMap.put(ItemID.JADE_MACHETE, WeaponStyle.MELEE); - StyleMap.put(ItemID.KATANA, WeaponStyle.MELEE); - StyleMap.put(ItemID.KITCHEN_KNIFE, WeaponStyle.MELEE); - StyleMap.put(ItemID.KERIS, WeaponStyle.MELEE); - StyleMap.put(ItemID.LARGE_SPADE, WeaponStyle.MELEE); - StyleMap.put(ItemID.LEAFBLADED_BATTLEAXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.LEAFBLADED_SPEAR, WeaponStyle.MELEE); - StyleMap.put(ItemID.LEAFBLADED_SPEAR_4159, WeaponStyle.MELEE); - StyleMap.put(ItemID.LEAFBLADED_SWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.LOVAKENGJ_BANNER, WeaponStyle.MELEE); - StyleMap.put(ItemID.LUCKY_CUTLASS, WeaponStyle.MELEE); - StyleMap.put(ItemID.LYRE, WeaponStyle.MELEE); - StyleMap.put(ItemID.MACE, WeaponStyle.MELEE); - StyleMap.put(ItemID.MACHETE, WeaponStyle.MELEE); - StyleMap.put(ItemID.MAGIC_BUTTERFLY_NET, WeaponStyle.MELEE); - StyleMap.put(ItemID.MAGIC_SECATEURS, WeaponStyle.MELEE); - StyleMap.put(ItemID.MAGIC_SECATEURS_NZ, WeaponStyle.MELEE); - StyleMap.put(ItemID.MAPLE_BLACKJACK, WeaponStyle.MELEE); - StyleMap.put(ItemID.MAPLE_BLACKJACKD, WeaponStyle.MELEE); - StyleMap.put(ItemID.MAPLE_BLACKJACKO, WeaponStyle.MELEE); - StyleMap.put(ItemID.MEAT_TENDERISER, WeaponStyle.MELEE); - StyleMap.put(ItemID.MERFOLK_TRIDENT, WeaponStyle.MELEE); - StyleMap.put(ItemID.MITHRIL_2H_SWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.MITHRIL_AXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.MITHRIL_BATTLEAXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.MITHRIL_CLAWS, WeaponStyle.MELEE); - StyleMap.put(ItemID.MITHRIL_DAGGER, WeaponStyle.MELEE); - StyleMap.put(ItemID.MITHRIL_DAGGERP, WeaponStyle.MELEE); - StyleMap.put(ItemID.MITHRIL_DAGGERP_5674, WeaponStyle.MELEE); - StyleMap.put(ItemID.MITHRIL_DAGGERP_5692, WeaponStyle.MELEE); - StyleMap.put(ItemID.MITHRIL_HALBERD, WeaponStyle.MELEE); - StyleMap.put(ItemID.MITHRIL_HASTA, WeaponStyle.MELEE); - StyleMap.put(ItemID.MITHRIL_LONGSWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.MITHRIL_MACE, WeaponStyle.MELEE); - StyleMap.put(ItemID.MITHRIL_PICKAXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.MITHRIL_SCIMITAR, WeaponStyle.MELEE); - StyleMap.put(ItemID.MITHRIL_SPEAR, WeaponStyle.MELEE); - StyleMap.put(ItemID.MITHRIL_SPEARP, WeaponStyle.MELEE); - StyleMap.put(ItemID.MITHRIL_SPEARP_5710, WeaponStyle.MELEE); - StyleMap.put(ItemID.MITHRIL_SPEARP_5724, WeaponStyle.MELEE); - StyleMap.put(ItemID.MITHRIL_SWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.MITHRIL_WARHAMMER, WeaponStyle.MELEE); - StyleMap.put(ItemID.MIXED_FLOWERS, WeaponStyle.MELEE); - StyleMap.put(ItemID.MOUSE_TOY, WeaponStyle.MELEE); - StyleMap.put(ItemID.NEW_CRYSTAL_HALBERD_FULL, WeaponStyle.MELEE); - StyleMap.put(ItemID.NEW_CRYSTAL_HALBERD_FULL_I, WeaponStyle.MELEE); - StyleMap.put(ItemID.NEW_CRYSTAL_HALBERD_FULL_16893, WeaponStyle.MELEE); - StyleMap.put(ItemID.NEW_CRYSTAL_HALBERD_FULL_I_16892, WeaponStyle.MELEE); - StyleMap.put(ItemID.NOOSE_WAND, WeaponStyle.MELEE); - StyleMap.put(ItemID.NUNCHAKU, WeaponStyle.MELEE); - StyleMap.put(ItemID.OAK_BLACKJACK, WeaponStyle.MELEE); - StyleMap.put(ItemID.OAK_BLACKJACKD, WeaponStyle.MELEE); - StyleMap.put(ItemID.OAK_BLACKJACKO, WeaponStyle.MELEE); - StyleMap.put(ItemID.OILY_FISHING_ROD, WeaponStyle.MELEE); - StyleMap.put(ItemID.OILY_PEARL_FISHING_ROD, WeaponStyle.MELEE); - StyleMap.put(ItemID.OPAL_MACHETE, WeaponStyle.MELEE); - StyleMap.put(ItemID.ORANGE_FLOWERS, WeaponStyle.MELEE); - StyleMap.put(ItemID.ORANGE_SALAMANDER, WeaponStyle.MELEE); - StyleMap.put(ItemID.PEACEFUL_HANDEGG, WeaponStyle.MELEE); - StyleMap.put(ItemID.PET_ROCK, WeaponStyle.MELEE); - StyleMap.put(ItemID.PISCARILIUS_BANNER, WeaponStyle.MELEE); - StyleMap.put(ItemID.PROP_SWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.PURPLE_FLOWERS, WeaponStyle.MELEE); - StyleMap.put(ItemID.RAPIER, WeaponStyle.MELEE); - StyleMap.put(ItemID.RAT_POLE, WeaponStyle.MELEE); - StyleMap.put(ItemID.RAT_POLE_6774, WeaponStyle.MELEE); - StyleMap.put(ItemID.RAT_POLE_6775, WeaponStyle.MELEE); - StyleMap.put(ItemID.RAT_POLE_6776, WeaponStyle.MELEE); - StyleMap.put(ItemID.RAT_POLE_6777, WeaponStyle.MELEE); - StyleMap.put(ItemID.RAT_POLE_6778, WeaponStyle.MELEE); - StyleMap.put(ItemID.RAT_POLE_6779, WeaponStyle.MELEE); - StyleMap.put(ItemID.RED_FLOWERS, WeaponStyle.MELEE); - StyleMap.put(ItemID.RED_FLOWERS_8938, WeaponStyle.MELEE); - StyleMap.put(ItemID.RED_SALAMANDER, WeaponStyle.MELEE); - StyleMap.put(ItemID.RED_TOPAZ_MACHETE, WeaponStyle.MELEE); - StyleMap.put(ItemID.ROCK_HAMMER, WeaponStyle.MELEE); - StyleMap.put(ItemID.ROYAL_SCEPTRE, WeaponStyle.MELEE); - StyleMap.put(ItemID.RUBBER_CHICKEN, WeaponStyle.MELEE); - StyleMap.put(ItemID.RUBBER_CHICKEN_22666, WeaponStyle.MELEE); - StyleMap.put(ItemID.RUNE_2H_SWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.RUNE_AXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.RUNE_BATTLEAXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.RUNE_BATTLEAXE_20552, WeaponStyle.MELEE); - StyleMap.put(ItemID.RUNE_CANE, WeaponStyle.MELEE); - StyleMap.put(ItemID.RUNE_CLAWS, WeaponStyle.MELEE); - StyleMap.put(ItemID.RUNE_DAGGER, WeaponStyle.MELEE); - StyleMap.put(ItemID.RUNE_DAGGERP, WeaponStyle.MELEE); - StyleMap.put(ItemID.RUNE_DAGGERP_5678, WeaponStyle.MELEE); - StyleMap.put(ItemID.RUNE_DAGGERP_5696, WeaponStyle.MELEE); - StyleMap.put(ItemID.RUNE_HALBERD, WeaponStyle.MELEE); - StyleMap.put(ItemID.RUNE_HASTA, WeaponStyle.MELEE); - StyleMap.put(ItemID.RUNE_LONGSWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.RUNE_MACE, WeaponStyle.MELEE); - StyleMap.put(ItemID.RUNE_PICKAXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.RUNE_SCIMITAR, WeaponStyle.MELEE); - StyleMap.put(ItemID.RUNE_SCIMITAR_20402, WeaponStyle.MELEE); - StyleMap.put(ItemID.RUNE_SCIMITAR_23330, WeaponStyle.MELEE); - StyleMap.put(ItemID.RUNE_SCIMITAR_23332, WeaponStyle.MELEE); - StyleMap.put(ItemID.RUNE_SCIMITAR_23334, WeaponStyle.MELEE); - StyleMap.put(ItemID.RUNE_SPEAR, WeaponStyle.MELEE); - StyleMap.put(ItemID.RUNE_SPEARP, WeaponStyle.MELEE); - StyleMap.put(ItemID.RUNE_SPEARP_5714, WeaponStyle.MELEE); - StyleMap.put(ItemID.RUNE_SPEARP_5728, WeaponStyle.MELEE); - StyleMap.put(ItemID.RUNE_SWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.RUNE_WARHAMMER, WeaponStyle.MELEE); - StyleMap.put(ItemID.SARADOMINS_BLESSED_SWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.SARADOMIN_GODSWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.SARADOMIN_GODSWORD_OR, WeaponStyle.MELEE); - StyleMap.put(ItemID.SARADOMIN_MJOLNIR, WeaponStyle.MELEE); - StyleMap.put(ItemID.SARADOMIN_SWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.SARAS_BLESSED_SWORD_FULL, WeaponStyle.MELEE); - StyleMap.put(ItemID.SCYTHE, WeaponStyle.MELEE); - StyleMap.put(ItemID.SCYTHE_OF_VITUR, WeaponStyle.MELEE); - StyleMap.put(ItemID.SCYTHE_OF_VITUR_22664, WeaponStyle.MELEE); - StyleMap.put(ItemID.SCYTHE_OF_VITUR_UNCHARGED, WeaponStyle.MELEE); - StyleMap.put(ItemID.SEVERED_LEG_24792, WeaponStyle.MELEE); - StyleMap.put(ItemID.SHADOW_SWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.SHAYZIEN_BANNER, WeaponStyle.MELEE); - StyleMap.put(ItemID.SILVERLIGHT, WeaponStyle.MELEE); - StyleMap.put(ItemID.SILVERLIGHT_6745, WeaponStyle.MELEE); - StyleMap.put(ItemID.SILVERLIGHT_8279, WeaponStyle.MELEE); - StyleMap.put(ItemID.SILVER_SICKLE, WeaponStyle.MELEE); - StyleMap.put(ItemID.SILVER_SICKLE_B, WeaponStyle.MELEE); - StyleMap.put(ItemID.SNOWBALL, WeaponStyle.MELEE); - StyleMap.put(ItemID.SPEAR, WeaponStyle.MELEE); - StyleMap.put(ItemID.STALE_BAGUETTE, WeaponStyle.MELEE); - StyleMap.put(ItemID.STATIUSS_WARHAMMER, WeaponStyle.MELEE); - StyleMap.put(ItemID.STATIUSS_WARHAMMER_23620, WeaponStyle.MELEE); - StyleMap.put(ItemID.STEEL_2H_SWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.STEEL_AXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.STEEL_BATTLEAXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.STEEL_CLAWS, WeaponStyle.MELEE); - StyleMap.put(ItemID.STEEL_DAGGER, WeaponStyle.MELEE); - StyleMap.put(ItemID.STEEL_DAGGERP, WeaponStyle.MELEE); - StyleMap.put(ItemID.STEEL_DAGGERP_5672, WeaponStyle.MELEE); - StyleMap.put(ItemID.STEEL_DAGGERP_5690, WeaponStyle.MELEE); - StyleMap.put(ItemID.STEEL_HALBERD, WeaponStyle.MELEE); - StyleMap.put(ItemID.STEEL_HASTA, WeaponStyle.MELEE); - StyleMap.put(ItemID.STEEL_LONGSWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.STEEL_PICKAXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.STEEL_SCIMITAR, WeaponStyle.MELEE); - StyleMap.put(ItemID.STEEL_SPEAR, WeaponStyle.MELEE); - StyleMap.put(ItemID.STEEL_SPEARP, WeaponStyle.MELEE); - StyleMap.put(ItemID.STEEL_SPEARP_5708, WeaponStyle.MELEE); - StyleMap.put(ItemID.STEEL_SPEARP_5722, WeaponStyle.MELEE); - StyleMap.put(ItemID.STEEL_SWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.STEEL_WARHAMMER, WeaponStyle.MELEE); - StyleMap.put(ItemID.STONE_BOWL, WeaponStyle.MELEE); - StyleMap.put(ItemID.SWAMP_LIZARD, WeaponStyle.MELEE); - StyleMap.put(ItemID.SWIFT_BLADE, WeaponStyle.MELEE); - StyleMap.put(ItemID.TOKTZXILAK, WeaponStyle.MELEE); - StyleMap.put(ItemID.TOKTZXILAK_20554, WeaponStyle.MELEE); - StyleMap.put(ItemID.TOKTZXILEK, WeaponStyle.MELEE); - StyleMap.put(ItemID.TORAGS_HAMMERS, WeaponStyle.MELEE); - StyleMap.put(ItemID.TORAGS_HAMMERS_0, WeaponStyle.MELEE); - StyleMap.put(ItemID.TORAGS_HAMMERS_100, WeaponStyle.MELEE); - StyleMap.put(ItemID.TORAGS_HAMMERS_25, WeaponStyle.MELEE); - StyleMap.put(ItemID.TORAGS_HAMMERS_50, WeaponStyle.MELEE); - StyleMap.put(ItemID.TORAGS_HAMMERS_75, WeaponStyle.MELEE); - StyleMap.put(ItemID.TRAINING_SWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.TRAILBLAZER_AXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.TRAILBLAZER_BANNER, WeaponStyle.MELEE); - StyleMap.put(ItemID.TRAILBLAZER_CANE, WeaponStyle.MELEE); - StyleMap.put(ItemID.TRAILBLAZER_HARPOON, WeaponStyle.MELEE); - StyleMap.put(ItemID.TRAILBLAZER_PICKAXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.TROLLWEISS, WeaponStyle.MELEE); - StyleMap.put(ItemID.TWISTED_BANNER, WeaponStyle.MELEE); - StyleMap.put(ItemID.TZHAARKETEM, WeaponStyle.MELEE); - StyleMap.put(ItemID.TZHAARKETOM, WeaponStyle.MELEE); - StyleMap.put(ItemID.TZHAARKETOM_T, WeaponStyle.MELEE); - StyleMap.put(ItemID.VERACS_FLAIL, WeaponStyle.MELEE); - StyleMap.put(ItemID.VERACS_FLAIL_0, WeaponStyle.MELEE); - StyleMap.put(ItemID.VERACS_FLAIL_100, WeaponStyle.MELEE); - StyleMap.put(ItemID.VERACS_FLAIL_25, WeaponStyle.MELEE); - StyleMap.put(ItemID.VERACS_FLAIL_50, WeaponStyle.MELEE); - StyleMap.put(ItemID.VERACS_FLAIL_75, WeaponStyle.MELEE); - StyleMap.put(ItemID.VESTAS_BLIGHTED_LONGSWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.VESTAS_LONGSWORD_INACTIVE, WeaponStyle.MELEE); - StyleMap.put(ItemID.VESTAS_LONGSWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.VESTAS_LONGSWORD_23615, WeaponStyle.MELEE); - StyleMap.put(ItemID.VESTAS_SPEAR, WeaponStyle.MELEE); - StyleMap.put(ItemID.VIGGORAS_CHAINMACE, WeaponStyle.MELEE); - StyleMap.put(ItemID.VIGGORAS_CHAINMACE_U, WeaponStyle.MELEE); - StyleMap.put(ItemID.VOLCANIC_ABYSSAL_WHIP, WeaponStyle.MELEE); - StyleMap.put(ItemID.WESTERN_BANNER_1, WeaponStyle.MELEE); - StyleMap.put(ItemID.WESTERN_BANNER_2, WeaponStyle.MELEE); - StyleMap.put(ItemID.WESTERN_BANNER_3, WeaponStyle.MELEE); - StyleMap.put(ItemID.WESTERN_BANNER_4, WeaponStyle.MELEE); - StyleMap.put(ItemID.WHITE_2H_SWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.WHITE_BATTLEAXE, WeaponStyle.MELEE); - StyleMap.put(ItemID.WHITE_CLAWS, WeaponStyle.MELEE); - StyleMap.put(ItemID.WHITE_DAGGER, WeaponStyle.MELEE); - StyleMap.put(ItemID.WHITE_DAGGERP, WeaponStyle.MELEE); - StyleMap.put(ItemID.WHITE_DAGGERP_6595, WeaponStyle.MELEE); - StyleMap.put(ItemID.WHITE_DAGGERP_6597, WeaponStyle.MELEE); - StyleMap.put(ItemID.WHITE_FLOWERS, WeaponStyle.MELEE); - StyleMap.put(ItemID.WHITE_HALBERD, WeaponStyle.MELEE); - StyleMap.put(ItemID.WHITE_LONGSWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.WHITE_MACE, WeaponStyle.MELEE); - StyleMap.put(ItemID.WHITE_SCIMITAR, WeaponStyle.MELEE); - StyleMap.put(ItemID.WHITE_SWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.WHITE_WARHAMMER, WeaponStyle.MELEE); - StyleMap.put(ItemID.WILDERNESS_SWORD_1, WeaponStyle.MELEE); - StyleMap.put(ItemID.WILDERNESS_SWORD_2, WeaponStyle.MELEE); - StyleMap.put(ItemID.WILDERNESS_SWORD_3, WeaponStyle.MELEE); - StyleMap.put(ItemID.WILDERNESS_SWORD_4, WeaponStyle.MELEE); - StyleMap.put(ItemID.WILLOW_BLACKJACK, WeaponStyle.MELEE); - StyleMap.put(ItemID.WILLOW_BLACKJACKD, WeaponStyle.MELEE); - StyleMap.put(ItemID.WILLOW_BLACKJACKO, WeaponStyle.MELEE); - StyleMap.put(ItemID.WOLFBANE, WeaponStyle.MELEE); - StyleMap.put(ItemID.WOODEN_SPOON, WeaponStyle.MELEE); - StyleMap.put(ItemID.WOODEN_SWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.YELLOW_FLOWERS, WeaponStyle.MELEE); - StyleMap.put(ItemID.ZAMORAKIAN_HASTA, WeaponStyle.MELEE); - StyleMap.put(ItemID.ZAMORAKIAN_SPEAR, WeaponStyle.MELEE); - StyleMap.put(ItemID.ZAMORAK_GODSWORD, WeaponStyle.MELEE); - StyleMap.put(ItemID.ZAMORAK_GODSWORD_OR, WeaponStyle.MELEE); - StyleMap.put(ItemID.ZOMBIE_HEAD, WeaponStyle.MELEE); - - //Ranged - StyleMap.put(ItemID._3RD_AGE_BOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.ADAMANT_CROSSBOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.ADAMANT_DART, WeaponStyle.RANGE); - StyleMap.put(ItemID.ADAMANT_DARTP, WeaponStyle.RANGE); - StyleMap.put(ItemID.ADAMANT_DARTP_5633, WeaponStyle.RANGE); - StyleMap.put(ItemID.ADAMANT_DARTP_5640, WeaponStyle.RANGE); - StyleMap.put(ItemID.ADAMANT_KNIFE, WeaponStyle.RANGE); - StyleMap.put(ItemID.ADAMANT_KNIFEP, WeaponStyle.RANGE); - StyleMap.put(ItemID.ADAMANT_KNIFEP_5659, WeaponStyle.RANGE); - StyleMap.put(ItemID.ADAMANT_KNIFEP_5666, WeaponStyle.RANGE); - StyleMap.put(ItemID.ADAMANT_THROWNAXE, WeaponStyle.RANGE); - StyleMap.put(ItemID.ARMADYL_CROSSBOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.ARMADYL_CROSSBOW_23611, WeaponStyle.RANGE); - StyleMap.put(ItemID.BLACK_CHINCHOMPA, WeaponStyle.RANGE); - StyleMap.put(ItemID.BLACK_DART, WeaponStyle.RANGE); - StyleMap.put(ItemID.BLACK_DARTP, WeaponStyle.RANGE); - StyleMap.put(ItemID.BLACK_DARTP_5631, WeaponStyle.RANGE); - StyleMap.put(ItemID.BLACK_DARTP_5638, WeaponStyle.RANGE); - StyleMap.put(ItemID.BLACK_KNIFE, WeaponStyle.RANGE); - StyleMap.put(ItemID.BLACK_KNIFEP, WeaponStyle.RANGE); - StyleMap.put(ItemID.BLACK_KNIFEP_5658, WeaponStyle.RANGE); - StyleMap.put(ItemID.BLACK_KNIFEP_5665, WeaponStyle.RANGE); - StyleMap.put(ItemID.BLURITE_CROSSBOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.BRONZE_CROSSBOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.BRONZE_DART, WeaponStyle.RANGE); - StyleMap.put(ItemID.BRONZE_DARTP, WeaponStyle.RANGE); - StyleMap.put(ItemID.BRONZE_DARTP_5628, WeaponStyle.RANGE); - StyleMap.put(ItemID.BRONZE_DARTP_5635, WeaponStyle.RANGE); - StyleMap.put(ItemID.BRONZE_KNIFE, WeaponStyle.RANGE); - StyleMap.put(ItemID.BRONZE_KNIFEP, WeaponStyle.RANGE); - StyleMap.put(ItemID.BRONZE_KNIFEP_5654, WeaponStyle.RANGE); - StyleMap.put(ItemID.BRONZE_KNIFEP_5661, WeaponStyle.RANGE); - StyleMap.put(ItemID.BRONZE_THROWNAXE, WeaponStyle.RANGE); - StyleMap.put(ItemID.CHINCHOMPA, WeaponStyle.RANGE); - StyleMap.put(ItemID.CHINCHOMPA_10033, WeaponStyle.RANGE); - StyleMap.put(ItemID.COMP_OGRE_BOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.CORRUPTED_BOW_ATTUNED, WeaponStyle.RANGE); - StyleMap.put(ItemID.CORRUPTED_BOW_BASIC, WeaponStyle.RANGE); - StyleMap.put(ItemID.CORRUPTED_BOW_PERFECTED, WeaponStyle.RANGE); - StyleMap.put(ItemID.CRAWS_BOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.CRAWS_BOW_U, WeaponStyle.RANGE); - StyleMap.put(ItemID.CROSSBOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.CRYSTAL_BOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.CRYSTAL_BOW_110, WeaponStyle.RANGE); - StyleMap.put(ItemID.CRYSTAL_BOW_110_I, WeaponStyle.RANGE); - StyleMap.put(ItemID.CRYSTAL_BOW_210, WeaponStyle.RANGE); - StyleMap.put(ItemID.CRYSTAL_BOW_210_I, WeaponStyle.RANGE); - StyleMap.put(ItemID.CRYSTAL_BOW_310, WeaponStyle.RANGE); - StyleMap.put(ItemID.CRYSTAL_BOW_310_I, WeaponStyle.RANGE); - StyleMap.put(ItemID.CRYSTAL_BOW_410, WeaponStyle.RANGE); - StyleMap.put(ItemID.CRYSTAL_BOW_410_I, WeaponStyle.RANGE); - StyleMap.put(ItemID.CRYSTAL_BOW_510, WeaponStyle.RANGE); - StyleMap.put(ItemID.CRYSTAL_BOW_510_I, WeaponStyle.RANGE); - StyleMap.put(ItemID.CRYSTAL_BOW_610, WeaponStyle.RANGE); - StyleMap.put(ItemID.CRYSTAL_BOW_610_I, WeaponStyle.RANGE); - StyleMap.put(ItemID.CRYSTAL_BOW_710, WeaponStyle.RANGE); - StyleMap.put(ItemID.CRYSTAL_BOW_710_I, WeaponStyle.RANGE); - StyleMap.put(ItemID.CRYSTAL_BOW_810, WeaponStyle.RANGE); - StyleMap.put(ItemID.CRYSTAL_BOW_810_I, WeaponStyle.RANGE); - StyleMap.put(ItemID.CRYSTAL_BOW_910, WeaponStyle.RANGE); - StyleMap.put(ItemID.CRYSTAL_BOW_910_I, WeaponStyle.RANGE); - StyleMap.put(ItemID.CRYSTAL_BOW_24123, WeaponStyle.RANGE); - StyleMap.put(ItemID.CRYSTAL_BOW_ATTUNED, WeaponStyle.RANGE); - StyleMap.put(ItemID.CRYSTAL_BOW_BASIC, WeaponStyle.RANGE); - StyleMap.put(ItemID.CRYSTAL_BOW_FULL, WeaponStyle.RANGE); - StyleMap.put(ItemID.CRYSTAL_BOW_FULL_I, WeaponStyle.RANGE); - StyleMap.put(ItemID.CRYSTAL_BOW_INACTIVE, WeaponStyle.RANGE); - StyleMap.put(ItemID.CRYSTAL_BOW_PERFECTED, WeaponStyle.RANGE); - StyleMap.put(ItemID.CURSED_GOBLIN_BOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.DARK_BOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.DARK_BOW_12765, WeaponStyle.RANGE); - StyleMap.put(ItemID.DARK_BOW_12766, WeaponStyle.RANGE); - StyleMap.put(ItemID.DARK_BOW_12767, WeaponStyle.RANGE); - StyleMap.put(ItemID.DARK_BOW_12768, WeaponStyle.RANGE); - StyleMap.put(ItemID.DARK_BOW_20408, WeaponStyle.RANGE); - StyleMap.put(ItemID.DART, WeaponStyle.RANGE); - StyleMap.put(ItemID.DORGESHUUN_CROSSBOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.DRAGON_CROSSBOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.DRAGON_DART, WeaponStyle.RANGE); - StyleMap.put(ItemID.DRAGON_DARTP, WeaponStyle.RANGE); - StyleMap.put(ItemID.DRAGON_DARTP_11233, WeaponStyle.RANGE); - StyleMap.put(ItemID.DRAGON_DARTP_11234, WeaponStyle.RANGE); - StyleMap.put(ItemID.DRAGON_HUNTER_CROSSBOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.DRAGON_KNIFE, WeaponStyle.RANGE); - StyleMap.put(ItemID.DRAGON_KNIFEP, WeaponStyle.RANGE); - StyleMap.put(ItemID.DRAGON_KNIFEP_22808, WeaponStyle.RANGE); - StyleMap.put(ItemID.DRAGON_KNIFEP_22810, WeaponStyle.RANGE); - StyleMap.put(ItemID.DRAGON_THROWNAXE, WeaponStyle.RANGE); - StyleMap.put(ItemID.HEAVY_BALLISTA, WeaponStyle.RANGE); - StyleMap.put(ItemID.HEAVY_BALLISTA_23630, WeaponStyle.RANGE); - StyleMap.put(ItemID.HOLY_WATER, WeaponStyle.RANGE); - StyleMap.put(ItemID.HUNTERS_CROSSBOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.IRON_CROSSBOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.IRON_DART, WeaponStyle.RANGE); - StyleMap.put(ItemID.IRON_DARTP, WeaponStyle.RANGE); - StyleMap.put(ItemID.IRON_DARTP_5629, WeaponStyle.RANGE); - StyleMap.put(ItemID.IRON_DARTP_5636, WeaponStyle.RANGE); - StyleMap.put(ItemID.IRON_KNIFE, WeaponStyle.RANGE); - StyleMap.put(ItemID.IRON_KNIFEP, WeaponStyle.RANGE); - StyleMap.put(ItemID.IRON_KNIFEP_5655, WeaponStyle.RANGE); - StyleMap.put(ItemID.IRON_KNIFEP_5662, WeaponStyle.RANGE); - StyleMap.put(ItemID.IRON_THROWNAXE, WeaponStyle.RANGE); - StyleMap.put(ItemID.KARILS_CROSSBOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.KARILS_CROSSBOW_0, WeaponStyle.RANGE); - StyleMap.put(ItemID.KARILS_CROSSBOW_100, WeaponStyle.RANGE); - StyleMap.put(ItemID.KARILS_CROSSBOW_25, WeaponStyle.RANGE); - StyleMap.put(ItemID.KARILS_CROSSBOW_50, WeaponStyle.RANGE); - StyleMap.put(ItemID.KARILS_CROSSBOW_75, WeaponStyle.RANGE); - StyleMap.put(ItemID.LIGHT_BALLISTA, WeaponStyle.RANGE); - StyleMap.put(ItemID.LONGBOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.MAGIC_COMP_BOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.MAGIC_LONGBOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.MAGIC_SHORTBOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.MAGIC_SHORTBOW_20558, WeaponStyle.RANGE); - StyleMap.put(ItemID.MAGIC_SHORTBOW_I, WeaponStyle.RANGE); - StyleMap.put(ItemID.MAPLE_LONGBOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.MAPLE_SHORTBOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.MITHRIL_DART, WeaponStyle.RANGE); - StyleMap.put(ItemID.MITHRIL_DARTP, WeaponStyle.RANGE); - StyleMap.put(ItemID.MITHRIL_DARTP_5632, WeaponStyle.RANGE); - StyleMap.put(ItemID.MITHRIL_DARTP_5639, WeaponStyle.RANGE); - StyleMap.put(ItemID.MITHRIL_KNIFE, WeaponStyle.RANGE); - StyleMap.put(ItemID.MITHRIL_KNIFEP, WeaponStyle.RANGE); - StyleMap.put(ItemID.MITHRIL_KNIFEP_5657, WeaponStyle.RANGE); - StyleMap.put(ItemID.MITHRIL_KNIFEP_5664, WeaponStyle.RANGE); - StyleMap.put(ItemID.MITHRIL_THROWNAXE, WeaponStyle.RANGE); - StyleMap.put(ItemID.MITHRIL_CROSSBOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.MONKEY_TALISMAN, WeaponStyle.RANGE); - StyleMap.put(ItemID.MORRIGANS_JAVELIN, WeaponStyle.RANGE); - StyleMap.put(ItemID.MORRIGANS_JAVELIN_23619, WeaponStyle.RANGE); - StyleMap.put(ItemID.MORRIGANS_THROWING_AXE, WeaponStyle.RANGE); - StyleMap.put(ItemID.MUD_PIE, WeaponStyle.RANGE); - StyleMap.put(ItemID.NEW_CRYSTAL_BOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.NEW_CRYSTAL_BOW_4213, WeaponStyle.RANGE); - StyleMap.put(ItemID.NEW_CRYSTAL_BOW_16888, WeaponStyle.RANGE); - StyleMap.put(ItemID.NEW_CRYSTAL_BOW_I, WeaponStyle.RANGE); - StyleMap.put(ItemID.NEW_CRYSTAL_BOW_I_16889, WeaponStyle.RANGE); - StyleMap.put(ItemID.OAK_LONGBOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.OAK_SHORTBOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.OGRE_BOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.PHOENIX_CROSSBOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.RED_CHINCHOMPA, WeaponStyle.RANGE); - StyleMap.put(ItemID.RED_CHINCHOMPA_10034, WeaponStyle.RANGE); - StyleMap.put(ItemID.RUNE_CROSSBOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.RUNE_CROSSBOW_23601, WeaponStyle.RANGE); - StyleMap.put(ItemID.RUNE_DART, WeaponStyle.RANGE); - StyleMap.put(ItemID.RUNE_DARTP, WeaponStyle.RANGE); - StyleMap.put(ItemID.RUNE_DARTP_5634, WeaponStyle.RANGE); - StyleMap.put(ItemID.RUNE_DARTP_5641, WeaponStyle.RANGE); - StyleMap.put(ItemID.RUNE_KNIFE, WeaponStyle.RANGE); - StyleMap.put(ItemID.RUNE_KNIFEP, WeaponStyle.RANGE); - StyleMap.put(ItemID.RUNE_KNIFEP_5660, WeaponStyle.RANGE); - StyleMap.put(ItemID.RUNE_KNIFEP_5667, WeaponStyle.RANGE); - StyleMap.put(ItemID.RUNE_THROWNAXE, WeaponStyle.RANGE); - StyleMap.put(ItemID.SEERCULL, WeaponStyle.RANGE); - StyleMap.put(ItemID.SHORTBOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.SIGNED_OAK_BOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.STARTER_BOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.STEEL_CROSSBOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.STEEL_DART, WeaponStyle.RANGE); - StyleMap.put(ItemID.STEEL_DARTP, WeaponStyle.RANGE); - StyleMap.put(ItemID.STEEL_DARTP_5630, WeaponStyle.RANGE); - StyleMap.put(ItemID.STEEL_DARTP_5637, WeaponStyle.RANGE); - StyleMap.put(ItemID.STEEL_KNIFE, WeaponStyle.RANGE); - StyleMap.put(ItemID.STEEL_KNIFEP, WeaponStyle.RANGE); - StyleMap.put(ItemID.STEEL_KNIFEP_5656, WeaponStyle.RANGE); - StyleMap.put(ItemID.STEEL_KNIFEP_5663, WeaponStyle.RANGE); - StyleMap.put(ItemID.STEEL_THROWNAXE, WeaponStyle.RANGE); - StyleMap.put(ItemID.TOKTZXILUL, WeaponStyle.RANGE); - StyleMap.put(ItemID.TOXIC_BLOWPIPE, WeaponStyle.RANGE); - StyleMap.put(ItemID.TOXIC_BLOWPIPE_EMPTY, WeaponStyle.RANGE); - StyleMap.put(ItemID.TRAINING_BOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.TWISTED_BOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.WILLOW_COMP_BOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.WILLOW_LONGBOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.WILLOW_SHORTBOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.YEW_COMP_BOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.YEW_LONGBOW, WeaponStyle.RANGE); - StyleMap.put(ItemID.YEW_SHORTBOW, WeaponStyle.RANGE); - - //Magic - StyleMap.put(ItemID._3RD_AGE_WAND, WeaponStyle.MAGIC); - StyleMap.put(ItemID.AHRIMS_STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.AHRIMS_STAFF_0, WeaponStyle.MAGIC); - StyleMap.put(ItemID.AHRIMS_STAFF_100, WeaponStyle.MAGIC); - StyleMap.put(ItemID.AHRIMS_STAFF_25, WeaponStyle.MAGIC); - StyleMap.put(ItemID.AHRIMS_STAFF_50, WeaponStyle.MAGIC); - StyleMap.put(ItemID.AHRIMS_STAFF_75, WeaponStyle.MAGIC); - StyleMap.put(ItemID.AHRIMS_STAFF_23653, WeaponStyle.MAGIC); - StyleMap.put(ItemID.AIR_BATTLESTAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.ANCIENT_CROZIER, WeaponStyle.MAGIC); - StyleMap.put(ItemID.ANCIENT_STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.APPRENTICE_WAND, WeaponStyle.MAGIC); - StyleMap.put(ItemID.ARMADYL_CROZIER, WeaponStyle.MAGIC); - StyleMap.put(ItemID.BANDOS_CROZIER, WeaponStyle.MAGIC); - StyleMap.put(ItemID.BATTLESTAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.BEGINNER_WAND, WeaponStyle.MAGIC); - StyleMap.put(ItemID.BLISTERWOOD_FLAIL, WeaponStyle.MAGIC); - StyleMap.put(ItemID.BROKEN_STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.BRYOPHYTAS_STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.BRYOPHYTAS_STAFF_UNCHARGED, WeaponStyle.MAGIC); - StyleMap.put(ItemID.CORRUPTED_STAFF_ATTUNED, WeaponStyle.MAGIC); - StyleMap.put(ItemID.CORRUPTED_STAFF_BASIC, WeaponStyle.MAGIC); - StyleMap.put(ItemID.CORRUPTED_STAFF_PERFECTED, WeaponStyle.MAGIC); - StyleMap.put(ItemID.CRYSTAL_STAFF_ATTUNED, WeaponStyle.MAGIC); - StyleMap.put(ItemID.CRYSTAL_STAFF_BASIC, WeaponStyle.MAGIC); - StyleMap.put(ItemID.CRYSTAL_STAFF_PERFECTED, WeaponStyle.MAGIC); - StyleMap.put(ItemID.CURSED_GOBLIN_STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.DAWNBRINGER, WeaponStyle.MAGIC); - StyleMap.put(ItemID.DRAMEN_STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.DUST_BATTLESTAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.EARTH_BATTLESTAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.ELDRITCH_NIGHTMARE_STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.FIRE_BATTLESTAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.GUTHIX_CROZIER, WeaponStyle.MAGIC); - StyleMap.put(ItemID.GUTHIX_STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.HARMONISED_NIGHTMARE_STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.IBANS_STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.IBANS_STAFF_1410, WeaponStyle.MAGIC); - StyleMap.put(ItemID.IBANS_STAFF_U, WeaponStyle.MAGIC); - StyleMap.put(ItemID.IVANDIS_FLAIL, WeaponStyle.MAGIC); - StyleMap.put(ItemID.KODAI_WAND, WeaponStyle.MAGIC); - StyleMap.put(ItemID.KODAI_WAND_23626, WeaponStyle.MAGIC); - StyleMap.put(ItemID.LAVA_BATTLESTAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.LAVA_BATTLESTAFF_21198, WeaponStyle.MAGIC); - StyleMap.put(ItemID.LUNAR_STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.MAGIC_STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.MASTER_WAND, WeaponStyle.MAGIC); - StyleMap.put(ItemID.MASTER_WAND_20560, WeaponStyle.MAGIC); - StyleMap.put(ItemID.MIST_BATTLESTAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.MUD_BATTLESTAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.MYSTIC_AIR_STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.MYSTIC_DUST_STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.MYSTIC_EARTH_STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.MYSTIC_FIRE_STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.MYSTIC_LAVA_STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.MYSTIC_LAVA_STAFF_21200, WeaponStyle.MAGIC); - StyleMap.put(ItemID.MYSTIC_MIST_STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.MYSTIC_MUD_STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.MYSTIC_SMOKE_STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.MYSTIC_STEAM_STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.MYSTIC_STEAM_STAFF_12796, WeaponStyle.MAGIC); - StyleMap.put(ItemID.MYSTIC_WATER_STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.NIGHTMARE_STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.PHARAOHS_SCEPTRE, WeaponStyle.MAGIC); - StyleMap.put(ItemID.PHARAOHS_SCEPTRE_1, WeaponStyle.MAGIC); - StyleMap.put(ItemID.PHARAOHS_SCEPTRE_2, WeaponStyle.MAGIC); - StyleMap.put(ItemID.PHARAOHS_SCEPTRE_3, WeaponStyle.MAGIC); - StyleMap.put(ItemID.PHARAOHS_SCEPTRE_4, WeaponStyle.MAGIC); - StyleMap.put(ItemID.PHARAOHS_SCEPTRE_5, WeaponStyle.MAGIC); - StyleMap.put(ItemID.PHARAOHS_SCEPTRE_6, WeaponStyle.MAGIC); - StyleMap.put(ItemID.PHARAOHS_SCEPTRE_7, WeaponStyle.MAGIC); - StyleMap.put(ItemID.PHARAOHS_SCEPTRE_8, WeaponStyle.MAGIC); - StyleMap.put(ItemID.ROD_OF_IVANDIS_1, WeaponStyle.MAGIC); - StyleMap.put(ItemID.ROD_OF_IVANDIS_10, WeaponStyle.MAGIC); - StyleMap.put(ItemID.ROD_OF_IVANDIS_2, WeaponStyle.MAGIC); - StyleMap.put(ItemID.ROD_OF_IVANDIS_3, WeaponStyle.MAGIC); - StyleMap.put(ItemID.ROD_OF_IVANDIS_4, WeaponStyle.MAGIC); - StyleMap.put(ItemID.ROD_OF_IVANDIS_5, WeaponStyle.MAGIC); - StyleMap.put(ItemID.ROD_OF_IVANDIS_6, WeaponStyle.MAGIC); - StyleMap.put(ItemID.ROD_OF_IVANDIS_7, WeaponStyle.MAGIC); - StyleMap.put(ItemID.ROD_OF_IVANDIS_8, WeaponStyle.MAGIC); - StyleMap.put(ItemID.ROD_OF_IVANDIS_9, WeaponStyle.MAGIC); - StyleMap.put(ItemID.SANGUINESTI_STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.SANGUINESTI_STAFF_UNCHARGED, WeaponStyle.MAGIC); - StyleMap.put(ItemID.SARADOMIN_CROZIER, WeaponStyle.MAGIC); - StyleMap.put(ItemID.SARADOMIN_STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.SKULL_SCEPTRE, WeaponStyle.MAGIC); - StyleMap.put(ItemID.SKULL_SCEPTRE_I, WeaponStyle.MAGIC); - StyleMap.put(ItemID.SLAYERS_STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.SLAYERS_STAFF_E, WeaponStyle.MAGIC); - StyleMap.put(ItemID.SMOKE_BATTLESTAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.STAFF_OF_AIR, WeaponStyle.MAGIC); - StyleMap.put(ItemID.STAFF_OF_BALANCE, WeaponStyle.MAGIC); - StyleMap.put(ItemID.STAFF_OF_BOB_THE_CAT, WeaponStyle.MAGIC); - StyleMap.put(ItemID.STAFF_OF_EARTH, WeaponStyle.MAGIC); - StyleMap.put(ItemID.STAFF_OF_FIRE, WeaponStyle.MAGIC); - StyleMap.put(ItemID.STAFF_OF_LIGHT, WeaponStyle.MAGIC); - StyleMap.put(ItemID.STAFF_OF_THE_DEAD, WeaponStyle.MAGIC); - StyleMap.put(ItemID.STAFF_OF_THE_DEAD_23613, WeaponStyle.MAGIC); - StyleMap.put(ItemID.STAFF_OF_WATER, WeaponStyle.MAGIC); - StyleMap.put(ItemID.STARTER_STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.STEAM_BATTLESTAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.STEAM_BATTLESTAFF_12795, WeaponStyle.MAGIC); - StyleMap.put(ItemID.TEACHER_WAND, WeaponStyle.MAGIC); - StyleMap.put(ItemID.THAMMARONS_SCEPTRE, WeaponStyle.MAGIC); - StyleMap.put(ItemID.THAMMARONS_SCEPTRE_U, WeaponStyle.MAGIC); - StyleMap.put(ItemID.TOKTZMEJTAL, WeaponStyle.MAGIC); - StyleMap.put(ItemID.TOXIC_STAFF_OF_THE_DEAD, WeaponStyle.MAGIC); - StyleMap.put(ItemID.TOXIC_STAFF_UNCHARGED, WeaponStyle.MAGIC); - StyleMap.put(ItemID.TRIDENT_OF_THE_SEAS, WeaponStyle.MAGIC); - StyleMap.put(ItemID.TRIDENT_OF_THE_SEAS_E, WeaponStyle.MAGIC); - StyleMap.put(ItemID.TRIDENT_OF_THE_SEAS_FULL, WeaponStyle.MAGIC); - StyleMap.put(ItemID.TRIDENT_OF_THE_SWAMP, WeaponStyle.MAGIC); - StyleMap.put(ItemID.TRIDENT_OF_THE_SWAMP_E, WeaponStyle.MAGIC); - StyleMap.put(ItemID.UNCHARGED_TOXIC_TRIDENT, WeaponStyle.MAGIC); - StyleMap.put(ItemID.UNCHARGED_TOXIC_TRIDENT_E, WeaponStyle.MAGIC); - StyleMap.put(ItemID.UNCHARGED_TRIDENT, WeaponStyle.MAGIC); - StyleMap.put(ItemID.UNCHARGED_TRIDENT_E, WeaponStyle.MAGIC); - StyleMap.put(ItemID.VOID_KNIGHT_MACE, WeaponStyle.MAGIC); - StyleMap.put(ItemID.VOID_KNIGHT_MACE_BROKEN, WeaponStyle.MAGIC); - StyleMap.put(ItemID.VOLATILE_NIGHTMARE_STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.WAND, WeaponStyle.MAGIC); - StyleMap.put(ItemID.WATER_BATTLESTAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.WHITE_MAGIC_STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.ZAMORAK_CROZIER, WeaponStyle.MAGIC); - StyleMap.put(ItemID.ZAMORAK_STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.ZURIELS_STAFF, WeaponStyle.MAGIC); - StyleMap.put(ItemID.ZURIELS_STAFF_23617, WeaponStyle.MAGIC); - //what the fuck... - StyleMap.put(ItemID.GNOMEBALL, WeaponStyle.MAGIC); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/util/WeaponStyle.java b/runelite-client/src/main/java/net/runelite/client/util/WeaponStyle.java deleted file mode 100644 index f5e2a03cde..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/util/WeaponStyle.java +++ /dev/null @@ -1,6 +0,0 @@ -package net.runelite.client.util; - -public enum WeaponStyle -{ - MAGIC, RANGE, MELEE -} diff --git a/runelite-client/src/main/java/net/runelite/client/util/WildcardMatcher.java b/runelite-client/src/main/java/net/runelite/client/util/WildcardMatcher.java index 588d554433..2421010fb5 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/WildcardMatcher.java +++ b/runelite-client/src/main/java/net/runelite/client/util/WildcardMatcher.java @@ -34,23 +34,23 @@ public class WildcardMatcher public static boolean matches(String pattern, String text) { final Matcher matcher = WILDCARD_PATTERN.matcher(pattern); - final StringBuilder sb = new StringBuilder(); + final StringBuffer buffer = new StringBuffer(); - sb.append("(?i)"); + buffer.append("(?i)"); while (matcher.find()) { if (matcher.group(1) != null) { - matcher.appendReplacement(sb, ".*"); + matcher.appendReplacement(buffer, ".*"); } else { - matcher.appendReplacement(sb, Matcher.quoteReplacement(Pattern.quote(matcher.group(0)))); + matcher.appendReplacement(buffer, Matcher.quoteReplacement(Pattern.quote(matcher.group(0)))); } } - matcher.appendTail(sb); - final String replaced = sb.toString(); + matcher.appendTail(buffer); + final String replaced = buffer.toString(); return text.matches(replaced); } diff --git a/runelite-client/src/main/java/net/runelite/client/util/WinApi.java b/runelite-client/src/main/java/net/runelite/client/util/WinApi.java deleted file mode 100644 index 430998c7a3..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/util/WinApi.java +++ /dev/null @@ -1,38 +0,0 @@ -package net.runelite.client.util; - -import com.sun.jna.Native; -import com.sun.jna.WString; -import com.sun.jna.platform.win32.WinBase; -import com.sun.jna.platform.win32.WinDef; -import com.sun.jna.win32.StdCallLibrary; -import java.time.LocalDateTime; - -interface Kernel32 extends StdCallLibrary -{ - int TIME_NOSECONDS = 0x00000002; - - int GetTimeFormatEx( - WString lpLocaleName, WinDef.DWORD dwFlags, WinBase.SYSTEMTIME lpTime, - WString lpFormat, char[] lpTimeStr, int cchTime); - -} - -public class WinApi -{ - static Kernel32 kernel32 = Native.loadLibrary("kernel32", Kernel32.class); - - public static String getTimeFormatString(LocalDateTime localDateTime) - { - char[] lpTimeStr = new char[80]; - WinDef.DWORD dwFlags = new WinDef.DWORD(Kernel32.TIME_NOSECONDS); - - WinBase.SYSTEMTIME time = new WinBase.SYSTEMTIME(); - time.wHour = (short) localDateTime.getHour(); - time.wMinute = (short) localDateTime.getMinute(); - time.wSecond = (short) localDateTime.getSecond(); - time.wMilliseconds = 0; - - kernel32.GetTimeFormatEx(null, dwFlags, time, null, lpTimeStr, 80); - return new String(lpTimeStr); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/util/WorldUtil.java b/runelite-client/src/main/java/net/runelite/client/util/WorldUtil.java index 1acb092239..9f508676a5 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/WorldUtil.java +++ b/runelite-client/src/main/java/net/runelite/client/util/WorldUtil.java @@ -35,7 +35,6 @@ public class WorldUtil /** * Converts http-api world types to runelite-api world types * TODO: Find a better way to handle these to not have duplicate interfaces - * * @param apiTypes http-api world types * @return runelite-api world types */ diff --git a/runelite-client/src/main/java/net/runelite/client/util/ping/IPHlpAPI.java b/runelite-client/src/main/java/net/runelite/client/util/ping/IPHlpAPI.java deleted file mode 100644 index 34012ece06..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/util/ping/IPHlpAPI.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2018, Adam - * 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.util.ping; - -import com.sun.jna.Library; -import com.sun.jna.Native; -import com.sun.jna.Pointer; - -interface IPHlpAPI extends Library -{ - IPHlpAPI INSTANCE = Native.load("IPHlpAPI", IPHlpAPI.class); - - Pointer IcmpCreateFile(); - - boolean IcmpCloseHandle(Pointer handle); - - int IcmpSendEcho(Pointer IcmpHandle, int DestinationAddress, Pointer RequestData, short RequestSize, Pointer RequestOptions, IcmpEchoReply ReplyBuffer, int ReplySize, int Timeout); -} diff --git a/runelite-client/src/main/java/net/runelite/client/util/ping/IcmpEchoReply.java b/runelite-client/src/main/java/net/runelite/client/util/ping/IcmpEchoReply.java deleted file mode 100644 index bf5616144b..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/util/ping/IcmpEchoReply.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2018, Adam - * 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.util.ping; - -import com.sun.jna.Native; -import com.sun.jna.Pointer; -import com.sun.jna.Structure; -import com.sun.jna.platform.win32.WinDef; -import java.util.Arrays; -import java.util.List; - -public class IcmpEchoReply extends Structure -{ - private static final int IP_OPTION_INFO_SIZE = 1 + 1 + 1 + 1 + (Native.POINTER_SIZE == 8 ? 12 : 4); // on 64bit vms add 4 byte padding - public static final int SIZE = 4 + 4 + 4 + 2 + 2 + Native.POINTER_SIZE + IP_OPTION_INFO_SIZE; - - public WinDef.ULONG address; - public WinDef.ULONG status; - public WinDef.ULONG roundTripTime; - public WinDef.USHORT dataSize; - public WinDef.USHORT reserved; - public WinDef.PVOID data; - public WinDef.UCHAR ttl; - public WinDef.UCHAR tos; - public WinDef.UCHAR flags; - public WinDef.UCHAR optionsSize; - public WinDef.PVOID optionsData; - - IcmpEchoReply(Pointer p) - { - super(p); - } - - @Override - protected List getFieldOrder() - { - return Arrays.asList("address", "status", "roundTripTime", "dataSize", "reserved", "data", "ttl", "tos", "flags", "optionsSize", "optionsData"); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/util/ping/Ping.java b/runelite-client/src/main/java/net/runelite/client/util/ping/Ping.java deleted file mode 100644 index 873a1b0f7b..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/util/ping/Ping.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2018, Adam - * 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.util.ping; - -import com.sun.jna.Memory; -import com.sun.jna.Pointer; -import java.io.IOException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.net.UnknownHostException; -import lombok.extern.slf4j.Slf4j; -import net.runelite.client.util.OSType; -import net.runelite.http.api.worlds.World; - -@Slf4j -public class Ping -{ - private static final String RUNELITE_PING = "RuneLitePing"; - - private static final int TIMEOUT = 2000; - private static final int PORT = 43594; - - public static int ping(World world) - { - return ping(world.getAddress()); - } - - public static int ping(String address) - { - try - { - if (OSType.getOSType() == OSType.Windows) - { - return windowsPing(address); - } - return tcpPing(address); - } - catch (IOException ex) - { - log.warn("error pinging", ex); - return -1; - } - } - - private static int windowsPing(String worldAddress) throws UnknownHostException - { - IPHlpAPI ipHlpAPI = IPHlpAPI.INSTANCE; - Pointer ptr = ipHlpAPI.IcmpCreateFile(); - InetAddress inetAddress = InetAddress.getByName(worldAddress); - byte[] address = inetAddress.getAddress(); - String dataStr = RUNELITE_PING; - int dataLength = dataStr.length() + 1; - Pointer data = new Memory(dataLength); - data.setString(0L, dataStr); - IcmpEchoReply icmpEchoReply = new IcmpEchoReply(new Memory(IcmpEchoReply.SIZE + dataLength)); - assert icmpEchoReply.size() == IcmpEchoReply.SIZE; - int packed = (address[0] & 0xff) | ((address[1] & 0xff) << 8) | ((address[2] & 0xff) << 16) | ((address[3] & 0xff) << 24); - int ret = ipHlpAPI.IcmpSendEcho(ptr, packed, data, (short) (dataLength), Pointer.NULL, icmpEchoReply, IcmpEchoReply.SIZE + dataLength, TIMEOUT); - if (ret != 1) - { - ipHlpAPI.IcmpCloseHandle(ptr); - return -1; - } - - int rtt = Math.toIntExact(icmpEchoReply.roundTripTime.longValue()); - ipHlpAPI.IcmpCloseHandle(ptr); - - return rtt; - } - - private static int tcpPing(String worldAddress) throws IOException - { - try (Socket socket = new Socket()) - { - socket.setSoTimeout(TIMEOUT); - InetAddress inetAddress = InetAddress.getByName(worldAddress); - long start = System.nanoTime(); - socket.connect(new InetSocketAddress(inetAddress, PORT)); - long end = System.nanoTime(); - return (int) ((end - start) / 1000000L); - } - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/ws/PartyService.java b/runelite-client/src/main/java/net/runelite/client/ws/PartyService.java index a6ecd263e4..094b1d3de4 100644 --- a/runelite-client/src/main/java/net/runelite/client/ws/PartyService.java +++ b/runelite-client/src/main/java/net/runelite/client/ws/PartyService.java @@ -34,13 +34,14 @@ import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import net.runelite.api.ChatMessageType; -import static net.runelite.api.util.Text.JAGEX_PRINTABLE_CHAR_MATCHER; import net.runelite.client.account.AccountSession; import net.runelite.client.account.SessionManager; import net.runelite.client.chat.ChatMessageManager; import net.runelite.client.chat.QueuedMessage; import net.runelite.client.eventbus.EventBus; +import net.runelite.client.eventbus.Subscribe; import net.runelite.client.events.PartyChanged; +import static net.runelite.client.util.Text.JAGEX_PRINTABLE_CHAR_MATCHER; import net.runelite.http.api.ws.messages.party.Join; import net.runelite.http.api.ws.messages.party.Part; import net.runelite.http.api.ws.messages.party.PartyChatMessage; @@ -77,10 +78,6 @@ public class PartyService this.sessionManager = sessionManager; this.eventBus = eventBus; this.chat = chat; - - eventBus.subscribe(UserJoin.class, this, this::onUserJoin); - eventBus.subscribe(UserPart.class, this, this::onUserPart); - eventBus.subscribe(PartyChatMessage.class, this, this::onPartyChatMessage); } public void changeParty(UUID newParty) @@ -104,7 +101,7 @@ public class PartyService wsClient.changeSession(null); } - eventBus.post(PartyChanged.class, new PartyChanged(partyId)); + eventBus.post(new PartyChanged(partyId)); return; } @@ -117,10 +114,11 @@ public class PartyService wsClient.changeSession(uuid); } - eventBus.post(PartyChanged.class, new PartyChanged(partyId)); + eventBus.post(new PartyChanged(partyId)); wsClient.send(new Join(partyId, username)); } + @Subscribe(priority = 1) // run prior to plugins so that the member is joined by the time the plugins see it. public void onUserJoin(final UserJoin message) { if (!partyId.equals(message.getPartyId())) @@ -144,12 +142,14 @@ public class PartyService } } - private void onUserPart(final UserPart message) + @Subscribe + public void onUserPart(final UserPart message) { members.removeIf(member -> member.getMemberId().equals(message.getMemberId())); } - private void onPartyChatMessage(final PartyChatMessage message) + @Subscribe + public void onPartyChatMessage(final PartyChatMessage message) { // Remove non-printable characters, and tags from message String sentMesage = JAGEX_PRINTABLE_CHAR_MATCHER.retainFrom(message.getValue()) @@ -187,7 +187,7 @@ public class PartyService return null; } - private PartyMember getMemberByName(final String name) + public PartyMember getMemberByName(final String name) { for (PartyMember member : members) { diff --git a/runelite-client/src/main/java/net/runelite/client/ws/WSClient.java b/runelite-client/src/main/java/net/runelite/client/ws/WSClient.java index 3b6ee1752e..a2f5f7b9e0 100644 --- a/runelite-client/src/main/java/net/runelite/client/ws/WSClient.java +++ b/runelite-client/src/main/java/net/runelite/client/ws/WSClient.java @@ -177,8 +177,7 @@ public class WSClient extends WebSocketListener implements AutoCloseable } log.debug("Got: {}", text); - - eventBus.post(message.getClass(), message); + eventBus.post(message); } @Override @@ -194,4 +193,4 @@ public class WSClient extends WebSocketListener implements AutoCloseable log.warn("Error in websocket {}:{}", response, t); this.webSocket = null; } -} \ No newline at end of file +}