Merges Injector

Welcome to the new world boys.
This commit is contained in:
zeruth
2019-06-06 20:47:41 -04:00
parent 79ed69ccdf
commit 882be3cb71
3613 changed files with 193663 additions and 158070 deletions

View File

@@ -1,113 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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;
import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
@Singleton
@Slf4j
public class ClientSessionManager
{
private final SessionClient sessionClient = new SessionClient();
private final ScheduledExecutorService executorService;
private ScheduledFuture<?> scheduledFuture;
private UUID sessionId;
@Inject
ClientSessionManager(ScheduledExecutorService executorService)
{
this.executorService = executorService;
}
public void start()
{
try
{
sessionId = sessionClient.open();
log.debug("Opened session {}", sessionId);
}
catch (IOException ex)
{
log.warn("error opening session", ex);
}
scheduledFuture = executorService.scheduleWithFixedDelay(this::ping, 1, 10, TimeUnit.MINUTES);
}
public void shutdown()
{
if (sessionId != null)
{
try
{
sessionClient.delete(sessionId);
}
catch (IOException ex)
{
log.warn(null, ex);
}
sessionId = null;
}
scheduledFuture.cancel(true);
}
private void ping()
{
try
{
if (sessionId == null)
{
sessionId = sessionClient.open();
log.debug("Opened session {}", sessionId);
return;
}
}
catch (IOException ex)
{
log.warn(null, ex);
}
try
{
sessionClient.ping(sessionId);
}
catch (IOException ex)
{
log.warn("Resetting session", ex);
sessionId = null;
}
}
}

View File

@@ -1,344 +0,0 @@
/*
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
* 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;
import com.google.common.base.Strings;
import com.google.common.escape.Escaper;
import com.google.common.escape.Escapers;
import com.google.inject.Inject;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.client.chat.ChatColorType;
import net.runelite.client.chat.ChatMessageBuilder;
import net.runelite.client.chat.ChatMessageManager;
import net.runelite.client.chat.QueuedMessage;
import net.runelite.client.config.RuneLiteConfig;
import net.runelite.client.ui.ClientUI;
import net.runelite.client.util.OSType;
@Singleton
@Slf4j
public class Notifier
{
// Default timeout of notification in milliseconds
private static final int DEFAULT_TIMEOUT = 10000;
private static final String DOUBLE_QUOTE = "\"";
private static final Escaper SHELL_ESCAPE = Escapers.builder()
.addEscape('"', "'")
.build();
// Notifier properties
private static final Color FLASH_COLOR = new Color(255, 0, 0, 70);
private static final int FLASH_DURATION = 2000;
private final Client client;
private final String appName;
private final RuneLiteConfig runeLiteConfig;
private final ClientUI clientUI;
private final ScheduledExecutorService executorService;
private final ChatMessageManager chatMessageManager;
private final Path notifyIconPath;
private final boolean terminalNotifierAvailable;
private Instant flashStart;
@Inject
private Notifier(
final ClientUI clientUI,
final Client client,
final RuneLiteConfig runeliteConfig,
final RuneLiteProperties runeLiteProperties,
final ScheduledExecutorService executorService,
final ChatMessageManager chatMessageManager)
{
this.client = client;
this.appName = runeLiteProperties.getTitle();
this.clientUI = clientUI;
this.runeLiteConfig = runeliteConfig;
this.executorService = executorService;
this.chatMessageManager = chatMessageManager;
this.notifyIconPath = RuneLite.RUNELITE_DIR.toPath().resolve("icon.png");
// First check if we are running in launcher
this.terminalNotifierAvailable =
!Strings.isNullOrEmpty(RuneLiteProperties.getLauncherVersion())
&& isTerminalNotifierAvailable();
storeIcon();
}
public void notify(String message)
{
notify(message, TrayIcon.MessageType.NONE);
}
public void notify(String message, TrayIcon.MessageType type)
{
if (!runeLiteConfig.sendNotificationsWhenFocused() && clientUI.isFocused())
{
return;
}
if (runeLiteConfig.requestFocusOnNotification())
{
clientUI.requestFocus();
}
if (runeLiteConfig.enableTrayNotifications())
{
sendNotification(appName, message, type);
}
if (runeLiteConfig.enableNotificationSound())
{
Toolkit.getDefaultToolkit().beep();
}
if (runeLiteConfig.enableGameMessageNotification() && client.getGameState() == GameState.LOGGED_IN)
{
final String formattedMessage = new ChatMessageBuilder()
.append(ChatColorType.HIGHLIGHT)
.append(message)
.build();
chatMessageManager.queue(QueuedMessage.builder()
.type(ChatMessageType.CONSOLE)
.name(appName)
.runeLiteFormattedMessage(formattedMessage)
.build());
}
if (runeLiteConfig.enableFlashNotification())
{
flashStart = Instant.now();
}
log.debug(message);
}
public void processFlash(final Graphics2D graphics)
{
if (flashStart == null || client.getGameCycle() % 40 >= 20)
{
return;
}
else if (client.getGameState() != GameState.LOGGED_IN)
{
flashStart = null;
return;
}
final Color color = graphics.getColor();
graphics.setColor(FLASH_COLOR);
graphics.fill(new Rectangle(client.getCanvas().getSize()));
graphics.setColor(color);
if (Instant.now().minusMillis(FLASH_DURATION).isAfter(flashStart))
{
flashStart = null;
}
}
private void sendNotification(
final String title,
final String message,
final TrayIcon.MessageType type)
{
final String escapedTitle = SHELL_ESCAPE.escape(title);
final String escapedMessage = SHELL_ESCAPE.escape(message);
switch (OSType.getOSType())
{
case Linux:
sendLinuxNotification(escapedTitle, escapedMessage, type);
break;
case MacOS:
sendMacNotification(escapedTitle, escapedMessage);
break;
default:
sendTrayNotification(title, message, type);
}
}
private void sendTrayNotification(
final String title,
final String message,
final TrayIcon.MessageType type)
{
if (clientUI.getTrayIcon() != null)
{
clientUI.getTrayIcon().displayMessage(title, message, type);
}
}
private void sendLinuxNotification(
final String title,
final String message,
final TrayIcon.MessageType type)
{
final List<String> commands = new ArrayList<>();
commands.add("notify-send");
commands.add(title);
commands.add(message);
commands.add("-i");
commands.add(SHELL_ESCAPE.escape(notifyIconPath.toAbsolutePath().toString()));
commands.add("-u");
commands.add(toUrgency(type));
commands.add("-t");
commands.add(String.valueOf(DEFAULT_TIMEOUT));
executorService.submit(() ->
{
try
{
Process notificationProcess = sendCommand(commands);
boolean exited = notificationProcess.waitFor(500, TimeUnit.MILLISECONDS);
if (exited && notificationProcess.exitValue() == 0)
{
return;
}
}
catch (IOException | InterruptedException ex)
{
log.debug("error sending notification", ex);
}
// fall back to tray notification
sendTrayNotification(title, message, type);
});
}
private void sendMacNotification(final String title, final String message)
{
final List<String> commands = new ArrayList<>();
if (terminalNotifierAvailable)
{
commands.add("terminal-notifier");
commands.add("-group");
commands.add("net.runelite.launcher");
commands.add("-sender");
commands.add("net.runelite.launcher");
commands.add("-message");
commands.add(DOUBLE_QUOTE + message + DOUBLE_QUOTE);
commands.add("-title");
commands.add(DOUBLE_QUOTE + title + DOUBLE_QUOTE);
}
else
{
commands.add("osascript");
commands.add("-e");
final String script = "display notification " + DOUBLE_QUOTE +
message +
DOUBLE_QUOTE +
" with title " +
DOUBLE_QUOTE +
title +
DOUBLE_QUOTE;
commands.add(script);
}
try
{
sendCommand(commands);
}
catch (IOException ex)
{
log.warn("error sending notification", ex);
}
}
private static Process sendCommand(final List<String> commands) throws IOException
{
return new ProcessBuilder(commands.toArray(new String[commands.size()]))
.redirectErrorStream(true)
.start();
}
private void storeIcon()
{
if (OSType.getOSType() == OSType.Linux && !Files.exists(notifyIconPath))
{
try (InputStream stream = Notifier.class.getResourceAsStream("/runelite.png"))
{
Files.copy(stream, notifyIconPath);
}
catch (IOException ex)
{
log.warn(null, ex);
}
}
}
private boolean isTerminalNotifierAvailable()
{
if (OSType.getOSType() == OSType.MacOS)
{
try
{
final Process exec = Runtime.getRuntime().exec(new String[]{"terminal-notifier", "-help"});
exec.waitFor();
return exec.exitValue() == 0;
}
catch (IOException | InterruptedException e)
{
return false;
}
}
return false;
}
private static String toUrgency(TrayIcon.MessageType type)
{
switch (type)
{
case WARNING:
case ERROR:
return "critical";
default:
return "normal";
}
}
}

View File

@@ -1,412 +0,0 @@
/*
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
* 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;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import com.google.common.annotations.VisibleForTesting;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import java.io.File;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.util.Locale;
import javax.annotation.Nullable;
import javax.inject.Provider;
import javax.inject.Singleton;
import joptsimple.ArgumentAcceptingOptionSpec;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import joptsimple.util.EnumConverter;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.client.account.SessionManager;
import net.runelite.client.chat.ChatMessageManager;
import net.runelite.client.chat.CommandManager;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.discord.DiscordService;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.game.ClanManager;
import net.runelite.client.game.ItemManager;
import net.runelite.client.game.LootManager;
import net.runelite.client.game.chatbox.ChatboxPanelManager;
import net.runelite.client.graphics.ModelOutlineRenderer;
import net.runelite.client.menus.MenuManager;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginInstantiationException;
import net.runelite.client.plugins.PluginManager;
import net.runelite.client.rs.ClientUpdateCheckMode;
import net.runelite.client.task.Scheduler;
import net.runelite.client.ui.ClientUI;
import net.runelite.client.ui.DrawManager;
import net.runelite.client.ui.RuneLiteSplashScreen;
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.infobox.InfoBoxOverlay;
import net.runelite.client.ui.overlay.tooltip.TooltipOverlay;
import net.runelite.client.ui.overlay.worldmap.WorldMapOverlay;
import net.runelite.client.ws.PartyService;
import org.slf4j.LoggerFactory;
@Singleton
@Slf4j
public class RuneLite
{
public static final String RUNELIT_VERSION = "0.1.2";
public static final File RUNELITE_DIR = new File(System.getProperty("user.home"), ".runelite");
public static final File PROFILES_DIR = new File(RUNELITE_DIR, "profiles");
public static final File PLUGIN_DIR = new File(RUNELITE_DIR, "plugins");
public static final File SCREENSHOT_DIR = new File(RUNELITE_DIR, "screenshots");
static final RuneLiteSplashScreen splashScreen = new RuneLiteSplashScreen();
@Getter
private static Injector injector;
@Inject
private PluginManager pluginManager;
@Inject
private EventBus eventBus;
@Inject
private ConfigManager configManager;
@Inject
private DrawManager drawManager;
@Inject
private SessionManager sessionManager;
@Inject
public DiscordService discordService;
@Inject
private ClientSessionManager clientSessionManager;
@Inject
private ClientUI clientUI;
@Inject
private InfoBoxManager infoBoxManager;
@Inject
private OverlayManager overlayManager;
@Inject
private PartyService partyService;
@Inject
private Provider<ItemManager> itemManager;
@Inject
private Provider<OverlayRenderer> overlayRenderer;
@Inject
private Provider<ClanManager> clanManager;
@Inject
private Provider<ChatMessageManager> chatMessageManager;
@Inject
private Provider<MenuManager> menuManager;
@Inject
private Provider<CommandManager> commandManager;
@Inject
private Provider<InfoBoxOverlay> infoBoxOverlay;
@Inject
private Provider<TooltipOverlay> tooltipOverlay;
@Inject
private Provider<WorldMapOverlay> worldMapOverlay;
@Inject
private Provider<ArrowWorldOverlay> arrowWorldOverlay;
@Inject
private Provider<ArrowMinimapOverlay> arrowMinimapOverlay;
@Inject
private Provider<LootManager> lootManager;
@Inject
private Provider<ChatboxPanelManager> chatboxPanelManager;
@Inject
@Nullable
private Client client;
@Inject
private Provider<ModelOutlineRenderer> modelOutlineRenderer;
@Inject
private Scheduler scheduler;
public static void main(String[] args) throws Exception
{
Locale.setDefault(Locale.ENGLISH);
final OptionParser parser = new OptionParser();
parser.accepts("developer-mode", "Enable developer tools");
parser.accepts("debug", "Show extra debugging output");
parser.accepts("no-splash", "Do not show the splash screen");
final ArgumentAcceptingOptionSpec<String> proxyInfo = parser
.accepts("proxy")
.withRequiredArg().ofType(String.class);
final ArgumentAcceptingOptionSpec<ClientUpdateCheckMode> updateMode = parser
.accepts("rs", "Select client type")
.withRequiredArg()
.ofType(ClientUpdateCheckMode.class)
.defaultsTo(ClientUpdateCheckMode.AUTO)
.withValuesConvertedBy(new EnumConverter<ClientUpdateCheckMode>(ClientUpdateCheckMode.class)
{
@Override
public ClientUpdateCheckMode convert(String v)
{
return super.convert(v.toUpperCase());
}
});
parser.accepts("help", "Show this text").forHelp();
OptionSet options = parser.parse(args);
if (options.has("proxy"))
{
String[] proxy = options.valueOf(proxyInfo).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 PasswordAuthentication auth = new PasswordAuthentication(user, pass);
protected PasswordAuthentication getPasswordAuthentication()
{
return auth;
}
});
}
}
if (options.has("help"))
{
parser.printHelpOn(System.out);
System.exit(0);
}
final boolean developerMode = true;
if (developerMode)
{
boolean assertions = false;
assert assertions = true;
if (!assertions)
{
java.util.logging.Logger.getAnonymousLogger().warning("Developers should enable assertions; Add `-ea` to your JVM arguments`");
}
}
PROFILES_DIR.mkdirs();
if (options.has("debug"))
{
final Logger logger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
logger.setLevel(Level.DEBUG);
}
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) ->
{
log.error("Uncaught exception:", throwable);
if (throwable instanceof AbstractMethodError)
{
log.error("Classes are out of date; Build with maven again.");
}
});
if (!options.has("no-splash"))
{
splashScreen.open(4);
}
// The submessage is shown in case the connection is slow
splashScreen.setMessage("Starting RuneLite Injector");
splashScreen.setSubMessage(" ");
final long start = System.currentTimeMillis();
injector = Guice.createInjector(new RuneLiteModule(
options.valueOf(updateMode),
developerMode));
injector.getInstance(RuneLite.class).start();
splashScreen.setProgress(1, 5);
final long end = System.currentTimeMillis();
final RuntimeMXBean rb = ManagementFactory.getRuntimeMXBean();
final long uptime = rb.getUptime();
log.info("Client initialization took {}ms. Uptime: {}ms", end - start, uptime);
}
public void start() throws Exception
{
// Load RuneLite or Vanilla client
final boolean isOutdated = client == null;
if (!isOutdated)
{
// Inject members into client
injector.injectMembers(client);
}
// Load user configuration
splashScreen.setMessage("Loading configuration");
configManager.load();
// Load the session, including saved configuration
sessionManager.loadSession();
splashScreen.setProgress(2, 5);
splashScreen.setMessage("Loading plugins");
// Begin watching for new plugins
pluginManager.watch();
// Tell the plugin manager if client is outdated or not
pluginManager.setOutdated(isOutdated);
// Load the plugins, but does not start them yet.
// This will initialize configuration
pluginManager.loadCorePlugins();
// Plugins have provided their config, so set default config
// to main settings
pluginManager.loadDefaultPluginConfiguration();
splashScreen.setProgress(3, 5);
splashScreen.setMessage("Starting Session");
// Start client session
clientSessionManager.start();
splashScreen.setProgress(4, 5);
// Load the session, including saved configuration
splashScreen.setMessage("Loading interface");
splashScreen.setProgress(5, 5);
// Initialize UI
clientUI.open(this);
// Close the splash screen
splashScreen.close();
// Register event listeners
eventBus.register(clientUI);
eventBus.register(pluginManager);
eventBus.register(overlayManager);
eventBus.register(drawManager);
eventBus.register(infoBoxManager);
eventBus.register(partyService);
if (!isOutdated)
{
// Initialize chat colors
chatMessageManager.get().loadColors();
eventBus.register(overlayRenderer.get());
eventBus.register(clanManager.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());
// Add core overlays
WidgetOverlay.createOverlays(client).forEach(overlayManager::add);
overlayManager.add(infoBoxOverlay.get());
overlayManager.add(worldMapOverlay.get());
overlayManager.add(tooltipOverlay.get());
overlayManager.add(arrowWorldOverlay.get());
overlayManager.add(arrowMinimapOverlay.get());
}
// Start plugins
pluginManager.startCorePlugins();
// Register additional schedulers
if (this.client != null)
{
scheduler.registerObject(modelOutlineRenderer.get());
}
}
public void shutdown()
{
configManager.sendConfig();
clientSessionManager.shutdown();
discordService.close();
for (final Plugin plugin : pluginManager.getPlugins())
{
try
{
pluginManager.stopPlugin(plugin);
}
catch (PluginInstantiationException e)
{
log.warn("Failed to gracefully close plugin", e);
}
}
}
@VisibleForTesting
public static void setInjector(Injector injector)
{
RuneLite.injector = injector;
}
}

View File

@@ -1,126 +0,0 @@
/*
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
* 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;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.name.Names;
import java.applet.Applet;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import javax.annotation.Nullable;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.hooks.Callbacks;
import net.runelite.client.account.SessionManager;
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.RuneLiteConfig;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.game.ItemManager;
import net.runelite.client.menus.MenuManager;
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.util.DeferredEventBus;
import net.runelite.client.util.ExecutorServiceExceptionLogger;
import net.runelite.http.api.RuneLiteAPI;
import okhttp3.OkHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Slf4j
public class RuneLiteModule extends AbstractModule
{
private final ClientUpdateCheckMode updateCheckMode;
private final boolean developerMode;
public RuneLiteModule(final ClientUpdateCheckMode updateCheckMode, final boolean developerMode)
{
this.updateCheckMode = updateCheckMode;
this.developerMode = developerMode;
}
@Override
protected void configure()
{
bindConstant().annotatedWith(Names.named("updateCheckMode")).to(updateCheckMode);
bindConstant().annotatedWith(Names.named("developerMode")).to(developerMode);
bind(ScheduledExecutorService.class).toInstance(new ExecutorServiceExceptionLogger(Executors.newSingleThreadScheduledExecutor()));
bind(OkHttpClient.class).toInstance(RuneLiteAPI.CLIENT);
bind(MenuManager.class);
bind(ChatMessageManager.class);
bind(ItemManager.class);
bind(Scheduler.class);
bind(PluginManager.class);
bind(RuneLiteProperties.class);
bind(SessionManager.class);
bind(Callbacks.class).to(Hooks.class);
bind(EventBus.class)
.toInstance(new EventBus());
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
@Singleton
Applet provideApplet(ClientLoader clientLoader)
{
return clientLoader.load();
}
@Provides
@Singleton
Client provideClient(@Nullable Applet applet)
{
return applet instanceof Client ? (Client) applet : null;
}
@Provides
@Singleton
RuneLiteConfig provideConfig(ConfigManager configManager)
{
return configManager.getConfig(RuneLiteConfig.class);
}
@Provides
@Singleton
ChatColorConfig provideChatColorConfig(ConfigManager configManager)
{
return configManager.getConfig(ChatColorConfig.class);
}
}

View File

@@ -1,124 +0,0 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* 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;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import net.runelite.client.plugins.runeliteplus.RuneLitePlusPlugin;
@Singleton
@Slf4j
public class RuneLiteProperties
{
public static String discordAppID = "409416265891971072";
private static final String RUNELITE_TITLE = "runelite.title";
private static final String RUNELITE_VERSION = "runelite.version";
private static final String RUNESCAPE_VERSION = "runescape.version";
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 = "runelite.launcher.version";
private final Properties properties = new Properties();
@Inject
public RuneLiteProperties()
{
try (InputStream in = getClass().getResourceAsStream("runelite.properties"))
{
properties.load(in);
}
catch (IOException ex)
{
log.warn("unable to load propertries", ex);
}
}
public 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();
}
public String getVersion()
{
return properties.getProperty(RUNELITE_VERSION);
}
public String getRunescapeVersion()
{
return properties.getProperty(RUNESCAPE_VERSION);
}
public String getDiscordAppId()
{
if (RuneLitePlusPlugin.customPresenceEnabled)
{
return properties.getProperty(RuneLitePlusPlugin.rlPlusDiscordApp);
}
else
{
return properties.getProperty(DISCORD_APP_ID);
}
}
public String getDiscordInvite()
{
return properties.getProperty(DISCORD_INVITE);
}
public String getGithubLink()
{
return properties.getProperty(GITHUB_LINK);
}
public String getWikiLink()
{
return properties.getProperty(WIKI_LINK);
}
public String getPatreonLink()
{
return properties.getProperty(PATREON_LINK);
}
@Nullable
public static String getLauncherVersion()
{
return System.getProperty(LAUNCHER_VERSION_PROPERTY);
}
}

View File

@@ -1,95 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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;
import com.google.gson.JsonParseException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.UUID;
import net.runelite.http.api.RuneLiteAPI;
import okhttp3.HttpUrl;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
class SessionClient
{
UUID open() throws IOException
{
HttpUrl url = RuneLiteAPI.getRuneLitePlusSessionBase().newBuilder()
.build();
Request request = new Request.Builder()
.url(url)
.build();
try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute())
{
ResponseBody body = response.body();
InputStream in = body.byteStream();
return RuneLiteAPI.GSON.fromJson(new InputStreamReader(in), UUID.class);
}
catch (JsonParseException | IllegalArgumentException ex) // UUID.fromString can throw IllegalArgumentException
{
throw new IOException(ex);
}
}
void ping(UUID uuid) throws IOException
{
HttpUrl url = RuneLiteAPI.getRuneLitePlusSessionBase().newBuilder()
.addPathSegment("ping")
.addQueryParameter("session", uuid.toString())
.build();
Request request = new Request.Builder()
.url(url)
.build();
try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute())
{
if (!response.isSuccessful())
{
throw new IOException("Unsuccessful ping");
}
}
}
void delete(UUID uuid) throws IOException
{
HttpUrl url = RuneLiteAPI.getRuneLitePlusSessionBase().newBuilder()
.addQueryParameter("session", uuid.toString())
.build();
Request request = new Request.Builder()
.delete()
.url(url)
.build();
RuneLiteAPI.CLIENT.newCall(request).execute().close();
}
}

View File

@@ -1,39 +0,0 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* 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.account;
import java.time.Instant;
import java.util.UUID;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(of = "uuid")
public class AccountSession
{
private final UUID uuid;
private final Instant created;
private String username;
}

View File

@@ -1,232 +0,0 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* 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.account;
import com.google.gson.Gson;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.time.Instant;
import java.util.UUID;
import javax.inject.Inject;
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;
import net.runelite.client.ws.WSClient;
import net.runelite.http.api.account.AccountClient;
import net.runelite.http.api.account.OAuthResponse;
import net.runelite.http.api.ws.messages.LoginResponse;
@Singleton
@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;
@Inject
private SessionManager(ConfigManager configManager, EventBus eventBus, WSClient wsClient)
{
this.configManager = configManager;
this.eventBus = eventBus;
this.wsClient = wsClient;
eventBus.register(this);
}
public void loadSession()
{
if (!SESSION_FILE.exists())
{
log.info("No session file exists");
return;
}
AccountSession session;
try (FileInputStream in = new FileInputStream(SESSION_FILE))
{
session = new Gson().fromJson(new InputStreamReader(in), AccountSession.class);
log.debug("Loaded session for {}", session.getUsername());
}
catch (Exception ex)
{
log.warn("Unable to load session file", ex);
return;
}
// Check if session is still valid
AccountClient accountClient = new AccountClient(session.getUuid());
if (!accountClient.sesssionCheck())
{
log.debug("Loaded session {} is invalid", session.getUuid());
return;
}
openSession(session, false);
}
private void saveSession()
{
if (accountSession == null)
{
return;
}
try (FileWriter fw = new FileWriter(SESSION_FILE))
{
new Gson().toJson(accountSession, fw);
log.debug("Saved session to {}", SESSION_FILE);
}
catch (IOException ex)
{
log.warn("Unable to save session file", ex);
}
}
private void deleteSession()
{
SESSION_FILE.delete();
}
/**
* Set the given session as the active session and open a socket to the
* server with the given session
*
* @param session session
*/
private void openSession(AccountSession session, boolean openSocket)
{
// Change session on the websocket
if (openSocket)
{
wsClient.changeSession(session.getUuid());
}
accountSession = session;
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();
}
eventBus.post(new SessionOpen());
}
private void closeSession()
{
wsClient.changeSession(null);
if (accountSession == null)
{
return;
}
log.debug("Logging out of account {}", accountSession.getUsername());
AccountClient client = new AccountClient(accountSession.getUuid());
try
{
client.logout();
}
catch (IOException ex)
{
log.warn("Unable to logout of session", ex);
}
accountSession = null; // No more account
// Restore config
configManager.switchSession();
eventBus.post(new SessionClose());
}
public void login()
{
// If a session is already open, use that id. Otherwise generate a new id.
UUID uuid = wsClient.getSessionId() != null ? wsClient.getSessionId() : UUID.randomUUID();
AccountClient loginClient = new AccountClient(uuid);
final OAuthResponse login;
try
{
login = loginClient.login();
}
catch (IOException ex)
{
log.warn("Unable to get oauth url", ex);
return;
}
// Create new session
openSession(new AccountSession(login.getUid(), Instant.now()), true);
// Navigate to login link
LinkBrowser.browse(login.getOauthUrl());
}
@Subscribe
public void onLoginResponse(LoginResponse loginResponse)
{
log.debug("Now logged in as {}", loginResponse.getUsername());
AccountSession session = getAccountSession();
session.setUsername(loginResponse.getUsername());
// Open session, again, now that we have a username
// This triggers onSessionOpen
// The socket is already opened here anyway so we pass true for openSocket
openSession(session, true);
// Save session to disk
saveSession();
}
public void logout()
{
closeSession();
deleteSession();
}
}

View File

@@ -1,115 +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.callback;
import com.google.inject.Inject;
import java.util.Iterator;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.function.BooleanSupplier;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
@Singleton
@Slf4j
public class ClientThread
{
private ConcurrentLinkedQueue<BooleanSupplier> invokes = new ConcurrentLinkedQueue<>();
@Inject
private Client client;
public void invoke(Runnable r)
{
invoke(() ->
{
r.run();
return true;
});
}
/**
* Will run r on the game thread, at a unspecified point in the future.
* If r returns false, r will be ran again, at a later point
*/
public void invoke(BooleanSupplier r)
{
if (client.isClientThread())
{
if (r.getAsBoolean())
{
invokes.add(r);
}
return;
}
invokeLater(r);
}
/**
* Will run r on the game thread after this method returns
* If r returns false, r will be ran again, at a later point
*/
public void invokeLater(Runnable r)
{
invokeLater(() ->
{
r.run();
return true;
});
}
public void invokeLater(BooleanSupplier r)
{
invokes.add(r);
}
void invoke()
{
assert client.isClientThread();
Iterator<BooleanSupplier> ir = invokes.iterator();
for (; ir.hasNext(); )
{
BooleanSupplier r = ir.next();
boolean remove = true;
try
{
remove = r.getAsBoolean();
}
catch (ThreadDeath d)
{
throw d;
}
catch (Throwable e)
{
log.warn("Exception in invoke", e);
}
if (remove)
{
ir.remove();
}
}
}
}

View File

@@ -1,534 +0,0 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* 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.callback;
import com.google.inject.Injector;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.image.BufferedImage;
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.Constants;
import net.runelite.api.MainBufferProvider;
import net.runelite.api.NullItemID;
import net.runelite.api.Point;
import net.runelite.api.RenderOverview;
import net.runelite.api.Renderable;
import net.runelite.api.WorldMapManager;
import net.runelite.api.events.BeforeMenuRender;
import net.runelite.api.events.BeforeRender;
import net.runelite.api.events.GameTick;
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.input.KeyManager;
import net.runelite.client.input.MouseManager;
import net.runelite.client.task.Scheduler;
import net.runelite.client.ui.ClientUI;
import net.runelite.client.ui.DrawManager;
import net.runelite.client.ui.overlay.OverlayLayer;
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;
/**
* This class contains field required for mixins and runelite hooks to work.
* All remaining method hooks in this class are performance-critical or contain client-specific logic and so they
* can't just be placed in mixins or sent through event bus.
*/
@Singleton
@Slf4j
public class Hooks implements Callbacks
{
private static final long CHECK = Constants.GAME_TICK_LENGTH; // ms - how often to run checks
private static final Injector injector = RuneLite.getInjector();
private static final Client client = injector.getInstance(Client.class);
private 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();
@Inject
private EventBus eventBus;
@Inject
private DeferredEventBus deferredEventBus;
@Inject
private Scheduler scheduler;
@Inject
private InfoBoxManager infoBoxManager;
@Inject
private ChatMessageManager chatMessageManager;
@Inject
private MouseManager mouseManager;
@Inject
private KeyManager keyManager;
@Inject
private ClientThread clientThread;
@Inject
private DrawManager drawManager;
@Inject
private Notifier notifier;
@Inject
private ClientUI clientUi;
private Dimension lastStretchedDimensions;
private VolatileImage stretchedImage;
private Graphics2D stretchedGraphics;
private long lastCheck;
private boolean shouldProcessGameTick;
@Override
public void post(Object event)
{
eventBus.post(event);
}
@Override
public void postDeferred(Object event)
{
deferredEventBus.post(event);
}
@Override
public void clientMainLoop()
{
if (shouldProcessGameTick)
{
shouldProcessGameTick = false;
deferredEventBus.replay();
eventBus.post(GAME_TICK);
int tick = client.getTickCount();
client.setTickCount(tick + 1);
}
eventBus.post(BEFORE_RENDER);
clientThread.invoke();
long now = System.currentTimeMillis();
if (now - lastCheck < CHECK)
{
return;
}
lastCheck = now;
try
{
// tick pending scheduled tasks
scheduler.tick();
// cull infoboxes
infoBoxManager.cull();
chatMessageManager.process();
checkWorldMap();
}
catch (Exception ex)
{
log.warn("error during main loop tasks", ex);
}
}
/**
* When the world map opens it loads about ~100mb of data into memory, which
* represents about half of the total memory allocated by the client.
* This gets cached and never released, which causes GC pressure which can affect
* performance. This method reinitializes the world map cache, which allows the
* data to be garbage collected, and causes the map data from disk each time
* is it opened.
*/
private void checkWorldMap()
{
Widget widget = client.getWidget(WORLD_MAP_VIEW);
if (widget != null)
{
return;
}
RenderOverview renderOverview = client.getRenderOverview();
if (renderOverview == null)
{
return;
}
WorldMapManager manager = renderOverview.getWorldMapManager();
if (manager != null && manager.isLoaded())
{
log.debug("World map was closed, reinitializing");
renderOverview.initializeWorldMap(renderOverview.getWorldMapData());
}
}
@Override
public MouseEvent mousePressed(MouseEvent mouseEvent)
{
return mouseManager.processMousePressed(mouseEvent);
}
@Override
public MouseEvent mouseReleased(MouseEvent mouseEvent)
{
return mouseManager.processMouseReleased(mouseEvent);
}
@Override
public MouseEvent mouseClicked(MouseEvent mouseEvent)
{
return mouseManager.processMouseClicked(mouseEvent);
}
@Override
public MouseEvent mouseEntered(MouseEvent mouseEvent)
{
return mouseManager.processMouseEntered(mouseEvent);
}
@Override
public MouseEvent mouseExited(MouseEvent mouseEvent)
{
return mouseManager.processMouseExited(mouseEvent);
}
@Override
public MouseEvent mouseDragged(MouseEvent mouseEvent)
{
return mouseManager.processMouseDragged(mouseEvent);
}
@Override
public MouseEvent mouseMoved(MouseEvent mouseEvent)
{
return mouseManager.processMouseMoved(mouseEvent);
}
@Override
public MouseWheelEvent mouseWheelMoved(MouseWheelEvent event)
{
return mouseManager.processMouseWheelMoved(event);
}
@Override
public void keyPressed(KeyEvent keyEvent)
{
keyManager.processKeyPressed(keyEvent);
}
@Override
public void keyReleased(KeyEvent keyEvent)
{
keyManager.processKeyReleased(keyEvent);
}
@Override
public void keyTyped(KeyEvent keyEvent)
{
keyManager.processKeyTyped(keyEvent);
}
@Override
public void draw(MainBufferProvider mainBufferProvider, Graphics graphics, int x, int y)
{
if (graphics == null)
{
return;
}
Image image = mainBufferProvider.getImage();
final Image finalImage;
final Graphics2D graphics2d = (Graphics2D) image.getGraphics();
try
{
renderer.render(graphics2d, OverlayLayer.ALWAYS_ON_TOP);
}
catch (Exception ex)
{
log.warn("Error during overlay rendering", ex);
}
notifier.processFlash(graphics2d);
// Draw clientUI overlays
clientUi.paintOverlays(graphics2d);
graphics2d.dispose();
if (client.isGpu())
{
// processDrawComplete gets called on GPU by the gpu plugin at the end of its
// drawing cycle, which is later on.
return;
}
// Stretch the game image if the user has that enabled
if (client.isStretchedEnabled())
{
GraphicsConfiguration gc = clientUi.getGraphicsConfiguration();
Dimension stretchedDimensions = client.getStretchedDimensions();
if (lastStretchedDimensions == null || !lastStretchedDimensions.equals(stretchedDimensions)
|| (stretchedImage != null && stretchedImage.validate(gc) == VolatileImage.IMAGE_INCOMPATIBLE))
{
/*
Reuse the resulting image instance to avoid creating an extreme amount of objects
*/
stretchedImage = gc.createCompatibleVolatileImage(stretchedDimensions.width, stretchedDimensions.height);
if (stretchedGraphics != null)
{
stretchedGraphics.dispose();
}
stretchedGraphics = (Graphics2D) stretchedImage.getGraphics();
lastStretchedDimensions = stretchedDimensions;
/*
Fill Canvas before drawing stretched image to prevent artifacts.
*/
graphics.setColor(Color.BLACK);
graphics.fillRect(0, 0, client.getCanvas().getWidth(), client.getCanvas().getHeight());
}
stretchedGraphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
client.isStretchedFast()
? RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR
: RenderingHints.VALUE_INTERPOLATION_BILINEAR);
stretchedGraphics.drawImage(image, 0, 0, stretchedDimensions.width, stretchedDimensions.height, null);
finalImage = stretchedImage;
}
else
{
finalImage = image;
}
// Draw the image onto the game canvas
graphics.drawImage(finalImage, 0, 0, client.getCanvas());
// finalImage is backed by the client buffer which will change soon. make a copy
// so that callbacks can safely use it later from threads.
drawManager.processDrawComplete(() -> copy(finalImage));
}
/**
* Copy an image
*
* @param src
* @return
*/
private static Image copy(Image src)
{
final int width = src.getWidth(null);
final int height = src.getHeight(null);
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics graphics = image.getGraphics();
graphics.drawImage(src, 0, 0, width, height, null);
graphics.dispose();
return image;
}
@Override
public void drawScene()
{
MainBufferProvider bufferProvider = (MainBufferProvider) client.getBufferProvider();
BufferedImage image = (BufferedImage) bufferProvider.getImage();
Graphics2D graphics2d = image.createGraphics();
// Update selected scene tile
if (!client.isMenuOpen())
{
Point p = client.getMouseCanvasPosition();
p = new Point(
p.getX() - client.getViewportXOffset(),
p.getY() - client.getViewportYOffset());
client.setCheckClick(true);
client.setMouseCanvasHoverPosition(p);
}
try
{
renderer.render(graphics2d, OverlayLayer.ABOVE_SCENE);
}
catch (Exception ex)
{
log.warn("Error during overlay rendering", ex);
}
finally
{
graphics2d.dispose();
}
}
@Override
public void drawAboveOverheads()
{
MainBufferProvider bufferProvider = (MainBufferProvider) client.getBufferProvider();
BufferedImage image = (BufferedImage) bufferProvider.getImage();
Graphics2D graphics2d = image.createGraphics();
try
{
renderer.render(graphics2d, OverlayLayer.UNDER_WIDGETS);
}
catch (Exception ex)
{
log.warn("Error during overlay rendering", ex);
}
finally
{
graphics2d.dispose();
}
}
public static void drawAfterWidgets()
{
MainBufferProvider bufferProvider = (MainBufferProvider) client.getBufferProvider();
BufferedImage image = (BufferedImage) bufferProvider.getImage();
Graphics2D graphics2d = image.createGraphics();
try
{
renderer.render(graphics2d, OverlayLayer.ABOVE_WIDGETS);
renderer.render(graphics2d, OverlayLayer.ABOVE_MAP);
}
catch (Exception ex)
{
log.warn("Error during overlay rendering", ex);
}
finally
{
graphics2d.dispose();
}
// WidgetItemOverlays render at ABOVE_WIDGETS, reset widget item
// list for next frame.
overlayManager.getItemWidgets().clear();
}
@Override
public void updateNpcs()
{
// The NPC update event seem to run every server tick,
// but having the game tick event after all packets
// have been processed is typically more useful.
shouldProcessGameTick = true;
// Replay deferred events, otherwise if two npc
// update packets get processed in one frame, a
// despawn event could be published prior to the
// spawn event, which is deferred
deferredEventBus.replay();
}
public static void renderDraw(Renderable renderable, 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(renderable, orientation, pitchSin, pitchCos, yawSin, yawCos, x, y, z, hash);
}
else
{
renderable.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)
{
// Empty bank item
if (widgetItem.getId() != NullItemID.NULL_6512)
{
overlayManager.getItemWidgets().add(widgetItem);
}
}
public static boolean drawMenu()
{
BeforeMenuRender event = new BeforeMenuRender();
client.getCallbacks().post(event);
return event.isConsumed();
}
}

View File

@@ -1,52 +0,0 @@
/*
* Copyright (c) 2017, Tomas Slusny <slusnucky@gmail.com>
* 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.chat;
import java.awt.Color;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(exclude = {"color", "isDefault"})
public class ChatColor
{
private ChatColorType type;
private Color color;
private boolean transparent;
private boolean isDefault;
public ChatColor(ChatColorType type, Color color, boolean transparent)
{
this(type, color, transparent, false);
}
public ChatColor(ChatColorType type, Color color, boolean transparent, boolean isDefault)
{
this.type = type;
this.color = color;
this.transparent = transparent;
this.isDefault = isDefault;
}
}

View File

@@ -1,31 +0,0 @@
/*
* Copyright (c) 2017, Tomas Slusny <slusnucky@gmail.com>
* 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.chat;
public enum ChatColorType
{
NORMAL,
HIGHLIGHT
}

View File

@@ -1,42 +0,0 @@
/*
* Copyright (c) 2019, Adam <Adam@sigterm.info>
* 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.chat;
import java.util.function.BiConsumer;
import java.util.function.BiPredicate;
import lombok.AllArgsConstructor;
import lombok.Getter;
import net.runelite.api.events.ChatMessage;
import net.runelite.client.events.ChatInput;
@AllArgsConstructor
@Getter
class ChatCommand
{
private final String name;
private boolean async;
private final BiConsumer<ChatMessage, String> execute;
private final BiPredicate<ChatInput, String> input;
}

View File

@@ -1,198 +0,0 @@
/*
* Copyright (c) 2019, Adam <Adam@sigterm.info>
* 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.chat;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.BiConsumer;
import java.util.function.BiPredicate;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.events.ChatMessage;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.ChatInput;
import net.runelite.client.events.ChatboxInput;
import net.runelite.client.events.PrivateMessageInput;
@Singleton
@Slf4j
public class ChatCommandManager implements ChatboxInputListener
{
private final Map<String, ChatCommand> commands = new HashMap<>();
private final Client client;
private final ScheduledExecutorService scheduledExecutorService;
@Inject
private ChatCommandManager(EventBus eventBus, CommandManager commandManager, Client client, ScheduledExecutorService scheduledExecutorService)
{
this.client = client;
this.scheduledExecutorService = scheduledExecutorService;
eventBus.register(this);
commandManager.register(this);
}
public void registerCommand(String command, BiConsumer<ChatMessage, String> execute)
{
registerCommand(command, execute, null);
}
public void registerCommand(String command, BiConsumer<ChatMessage, String> execute, BiPredicate<ChatInput, String> input)
{
commands.put(command.toLowerCase(), new ChatCommand(command, false, execute, input));
}
public void registerCommandAsync(String command, BiConsumer<ChatMessage, String> execute)
{
registerCommandAsync(command, execute, null);
}
public void registerCommandAsync(String command, BiConsumer<ChatMessage, String> execute, BiPredicate<ChatInput, String> input)
{
commands.put(command.toLowerCase(), new ChatCommand(command, true, execute, input));
}
public void unregisterCommand(String command)
{
commands.remove(command.toLowerCase());
}
@Subscribe
public void onChatMessage(ChatMessage chatMessage)
{
if (client.getGameState() != GameState.LOGGED_IN)
{
return;
}
switch (chatMessage.getType())
{
case PUBLICCHAT:
case MODCHAT:
case FRIENDSCHAT:
case PRIVATECHAT:
case MODPRIVATECHAT:
case PRIVATECHATOUT:
break;
default:
return;
}
String message = chatMessage.getMessage();
String command = extractCommand(message);
if (command == null)
{
return;
}
ChatCommand chatCommand = commands.get(command.toLowerCase());
if (chatCommand == null)
{
return;
}
if (chatCommand.isAsync())
{
scheduledExecutorService.execute(() -> chatCommand.getExecute().accept(chatMessage, message));
}
else
{
chatCommand.getExecute().accept(chatMessage, message);
}
}
@Override
public boolean onChatboxInput(ChatboxInput chatboxInput)
{
String message = chatboxInput.getValue();
if (message.startsWith("/"))
{
message = message.substring(1); // clan chat input
}
String command = extractCommand(message);
if (command == null)
{
return false;
}
ChatCommand chatCommand = commands.get(command.toLowerCase());
if (chatCommand == null)
{
return false;
}
BiPredicate<ChatInput, String> input = chatCommand.getInput();
if (input == null)
{
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<ChatInput, String> input = chatCommand.getInput();
if (input == null)
{
return false;
}
return input.test(privateMessageInput, message);
}
private static String extractCommand(String message)
{
int idx = message.indexOf(' ');
if (idx == -1)
{
return message;
}
return message.substring(0, idx);
}
}

View File

@@ -1,63 +0,0 @@
/*
* Copyright (c) 2017, Tomas Slusny <slusnucky@gmail.com>
* 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.chat;
import java.awt.Color;
import net.runelite.client.util.ColorUtil;
import net.runelite.client.util.Text;
public class ChatMessageBuilder
{
private final StringBuilder builder = new StringBuilder();
public ChatMessageBuilder append(final ChatColorType type)
{
builder.append("<col").append(type.name()).append(">");
return this;
}
public ChatMessageBuilder append(final Color color, final String message)
{
builder.append(ColorUtil.wrapWithColorTag(message, color));
return this;
}
public ChatMessageBuilder append(final String message)
{
builder.append(Text.escapeJagex(message));
return this;
}
public ChatMessageBuilder img(int imageId)
{
builder.append("<img=").append(imageId).append('>');
return this;
}
public String build()
{
return builder.toString();
}
}

View File

@@ -1,636 +0,0 @@
/*
* Copyright (c) 2017, Tomas Slusny <slusnucky@gmail.com>
* 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.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;
import java.util.Collection;
import java.util.Objects;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicReference;
import javax.inject.Inject;
import javax.inject.Singleton;
import net.runelite.api.ChatLineBuffer;
import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
import net.runelite.api.MessageNode;
import net.runelite.api.Player;
import net.runelite.api.Varbits;
import net.runelite.api.events.ChatMessage;
import net.runelite.api.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.Subscribe;
import net.runelite.client.ui.JagexColors;
import net.runelite.client.util.ColorUtil;
@Singleton
public class ChatMessageManager
{
private static final Set<Integer> TUTORIAL_ISLAND_REGIONS = ImmutableSet.of(12336, 12335, 12592, 12080, 12079, 12436);
private final Multimap<ChatMessageType, ChatColor> colorCache = HashMultimap.create();
private final Client client;
private final ChatColorConfig chatColorConfig;
private final ClientThread clientThread;
private int transparencyVarbit = -1;
private final Queue<QueuedMessage> queuedMessages = new ConcurrentLinkedQueue<>();
@Inject
private ChatMessageManager(
Client client,
ChatColorConfig chatColorConfig,
ClientThread clientThread)
{
this.client = client;
this.chatColorConfig = chatColorConfig;
this.clientThread = clientThread;
}
@Subscribe
public void onVarbitChanged(VarbitChanged event)
{
int setting = client.getVar(Varbits.TRANSPARENT_CHATBOX);
if (transparencyVarbit != setting)
{
transparencyVarbit = setting;
refreshAll();
}
}
@Subscribe
public void onResizeableChanged(ResizeableChanged event)
{
refreshAll();
}
@Subscribe
public void onConfigChanged(ConfigChanged event)
{
if (event.getGroup().equals("textrecolor"))
{
loadColors();
clientThread.invokeLater(this::refreshAll);
}
}
@Subscribe
public void onChatMessage(ChatMessage chatMessage)
{
MessageNode messageNode = chatMessage.getMessageNode();
ChatMessageType chatMessageType = chatMessage.getType();
boolean isChatboxTransparent = client.isResized() && client.getVar(Varbits.TRANSPARENT_CHATBOX) == 1;
Color usernameColor = null;
Color senderColor = null;
switch (chatMessageType)
{
case MODPRIVATECHAT:
case PRIVATECHAT:
case PRIVATECHATOUT:
usernameColor = isChatboxTransparent ? chatColorConfig.transparentPrivateUsernames() : chatColorConfig.opaquePrivateUsernames();
break;
case TRADEREQ:
case AUTOTYPER:
case PUBLICCHAT:
case MODCHAT:
{
boolean isFriend = client.isFriended(chatMessage.getName(), true) && !client.getLocalPlayer().getName().equals(chatMessage.getName());
if (isFriend)
{
usernameColor = isChatboxTransparent ? chatColorConfig.transparentPublicFriendUsernames() : chatColorConfig.opaquePublicFriendUsernames();
}
if (usernameColor == null)
{
usernameColor = isChatboxTransparent ? chatColorConfig.transparentUsername() : chatColorConfig.opaqueUsername();
}
break;
}
case FRIENDSCHAT:
usernameColor = isChatboxTransparent ? chatColorConfig.transparentClanUsernames() : chatColorConfig.opaqueClanUsernames();
break;
}
senderColor = isChatboxTransparent ? chatColorConfig.transparentClanChannelName() : chatColorConfig.opaqueClanChannelName();
if (usernameColor != null)
{
messageNode.setName(ColorUtil.wrapWithColorTag(messageNode.getName(), usernameColor));
}
String sender = chatMessage.getSender();
if (senderColor != null && !Strings.isNullOrEmpty(sender))
{
messageNode.setSender(ColorUtil.wrapWithColorTag(sender, senderColor));
}
final Collection<ChatColor> chatColors = colorCache.get(chatMessageType);
for (ChatColor chatColor : chatColors)
{
if (chatColor.isTransparent() != isChatboxTransparent || chatColor.getType() != ChatColorType.NORMAL || chatColor.isDefault())
{
continue;
}
messageNode.setValue(ColorUtil.wrapWithColorTag(messageNode.getValue(), chatColor.getColor()));
break;
}
}
@Subscribe
public void onScriptCallbackEvent(ScriptCallbackEvent scriptCallbackEvent)
{
final String eventName = scriptCallbackEvent.getEventName();
switch (eventName)
{
case "privateChatFrom":
case "privateChatTo":
case "privateChatSplitFrom":
case "privateChatSplitTo":
break;
default:
return;
}
boolean isChatboxTransparent = client.isResized() && client.getVar(Varbits.TRANSPARENT_CHATBOX) == 1;
Color usernameColor = isChatboxTransparent ? chatColorConfig.transparentPrivateUsernames() : chatColorConfig.opaquePrivateUsernames();
if (usernameColor == null)
{
return;
}
final String[] stringStack = client.getStringStack();
final int stringStackSize = client.getStringStackSize();
// Stack is: To/From playername :
String toFrom = stringStack[stringStackSize - 3];
stringStack[stringStackSize - 3] = ColorUtil.prependColorTag(toFrom, usernameColor);
}
private static Color getDefaultColor(ChatMessageType type, boolean transparent)
{
if (!transparent)
{
switch (type)
{
case PUBLICCHAT:
case MODCHAT:
return JagexColors.CHAT_PUBLIC_TEXT_OPAQUE_BACKGROUND;
case PRIVATECHATOUT:
case MODPRIVATECHAT:
case PRIVATECHAT:
return JagexColors.CHAT_PRIVATE_MESSAGE_TEXT_OPAQUE_BACKGROUND;
case FRIENDSCHAT:
return JagexColors.CHAT_CLAN_TEXT_OPAQUE_BACKGROUND;
case ITEM_EXAMINE:
case OBJECT_EXAMINE:
case NPC_EXAMINE:
case CONSOLE:
return JagexColors.CHAT_GAME_EXAMINE_TEXT_OPAQUE_BACKGROUND;
}
}
else
{
switch (type)
{
case PUBLICCHAT:
case MODCHAT:
return JagexColors.CHAT_PUBLIC_TEXT_TRANSPARENT_BACKGROUND;
case PRIVATECHATOUT:
case MODPRIVATECHAT:
case PRIVATECHAT:
return JagexColors.CHAT_PRIVATE_MESSAGE_TEXT_TRANSPARENT_BACKGROUND;
case FRIENDSCHAT:
return JagexColors.CHAT_CLAN_TEXT_TRANSPARENT_BACKGROUND;
case ITEM_EXAMINE:
case OBJECT_EXAMINE:
case NPC_EXAMINE:
case CONSOLE:
return JagexColors.CHAT_GAME_EXAMINE_TEXT_TRANSPARENT_BACKGROUND;
}
}
return null;
}
/**
* Load all configured colors
*/
public void loadColors()
{
colorCache.clear();
// Apply defaults
for (ChatMessageType chatMessageType : ChatMessageType.values())
{
Color defaultTransparent = getDefaultColor(chatMessageType, true);
if (defaultTransparent != null)
{
cacheColor(new ChatColor(ChatColorType.NORMAL, defaultTransparent, true, true), chatMessageType);
}
Color defaultOpaque = getDefaultColor(chatMessageType, false);
if (defaultOpaque != null)
{
cacheColor(new ChatColor(ChatColorType.NORMAL, defaultOpaque, false, true), chatMessageType);
}
}
if (chatColorConfig.opaquePublicChat() != null)
{
cacheColor(new ChatColor(ChatColorType.NORMAL, chatColorConfig.opaquePublicChat(), false),
ChatMessageType.PUBLICCHAT);
cacheColor(new ChatColor(ChatColorType.NORMAL, chatColorConfig.opaquePublicChat(), false),
ChatMessageType.MODCHAT);
}
if (chatColorConfig.opaquePublicChatHighlight() != null)
{
cacheColor(new ChatColor(ChatColorType.HIGHLIGHT, chatColorConfig.opaquePublicChatHighlight(), false),
ChatMessageType.PUBLICCHAT);
cacheColor(new ChatColor(ChatColorType.HIGHLIGHT, chatColorConfig.opaquePublicChatHighlight(), false),
ChatMessageType.MODCHAT);
}
if (chatColorConfig.opaquePrivateMessageSent() != null)
{
cacheColor(new ChatColor(ChatColorType.NORMAL, chatColorConfig.opaquePrivateMessageSent(), false),
ChatMessageType.PRIVATECHATOUT);
}
if (chatColorConfig.opaquePrivateMessageSentHighlight() != null)
{
cacheColor(new ChatColor(ChatColorType.HIGHLIGHT, chatColorConfig.opaquePrivateMessageSentHighlight(), false),
ChatMessageType.PRIVATECHATOUT);
}
if (chatColorConfig.opaquePrivateMessageReceived() != null)
{
cacheColor(new ChatColor(ChatColorType.NORMAL, chatColorConfig.opaquePrivateMessageReceived(), false),
ChatMessageType.PRIVATECHAT);
cacheColor(new ChatColor(ChatColorType.NORMAL, chatColorConfig.opaquePrivateMessageReceived(), false),
ChatMessageType.MODPRIVATECHAT);
}
if (chatColorConfig.opaquePrivateMessageReceivedHighlight() != null)
{
cacheColor(new ChatColor(ChatColorType.HIGHLIGHT, chatColorConfig.opaquePrivateMessageReceivedHighlight(), false),
ChatMessageType.PRIVATECHAT);
cacheColor(new ChatColor(ChatColorType.HIGHLIGHT, chatColorConfig.opaquePrivateMessageReceivedHighlight(), false),
ChatMessageType.MODPRIVATECHAT);
}
if (chatColorConfig.opaqueClanChatInfo() != null)
{
cacheColor(new ChatColor(ChatColorType.NORMAL, chatColorConfig.opaqueClanChatInfo(), false),
ChatMessageType.FRIENDSCHATNOTIFICATION);
}
if (chatColorConfig.opaqueClanChatInfoHighlight() != null)
{
cacheColor(new ChatColor(ChatColorType.HIGHLIGHT, chatColorConfig.opaqueClanChatInfoHighlight(), false),
ChatMessageType.FRIENDSCHATNOTIFICATION);
}
if (chatColorConfig.opaqueClanChatMessage() != null)
{
cacheColor(new ChatColor(ChatColorType.NORMAL, chatColorConfig.opaqueClanChatMessage(), false),
ChatMessageType.FRIENDSCHAT);
}
if (chatColorConfig.opaqueClanChatMessageHighlight() != null)
{
cacheColor(new ChatColor(ChatColorType.HIGHLIGHT, chatColorConfig.opaqueClanChatMessageHighlight(), false),
ChatMessageType.FRIENDSCHAT);
}
if (chatColorConfig.opaqueAutochatMessage() != null)
{
cacheColor(new ChatColor(ChatColorType.NORMAL, chatColorConfig.opaqueAutochatMessage(), false),
ChatMessageType.AUTOTYPER);
}
if (chatColorConfig.opaqueAutochatMessageHighlight() != null)
{
cacheColor(new ChatColor(ChatColorType.HIGHLIGHT, chatColorConfig.opaqueAutochatMessageHighlight(), false),
ChatMessageType.AUTOTYPER);
}
if (chatColorConfig.opaqueTradeChatMessage() != null)
{
cacheColor(new ChatColor(ChatColorType.NORMAL, chatColorConfig.opaqueTradeChatMessage(), false),
ChatMessageType.TRADEREQ);
}
if (chatColorConfig.opaqueTradeChatMessageHighlight() != null)
{
cacheColor(new ChatColor(ChatColorType.HIGHLIGHT, chatColorConfig.opaqueTradeChatMessageHighlight(), false),
ChatMessageType.TRADEREQ);
}
if (chatColorConfig.opaqueServerMessage() != null)
{
cacheColor(new ChatColor(ChatColorType.NORMAL, chatColorConfig.opaqueServerMessage(), false),
ChatMessageType.GAMEMESSAGE);
}
if (chatColorConfig.opaqueServerMessageHighlight() != null)
{
cacheColor(new ChatColor(ChatColorType.HIGHLIGHT, chatColorConfig.opaqueServerMessageHighlight(), false),
ChatMessageType.GAMEMESSAGE);
}
if (chatColorConfig.opaqueGameMessage() != null)
{
cacheColor(new ChatColor(ChatColorType.NORMAL, chatColorConfig.opaqueGameMessage(), false),
ChatMessageType.CONSOLE);
}
if (chatColorConfig.opaqueGameMessageHighlight() != null)
{
cacheColor(new ChatColor(ChatColorType.HIGHLIGHT, chatColorConfig.opaqueGameMessageHighlight(), false),
ChatMessageType.CONSOLE);
}
if (chatColorConfig.opaqueExamine() != null)
{
cacheColor(new ChatColor(ChatColorType.NORMAL, chatColorConfig.opaqueExamine(), false),
ChatMessageType.OBJECT_EXAMINE);
cacheColor(new ChatColor(ChatColorType.NORMAL, chatColorConfig.opaqueExamine(), false),
ChatMessageType.NPC_EXAMINE);
cacheColor(new ChatColor(ChatColorType.NORMAL, chatColorConfig.opaqueExamine(), false),
ChatMessageType.ITEM_EXAMINE);
}
if (chatColorConfig.opaqueExamineHighlight() != null)
{
cacheColor(new ChatColor(ChatColorType.HIGHLIGHT, chatColorConfig.opaqueExamineHighlight(), false),
ChatMessageType.OBJECT_EXAMINE);
cacheColor(new ChatColor(ChatColorType.HIGHLIGHT, chatColorConfig.opaqueExamineHighlight(), false),
ChatMessageType.NPC_EXAMINE);
cacheColor(new ChatColor(ChatColorType.HIGHLIGHT, chatColorConfig.opaqueExamineHighlight(), false),
ChatMessageType.ITEM_EXAMINE);
}
if (chatColorConfig.opaqueFiltered() != null)
{
cacheColor(new ChatColor(ChatColorType.NORMAL, chatColorConfig.opaqueFiltered(), false),
ChatMessageType.SPAM);
}
if (chatColorConfig.opaqueFilteredHighlight() != null)
{
cacheColor(new ChatColor(ChatColorType.HIGHLIGHT, chatColorConfig.opaqueFilteredHighlight(), false),
ChatMessageType.SPAM);
}
//Transparent Chat Colours
if (chatColorConfig.transparentPublicChat() != null)
{
cacheColor(new ChatColor(ChatColorType.NORMAL, chatColorConfig.transparentPublicChat(), true),
ChatMessageType.PUBLICCHAT);
cacheColor(new ChatColor(ChatColorType.NORMAL, chatColorConfig.transparentPublicChat(), true),
ChatMessageType.MODCHAT);
}
if (chatColorConfig.transparentPublicChatHighlight() != null)
{
cacheColor(new ChatColor(ChatColorType.HIGHLIGHT, chatColorConfig.transparentPublicChatHighlight(), true),
ChatMessageType.PUBLICCHAT);
cacheColor(new ChatColor(ChatColorType.HIGHLIGHT, chatColorConfig.transparentPublicChatHighlight(), true),
ChatMessageType.MODCHAT);
}
if (chatColorConfig.transparentPrivateMessageSent() != null)
{
cacheColor(new ChatColor(ChatColorType.NORMAL, chatColorConfig.transparentPrivateMessageSent(), true),
ChatMessageType.PRIVATECHATOUT);
}
if (chatColorConfig.transparentPrivateMessageSentHighlight() != null)
{
cacheColor(new ChatColor(ChatColorType.HIGHLIGHT, chatColorConfig.transparentPrivateMessageSentHighlight(), true),
ChatMessageType.PRIVATECHATOUT);
}
if (chatColorConfig.transparentPrivateMessageReceived() != null)
{
cacheColor(new ChatColor(ChatColorType.NORMAL, chatColorConfig.transparentPrivateMessageReceived(), true),
ChatMessageType.PRIVATECHAT);
cacheColor(new ChatColor(ChatColorType.NORMAL, chatColorConfig.transparentPrivateMessageReceived(), true),
ChatMessageType.MODPRIVATECHAT);
}
if (chatColorConfig.transparentPrivateMessageReceivedHighlight() != null)
{
cacheColor(new ChatColor(ChatColorType.HIGHLIGHT, chatColorConfig.transparentPrivateMessageReceivedHighlight(), true),
ChatMessageType.PRIVATECHAT);
cacheColor(new ChatColor(ChatColorType.HIGHLIGHT, chatColorConfig.transparentPrivateMessageReceivedHighlight(), true),
ChatMessageType.MODPRIVATECHAT);
}
if (chatColorConfig.transparentClanChatInfo() != null)
{
cacheColor(new ChatColor(ChatColorType.NORMAL, chatColorConfig.transparentClanChatInfo(), true),
ChatMessageType.FRIENDSCHATNOTIFICATION);
}
if (chatColorConfig.transparentClanChatInfoHighlight() != null)
{
cacheColor(new ChatColor(ChatColorType.HIGHLIGHT, chatColorConfig.transparentClanChatInfoHighlight(), true),
ChatMessageType.FRIENDSCHATNOTIFICATION);
}
if (chatColorConfig.transparentClanChatMessage() != null)
{
cacheColor(new ChatColor(ChatColorType.NORMAL, chatColorConfig.transparentClanChatMessage(), true),
ChatMessageType.FRIENDSCHAT);
}
if (chatColorConfig.transparentClanChatMessageHighlight() != null)
{
cacheColor(new ChatColor(ChatColorType.HIGHLIGHT, chatColorConfig.transparentClanChatMessageHighlight(), true),
ChatMessageType.FRIENDSCHAT);
}
if (chatColorConfig.transparentAutochatMessage() != null)
{
cacheColor(new ChatColor(ChatColorType.NORMAL, chatColorConfig.transparentAutochatMessage(), true),
ChatMessageType.AUTOTYPER);
}
if (chatColorConfig.transparentAutochatMessageHighlight() != null)
{
cacheColor(new ChatColor(ChatColorType.HIGHLIGHT, chatColorConfig.transparentAutochatMessageHighlight(), true),
ChatMessageType.AUTOTYPER);
}
if (chatColorConfig.transparentTradeChatMessage() != null)
{
cacheColor(new ChatColor(ChatColorType.NORMAL, chatColorConfig.transparentTradeChatMessage(), true),
ChatMessageType.TRADEREQ);
}
if (chatColorConfig.transparentTradeChatMessageHighlight() != null)
{
cacheColor(new ChatColor(ChatColorType.HIGHLIGHT, chatColorConfig.transparentTradeChatMessageHighlight(), true),
ChatMessageType.TRADEREQ);
}
if (chatColorConfig.transparentServerMessage() != null)
{
cacheColor(new ChatColor(ChatColorType.NORMAL, chatColorConfig.transparentServerMessage(), true),
ChatMessageType.GAMEMESSAGE);
}
if (chatColorConfig.transparentServerMessageHighlight() != null)
{
cacheColor(new ChatColor(ChatColorType.HIGHLIGHT, chatColorConfig.transparentServerMessageHighlight(), true),
ChatMessageType.GAMEMESSAGE);
}
if (chatColorConfig.transparentGameMessage() != null)
{
cacheColor(new ChatColor(ChatColorType.NORMAL, chatColorConfig.transparentGameMessage(), true),
ChatMessageType.CONSOLE);
}
if (chatColorConfig.transparentGameMessageHighlight() != null)
{
cacheColor(new ChatColor(ChatColorType.HIGHLIGHT, chatColorConfig.transparentGameMessageHighlight(), true),
ChatMessageType.CONSOLE);
}
if (chatColorConfig.transparentExamine() != null)
{
cacheColor(new ChatColor(ChatColorType.NORMAL, chatColorConfig.transparentExamine(), true),
ChatMessageType.OBJECT_EXAMINE);
cacheColor(new ChatColor(ChatColorType.NORMAL, chatColorConfig.transparentExamine(), true),
ChatMessageType.NPC_EXAMINE);
cacheColor(new ChatColor(ChatColorType.NORMAL, chatColorConfig.transparentExamine(), true),
ChatMessageType.ITEM_EXAMINE);
}
if (chatColorConfig.transparentExamineHighlight() != null)
{
cacheColor(new ChatColor(ChatColorType.HIGHLIGHT, chatColorConfig.transparentExamineHighlight(), true),
ChatMessageType.OBJECT_EXAMINE);
cacheColor(new ChatColor(ChatColorType.HIGHLIGHT, chatColorConfig.transparentExamineHighlight(), true),
ChatMessageType.NPC_EXAMINE);
cacheColor(new ChatColor(ChatColorType.HIGHLIGHT, chatColorConfig.transparentExamineHighlight(), true),
ChatMessageType.ITEM_EXAMINE);
}
if (chatColorConfig.transparentFiltered() != null)
{
cacheColor(new ChatColor(ChatColorType.NORMAL, chatColorConfig.transparentFiltered(), true),
ChatMessageType.SPAM);
}
if (chatColorConfig.transparentFilteredHighlight() != null)
{
cacheColor(new ChatColor(ChatColorType.HIGHLIGHT, chatColorConfig.transparentFilteredHighlight(), true),
ChatMessageType.SPAM);
}
}
private void cacheColor(final ChatColor chatColor, final ChatMessageType... types)
{
for (ChatMessageType chatMessageType : types)
{
// color is excluded from equals/hashCode on ChatColor
colorCache.remove(chatMessageType, chatColor);
colorCache.put(chatMessageType, chatColor);
}
}
public void queue(QueuedMessage message)
{
queuedMessages.add(message);
}
public void process()
{
if (!queuedMessages.isEmpty())
{
try
{
queuedMessages.forEach(this::add);
}
finally
{
queuedMessages.clear();
}
}
}
private void add(QueuedMessage message)
{
// Do not send message if the player is on tutorial island
final Player player = client.getLocalPlayer();
if (player != null && TUTORIAL_ISLAND_REGIONS.contains(player.getWorldLocation().getRegionID()))
{
return;
}
// this updates chat cycle
client.addChatMessage(
message.getType(),
MoreObjects.firstNonNull(message.getName(), ""),
MoreObjects.firstNonNull(message.getValue(), message.getRuneLiteFormattedMessage()),
message.getSender());
// Get last message from line buffer (the one we just added)
final ChatLineBuffer chatLineBuffer = client.getChatLineMap().get(message.getType().getType());
final MessageNode[] lines = chatLineBuffer.getLines();
final MessageNode line = lines[0];
// Update the message with RuneLite additions
line.setRuneLiteFormatMessage(message.getRuneLiteFormattedMessage());
line.setTimestamp(message.getTimestamp());
update(line);
}
public void update(final MessageNode target)
{
if (Strings.isNullOrEmpty(target.getRuneLiteFormatMessage()))
{
return;
}
final boolean transparent = client.isResized() && transparencyVarbit != 0;
final Collection<ChatColor> chatColors = colorCache.get(target.getType());
// If we do not have any colors cached, simply set clean message
if (chatColors == null || chatColors.isEmpty())
{
target.setValue(target.getRuneLiteFormatMessage());
return;
}
target.setValue(recolorMessage(transparent, target.getRuneLiteFormatMessage(), target.getType()));
}
private String recolorMessage(boolean transparent, String message, ChatMessageType messageType)
{
final Collection<ChatColor> chatColors = colorCache.get(messageType);
final AtomicReference<String> resultMessage = new AtomicReference<>(message);
// Replace custom formatting with actual colors
chatColors.stream()
.filter(chatColor -> chatColor.isTransparent() == transparent)
.forEach(chatColor ->
resultMessage.getAndUpdate(oldMessage -> oldMessage.replaceAll(
"<col" + chatColor.getType().name() + ">",
ColorUtil.colorTag(chatColor.getColor()))));
return resultMessage.get();
}
private void refreshAll()
{
client.getChatLineMap().values().stream()
.filter(Objects::nonNull)
.flatMap(clb -> Arrays.stream(clb.getLines()))
.filter(Objects::nonNull)
.forEach(this::update);
client.refreshChat();
}
}

View File

@@ -1,35 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.chat;
import net.runelite.client.events.ChatboxInput;
import net.runelite.client.events.PrivateMessageInput;
public interface ChatboxInputListener
{
boolean onChatboxInput(ChatboxInput chatboxInput);
boolean onPrivateMessageInput(PrivateMessageInput privateMessageInput);
}

View File

@@ -1,217 +0,0 @@
/*
* Copyright (c) 2018, Kamiel
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.chat;
import java.util.ArrayList;
import java.util.Arrays;
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.ScriptID;
import net.runelite.api.VarClientStr;
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;
@Slf4j
@Singleton
public class CommandManager
{
private static final String RUNELITE_COMMAND = "runeliteCommand";
private static final String CHATBOX_INPUT = "chatboxInput";
private static final String PRIVMATE_MESSAGE = "privateMessage";
private final Client client;
private final EventBus eventBus;
private final ClientThread clientThread;
private boolean sending;
private final List<ChatboxInputListener> chatboxInputListenerList = new ArrayList<>();
@Inject
private CommandManager(Client client, EventBus eventBus, ClientThread clientThread)
{
this.client = client;
this.eventBus = eventBus;
this.clientThread = clientThread;
}
public void register(ChatboxInputListener chatboxInputListener)
{
chatboxInputListenerList.add(chatboxInputListener);
}
public void unregister(ChatboxInputListener chatboxInputListener)
{
chatboxInputListenerList.remove(chatboxInputListener);
}
@Subscribe
private void onScriptCallbackEvent(ScriptCallbackEvent event)
{
if (sending)
{
return;
}
switch (event.getEventName())
{
case RUNELITE_COMMAND:
runCommand();
break;
case CHATBOX_INPUT:
handleInput(event);
break;
case PRIVMATE_MESSAGE:
handlePrivateMessage(event);
break;
}
}
private void runCommand()
{
String typedText = client.getVar(VarClientStr.CHATBOX_TYPED_TEXT).substring(2); // strip ::
log.debug("Command: {}", typedText);
String[] split = typedText.split(" ");
// Fixes ArrayIndexOutOfBounds when typing ":: "
if (split.length == 0)
{
return;
}
String command = split[0];
String[] args = Arrays.copyOfRange(split, 1, split.length);
CommandExecuted commandExecuted = new CommandExecuted(command, args);
eventBus.post(commandExecuted);
}
private void handleInput(ScriptCallbackEvent event)
{
final String[] stringStack = client.getStringStack();
final int[] intStack = client.getIntStack();
int stringStackCount = client.getStringStackSize();
int intStackCount = client.getIntStackSize();
final String typedText = stringStack[stringStackCount - 1];
final int chatType = intStack[intStackCount - 1];
ChatboxInput chatboxInput = new ChatboxInput(typedText, chatType)
{
private boolean resumed;
@Override
public void resume()
{
if (resumed)
{
return;
}
resumed = true;
clientThread.invoke(() -> sendChatboxInput(chatType, typedText));
}
};
boolean stop = false;
for (ChatboxInputListener chatboxInputListener : chatboxInputListenerList)
{
stop |= chatboxInputListener.onChatboxInput(chatboxInput);
}
if (stop)
{
// input was blocked.
stringStack[stringStackCount - 1] = ""; // prevent script from sending
}
}
private void handlePrivateMessage(ScriptCallbackEvent event)
{
final String[] stringStack = client.getStringStack();
final int[] intStack = client.getIntStack();
int stringStackCount = client.getStringStackSize();
int intStackCount = client.getIntStackSize();
final String target = stringStack[stringStackCount - 2];
final String message = stringStack[stringStackCount - 1];
PrivateMessageInput privateMessageInput = new PrivateMessageInput(target, message)
{
private boolean resumed;
@Override
public void resume()
{
if (resumed)
{
return;
}
resumed = true;
clientThread.invoke(() -> sendPrivmsg(target, message));
}
};
boolean stop = false;
for (ChatboxInputListener chatboxInputListener : chatboxInputListenerList)
{
stop |= chatboxInputListener.onPrivateMessageInput(privateMessageInput);
}
if (stop)
{
intStack[intStackCount - 1] = 1;
client.setStringStackSize(stringStackCount - 2); // remove both target and message
}
}
private void sendChatboxInput(int chatType, String input)
{
sending = true;
try
{
client.runScript(ScriptID.CHATBOX_INPUT, chatType, input);
}
finally
{
sending = false;
}
}
private void sendPrivmsg(String target, String message)
{
client.runScript(ScriptID.PRIVMSG, target, message);
}
}

View File

@@ -1,43 +0,0 @@
/*
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
* 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.chat;
import lombok.Builder;
import lombok.Data;
import lombok.NonNull;
import net.runelite.api.ChatMessageType;
@Data
@Builder
public class QueuedMessage
{
@NonNull
private final ChatMessageType type;
private final String value;
private String name;
private String sender;
private String runeLiteFormattedMessage;
private int timestamp;
}

View File

@@ -1,42 +0,0 @@
/*
* Copyright (c) 2018, Ron Young <https://github.com/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.config;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Used with ConfigItem, determines if to use alpha slider on colors
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface Alpha
{
}

View File

@@ -1,512 +0,0 @@
/*
* Copyright (c) 2018, Hydrox6 <ikada@protonmail.ch>
* 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 net.runelite.client.ui.JagexColors;
@ConfigGroup("textrecolor")
public interface ChatColorConfig extends Config
{
@ConfigItem(
position = 31,
keyName = "opaquePublicChat",
name = "Public chat",
description = "Color of Public chat"
)
Color opaquePublicChat();
@ConfigItem(
position = 32,
keyName = "opaquePublicChatHighlight",
name = "Public chat highlight",
description = "Color of highlights in Public chat"
)
default Color opaquePublicChatHighlight()
{
return Color.decode("#000000");
}
@ConfigItem(
position = 33,
keyName = "opaquePrivateMessageSent",
name = "Sent private messages",
description = "Color of Private messages you've sent"
)
Color opaquePrivateMessageSent();
@ConfigItem(
position = 34,
keyName = "opaquePrivateMessageSentHighlight",
name = "Sent private messages highlight",
description = "Color of highlights in Private messages you've sent"
)
default Color opaquePrivateMessageSentHighlight()
{
return Color.decode("#002783");
}
@ConfigItem(
position = 35,
keyName = "opaquePrivateMessageReceived",
name = "Received private messages",
description = "Color of Private messages you've received"
)
Color opaquePrivateMessageReceived();
@ConfigItem(
position = 36,
keyName = "opaquePrivateMessageReceivedHighlight",
name = "Received private messages highlight",
description = "Color of highlights in Private messages you've received"
)
default Color opaquePrivateMessageReceivedHighlight()
{
return Color.decode("#002783");
}
@ConfigItem(
position = 37,
keyName = "opaqueClanChatInfo",
name = "Clan chat info",
description = "Clan Chat Information (eg. when joining a channel)"
)
default Color opaqueClanChatInfo()
{
return JagexColors.CHAT_GAME_EXAMINE_TEXT_OPAQUE_BACKGROUND;
}
@ConfigItem(
position = 38,
keyName = "opaqueClanChatInfoHighlight",
name = "Clan chat info highlight",
description = "Clan Chat Information highlight (used for the Raids plugin)"
)
default Color opaqueClanChatInfoHighlight()
{
return Color.RED;
}
@ConfigItem(
position = 39,
keyName = "opaqueClanChatMessage",
name = "Clan chat message",
description = "Color of Clan Chat Messages"
)
Color opaqueClanChatMessage();
@ConfigItem(
position = 40,
keyName = "opaqueClanChatMessageHighlight",
name = "Clan chat message highlight",
description = "Color of highlights in Clan Chat Messages"
)
default Color opaqueClanChatMessageHighlight()
{
return Color.decode("#000000");
}
@ConfigItem(
position = 41,
keyName = "opaqueAutochatMessage",
name = "Autochat",
description = "Color of Autochat messages"
)
Color opaqueAutochatMessage();
@ConfigItem(
position = 42,
keyName = "opaqueAutochatMessageHighlight",
name = "Autochat highlight",
description = "Color of highlights in Autochat messages"
)
Color opaqueAutochatMessageHighlight();
@ConfigItem(
position = 43,
keyName = "opaqueTradeChatMessage",
name = "Trade chat",
description = "Color of Trade Chat Messages"
)
Color opaqueTradeChatMessage();
@ConfigItem(
position = 44,
keyName = "opaqueTradeChatMessageHighlight",
name = "Trade chat highlight",
description = "Color of highlights in Trade Chat Messages"
)
Color opaqueTradeChatMessageHighlight();
@ConfigItem(
position = 45,
keyName = "opaqueServerMessage",
name = "Server message",
description = "Color of Server Messages (eg. 'Welcome to Runescape')"
)
Color opaqueServerMessage();
@ConfigItem(
position = 46,
keyName = "opaqueServerMessageHighlight",
name = "Server message highlight",
description = "Color of highlights in Server Messages"
)
Color opaqueServerMessageHighlight();
@ConfigItem(
position = 47,
keyName = "opaqueGameMessage",
name = "Game message",
description = "Color of Game Messages"
)
Color opaqueGameMessage();
@ConfigItem(
position = 48,
keyName = "opaqueGameMessageHighlight",
name = "Game message highlight",
description = "Color of highlights in Game Messages"
)
default Color opaqueGameMessageHighlight()
{
return Color.decode("#EF1020");
}
@ConfigItem(
position = 49,
keyName = "opaqueExamine",
name = "Examine",
description = "Color of Examine Text"
)
Color opaqueExamine();
@ConfigItem(
position = 50,
keyName = "opaqueExamineHighlight",
name = "Examine highlight",
description = "Color of highlights in Examine Text"
)
default Color opaqueExamineHighlight()
{
return Color.decode("#0000FF");
}
@ConfigItem(
position = 51,
keyName = "opaqueFiltered",
name = "Filtered",
description = "Color of Filtered Text (messages that aren't shown when Game messages are filtered)"
)
Color opaqueFiltered();
@ConfigItem(
position = 52,
keyName = "opaqueFilteredHighlight",
name = "Filtered highlight",
description = "Color of highlights in Filtered Text"
)
Color opaqueFilteredHighlight();
@ConfigItem(
position = 53,
keyName = "opaqueUsername",
name = "Usernames",
description = "Color of Usernames"
)
Color opaqueUsername();
@ConfigItem(
position = 54,
keyName = "opaquePrivateUsernames",
name = "Private chat usernames",
description = "Color of Usernames in Private Chat"
)
Color opaquePrivateUsernames();
@ConfigItem(
position = 55,
keyName = "opaqueClanChannelName",
name = "Clan channel name",
description = "Color of Clan Channel Name"
)
Color opaqueClanChannelName();
@ConfigItem(
position = 56,
keyName = "opaqueClanUsernames",
name = "Clan usernames",
description = "Color of Usernames in Clan Chat"
)
Color opaqueClanUsernames();
@ConfigItem(
position = 57,
keyName = "opaquePublicFriendUsernames",
name = "Public friend usernames",
description = "Color of Friend Usernames in Public Chat"
)
Color opaquePublicFriendUsernames();
@ConfigItem(
position = 61,
keyName = "transparentPublicChat",
name = "Public chat (transparent)",
description = "Color of Public chat (transparent)"
)
Color transparentPublicChat();
@ConfigItem(
position = 62,
keyName = "transparentPublicChatHighlight",
name = "Public chat highlight (transparent)",
description = "Color of highlights in Public chat (transparent)"
)
default Color transparentPublicChatHighlight()
{
return Color.decode("#FFFFFF");
}
@ConfigItem(
position = 63,
keyName = "transparentPrivateMessageSent",
name = "Sent private messages (transparent)",
description = "Color of Private messages you've sent (transparent)"
)
Color transparentPrivateMessageSent();
@ConfigItem(
position = 64,
keyName = "transparentPrivateMessageSentHighlight",
name = "Sent private messages highlight (transparent)",
description = "Color of highlights in Private messages you've sent (transparent)"
)
default Color transparentPrivateMessageSentHighlight()
{
return Color.decode("#FFFFFF");
}
@ConfigItem(
position = 65,
keyName = "transparentPrivateMessageReceived",
name = "Received private messages (transparent)",
description = "Color of Private messages you've received (transparent)"
)
Color transparentPrivateMessageReceived();
@ConfigItem(
position = 66,
keyName = "transparentPrivateMessageReceivedHighlight",
name = "Received private messages highlight (transparent)",
description = "Color of highlights in Private messages you've received (transparent)"
)
default Color transparentPrivateMessageReceivedHighlight()
{
return Color.decode("#FFFFFF");
}
@ConfigItem(
position = 67,
keyName = "transparentClanChatInfo",
name = "Clan chat info (transparent)",
description = "Clan Chat Information (eg. when joining a channel) (transparent)"
)
default Color transparentClanChatInfo()
{
return JagexColors.CHAT_GAME_EXAMINE_TEXT_TRANSPARENT_BACKGROUND;
}
@ConfigItem(
position = 68,
keyName = "transparentClanChatInfoHighlight",
name = "Clan chat info highlight (transparent)",
description = "Clan Chat Information highlight (used for the Raids plugin) (transparent)"
)
default Color transparentClanChatInfoHighlight()
{
return Color.RED;
}
@ConfigItem(
position = 69,
keyName = "transparentClanChatMessage",
name = "Clan chat message (transparent)",
description = "Color of Clan Chat Messages (transparent)"
)
Color transparentClanChatMessage();
@ConfigItem(
position = 70,
keyName = "transparentClanChatMessageHighlight",
name = "Clan chat message highlight (transparent)",
description = "Color of highlights in Clan Chat Messages (transparent)"
)
default Color transparentClanChatMessageHighlight()
{
return Color.decode("#FFFFFF");
}
@ConfigItem(
position = 71,
keyName = "transparentAutochatMessage",
name = "Autochat (transparent)",
description = "Color of Autochat messages (transparent)"
)
Color transparentAutochatMessage();
@ConfigItem(
position = 72,
keyName = "transparentAutochatMessageHighlight",
name = "Autochat highlight (transparent)",
description = "Color of highlights in Autochat messages (transparent)"
)
Color transparentAutochatMessageHighlight();
@ConfigItem(
position = 73,
keyName = "transparentTradeChatMessage",
name = "Trade chat (transparent)",
description = "Color of Trade Chat Messages (transparent)"
)
Color transparentTradeChatMessage();
@ConfigItem(
position = 74,
keyName = "transparentTradeChatMessageHighlight",
name = "Trade chat highlight (transparent)",
description = "Color of highlights in Trade Chat Messages (transparent)"
)
Color transparentTradeChatMessageHighlight();
@ConfigItem(
position = 75,
keyName = "transparentServerMessage",
name = "Server message (transparent)",
description = "Color of Server Messages (eg. 'Welcome to Runescape') (transparent)"
)
Color transparentServerMessage();
@ConfigItem(
position = 76,
keyName = "transparentServerMessageHighlight",
name = "Server message highlight (transparent)",
description = "Color of highlights in Server Messages (transparent)"
)
Color transparentServerMessageHighlight();
@ConfigItem(
position = 77,
keyName = "transparentGameMessage",
name = "Game message (transparent)",
description = "Color of Game Messages (transparent)"
)
Color transparentGameMessage();
@ConfigItem(
position = 78,
keyName = "transparentGameMessageHighlight",
name = "Game message highlight (transparent)",
description = "Color of highlights in Game Messages (transparent)"
)
default Color transparentGameMessageHighlight()
{
return Color.decode("#EF1020");
}
@ConfigItem(
position = 79,
keyName = "transparentExamine",
name = "Examine (transparent)",
description = "Color of Examine Text (transparent)"
)
Color transparentExamine();
@ConfigItem(
position = 80,
keyName = "transparentExamineHighlight",
name = "Examine highlight (transparent)",
description = "Color of highlights in Examine Text (transparent)"
)
default Color transparentExamineHighlight()
{
return Color.decode("#0000FF");
}
@ConfigItem(
position = 81,
keyName = "transparentFiltered",
name = "Filtered (transparent)",
description = "Color of Filtered Text (messages that aren't shown when Game messages are filtered) (transparent)"
)
Color transparentFiltered();
@ConfigItem(
position = 82,
keyName = "transparentFilteredHighlight",
name = "Filtered highlight (transparent)",
description = "Color of highlights in Filtered Text (transparent)"
)
Color transparentFilteredHighlight();
@ConfigItem(
position = 83,
keyName = "transparentUsername",
name = "Usernames (transparent)",
description = "Color of Usernames (transparent)"
)
Color transparentUsername();
@ConfigItem(
position = 84,
keyName = "transparentPrivateUsernames",
name = "Private chat usernames (transparent)",
description = "Color of Usernames in Private Chat (transparent)"
)
Color transparentPrivateUsernames();
@ConfigItem(
position = 85,
keyName = "transparentClanChannelName",
name = "Clan channel name (transparent)",
description = "Color of Clan Channel Name (transparent)"
)
Color transparentClanChannelName();
@ConfigItem(
position = 86,
keyName = "transparentClanUsernames",
name = "Clan usernames (transparent)",
description = "Color of Usernames in Clan Chat (transparent)"
)
Color transparentClanUsernames();
@ConfigItem(
position = 87,
keyName = "transparentPublicFriendUsernames",
name = "Public friend usernames (transparent)",
description = "Color of Friend Usernames in Public Chat (transparent)"
)
Color transparentPublicFriendUsernames();
}

View File

@@ -1,29 +0,0 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* 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;
public interface Config
{
}

View File

@@ -1,64 +0,0 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* 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.util.ArrayList;
import java.util.Collection;
public class ConfigDescriptor
{
private final ConfigGroup group;
private final Collection<ConfigItemsGroup> itemGroups;
public ConfigDescriptor(ConfigGroup group, Collection<ConfigItemsGroup> itemGroups)
{
this.group = group;
this.itemGroups = itemGroups;
}
public ConfigGroup getGroup()
{
return group;
}
public Collection<ConfigItemsGroup> getItemGroups()
{
return itemGroups;
}
public Collection<ConfigItemDescriptor> getItems()
{
Collection<ConfigItemDescriptor> allItems = new ArrayList<>();
for (ConfigItemsGroup g : itemGroups)
{
for (ConfigItemDescriptor item : g.getItems())
{
allItems.add(item);
}
}
return allItems;
}
}

View File

@@ -1,43 +0,0 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* 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.TYPE)
public @interface ConfigGroup
{
/**
* The key name of the config group used for storing configuration within the config group.
* This should typically be a lowercased version of your plugin name, with all spaces removed.
* <p>
* For example, the {@code Grand Exchange} plugin uses the key name {@code grandexchange}.
*/
String value();
}

View File

@@ -1,157 +0,0 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* 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.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
@Slf4j
class ConfigInvocationHandler implements InvocationHandler
{
private final ConfigManager manager;
public ConfigInvocationHandler(ConfigManager manager)
{
this.manager = manager;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
Class<?> iface = proxy.getClass().getInterfaces()[0];
ConfigGroup group = iface.getAnnotation(ConfigGroup.class);
ConfigItem item = method.getAnnotation(ConfigItem.class);
if (group == null)
{
log.warn("Configuration proxy class {} has no @ConfigGroup!", proxy.getClass());
return null;
}
if (item == null)
{
log.warn("Configuration method {} has no @ConfigItem!", method);
return null;
}
if (args == null)
{
// Getting configuration item
String value = manager.getConfiguration(group.value(), item.keyName());
if (value == null)
{
if (method.isDefault())
{
return callDefaultMethod(proxy, method, null);
}
return null;
}
// Convert value to return type
Class<?> returnType = method.getReturnType();
try
{
return ConfigManager.stringToObject(value, returnType);
}
catch (Exception e)
{
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 null;
}
}
else
{
// Setting a configuration value
if (args.length != 1)
{
throw new RuntimeException("Invalid number of arguents to configuration method");
}
Object newValue = args[0];
Class<?> type = method.getParameterTypes()[0];
Object oldValue = manager.getConfiguration(group.value(), item.keyName(), type);
if (Objects.equals(oldValue, newValue))
{
// nothing to do
return null;
}
if (method.isDefault())
{
Object defaultValue = callDefaultMethod(proxy, method, args);
if (Objects.equals(newValue, defaultValue))
{
// Just unset if it goes back to the default
manager.unsetConfiguration(group.value(), item.keyName());
return null;
}
}
if (newValue == null)
{
manager.unsetConfiguration(group.value(), item.keyName());
}
else
{
String newValueStr = ConfigManager.objectToString(newValue);
manager.setConfiguration(group.value(), item.keyName(), newValueStr);
}
return null;
}
}
static Object callDefaultMethod(Object proxy, Method method, Object[] args) throws Throwable
{
// Call the default method implementation - https://rmannibucau.wordpress.com/2014/03/27/java-8-default-interface-methods-and-jdk-dynamic-proxies/
Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
constructor.setAccessible(true);
Class<?> declaringClass = method.getDeclaringClass();
return constructor.newInstance(declaringClass, MethodHandles.Lookup.PUBLIC | MethodHandles.Lookup.PRIVATE)
.unreflectSpecial(method, declaringClass)
.bindTo(proxy)
.invokeWithArguments(args);
}
}

View File

@@ -1,66 +0,0 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* 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 ConfigItem
{
int position() default -1;
String keyName();
String name();
String description();
boolean hidden() default false;
String warning() default "";
boolean secret() default false;
String group() default "";
String unhide() default "";
String hide() default "";
String parent() default "";
String enabledBy() default "";
String disabledBy() default "";
boolean parse() default false;
Class<?> clazz() default void.class;
String method() default "";
}

View File

@@ -1,36 +0,0 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* 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.Value;
@Value
public class ConfigItemDescriptor
{
private final ConfigItem item;
private final Class<?> type;
private final Range range;
private final Alpha alpha;
}

View File

@@ -1,52 +0,0 @@
/*
* Copyright (c) 2018, Craftiii4 <craftiii4@gmail.com>
* 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.util.ArrayList;
import java.util.Collection;
import lombok.AccessLevel;
import lombok.Getter;
public class ConfigItemsGroup
{
@Getter(AccessLevel.PUBLIC)
private final String group;
@Getter(AccessLevel.PUBLIC)
private Collection<ConfigItemDescriptor> items;
public ConfigItemsGroup(String group)
{
this.group = group;
this.items = new ArrayList<>();
}
public void addItem(ConfigItemDescriptor item)
{
items.add(item);
}
}

View File

@@ -1,628 +0,0 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* 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 com.google.common.base.Strings;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.ImmutableMap;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.nio.channels.FileLock;
import java.nio.charset.Charset;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.ConfigChanged;
import net.runelite.client.RuneLite;
import static net.runelite.client.RuneLite.PROFILES_DIR;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.util.ColorUtil;
@Singleton
@Slf4j
public class ConfigManager
{
private static final String SETTINGS_FILE_NAME = "runeliteplus.properties";
private static final String STANDARD_SETTINGS_FILE_NAME = "settings.properties";
private static final File SETTINGS_FILE = new File(RuneLite.RUNELITE_DIR, SETTINGS_FILE_NAME);
private static final File STANDARD_SETTINGS_FILE = new File(RuneLite.RUNELITE_DIR, STANDARD_SETTINGS_FILE_NAME);
@Inject
EventBus eventBus;
private final ScheduledExecutorService executor;
private final ConfigInvocationHandler handler = new ConfigInvocationHandler(this);
private final Properties properties = new Properties();
private final Map<String, String> pendingChanges = new HashMap<>();
@Inject
public ConfigManager(ScheduledExecutorService scheduledExecutorService)
{
this.executor = scheduledExecutorService;
executor.scheduleWithFixedDelay(this::sendConfig, 30, 30, TimeUnit.SECONDS);
}
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, Charset.forName("UTF-8")));
}
catch (Exception e)
{
log.debug("Malformed properties, skipping update");
return;
}
final Map<String, String> copy = (Map) ImmutableMap.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;
}
final String groupName = split[0];
final String key = split[1];
final String value = String.valueOf(objValue);
setConfiguration(groupName, key, value);
});
}
public void importLocal()
{
log.info("Nothing changed, don't worry!");
}
private synchronized void loadFromFile()
{
properties.clear();
try (FileInputStream in = new FileInputStream(SETTINGS_FILE))
{
properties.load(new InputStreamReader(in, Charset.forName("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);
}
try
{
Map<String, String> copy = (Map) ImmutableMap.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);
});
}
catch (Exception ex)
{
log.warn("Error posting config events", ex);
}
}
private void saveToFile() throws IOException
{
ConfigManager.SETTINGS_FILE.getParentFile().mkdirs();
try (FileOutputStream out = new FileOutputStream(ConfigManager.SETTINGS_FILE))
{
final FileLock lock = out.getChannel().lock();
try
{
properties.store(new OutputStreamWriter(out, Charset.forName("UTF-8")), "RuneLite configuration");
}
finally
{
lock.release();
}
}
}
public <T> T getConfig(Class<T> 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<String> 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);
}
public <T> T getConfiguration(String groupName, String key, Class<T> 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, String value)
{
String oldValue = (String) properties.setProperty(groupName + "." + key, value);
if (Objects.equals(oldValue, value))
{
return;
}
log.debug("Setting configuration value for {}.{} to {}", groupName, key, value);
synchronized (pendingChanges)
{
pendingChanges.put(groupName + "." + key, value);
}
ConfigChanged configChanged = new ConfigChanged();
configChanged.setGroup(groupName);
configChanged.setKey(key);
configChanged.setOldValue(oldValue);
configChanged.setNewValue(value);
eventBus.post(configChanged);
}
public void setConfiguration(String groupName, String key, Object value)
{
setConfiguration(groupName, key, objectToString(value));
}
public void unsetConfiguration(String groupName, String key)
{
String oldValue = (String) properties.remove(groupName + "." + key);
if (oldValue == null)
{
return;
}
log.debug("Unsetting configuration value for {}.{}", groupName, key);
synchronized (pendingChanges)
{
pendingChanges.put(groupName + "." + key, null);
}
ConfigChanged configChanged = new ConfigChanged();
configChanged.setGroup(groupName);
configChanged.setKey(key);
configChanged.setOldValue(oldValue);
eventBus.post(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<ConfigItemDescriptor> items = Arrays.stream(inter.getMethods())
.filter(m -> m.getParameterCount() == 0)
.map(m -> new ConfigItemDescriptor(
m.getDeclaredAnnotation(ConfigItem.class),
m.getReturnType(),
m.getDeclaredAnnotation(Range.class),
m.getDeclaredAnnotation(Alpha.class)
))
.sorted((a, b) -> ComparisonChain.start()
.compare(a.getItem().position(), b.getItem().position())
.compare(a.getItem().name(), b.getItem().name())
.result())
.collect(Collectors.toList());
Collection<ConfigItemsGroup> itemGroups = new ArrayList<>();
for (ConfigItemDescriptor item : items)
{
String groupName = item.getItem().group();
boolean found = false;
for (ConfigItemsGroup g : itemGroups)
{
if (g.getGroup().equals(groupName))
{
g.addItem(item);
found = true;
break;
}
}
if (!found)
{
ConfigItemsGroup newGroup = new ConfigItemsGroup(groupName);
newGroup.addItem(item);
itemGroups.add(newGroup);
}
}
itemGroups = itemGroups.stream().sorted((a, b) -> ComparisonChain.start()
.compare(a.getGroup(), b.getGroup())
.result())
.collect(Collectors.toList());
return new ConfigDescriptor(group, itemGroups);
}
/**
* 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)
{
String current = getConfiguration(group.value(), item.keyName());
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);
if (Objects.equals(current, 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)
{
return Boolean.parseBoolean(str);
}
if (type == int.class)
{
return Integer.parseInt(str);
}
if (type == Color.class)
{
return ColorUtil.fromString(str);
}
if (type == Dimension.class)
{
String[] splitStr = str.split("x");
int width = Integer.parseInt(splitStr[0]);
int height = Integer.parseInt(splitStr[1]);
return new Dimension(width, height);
}
if (type == Point.class)
{
String[] splitStr = str.split(":");
int width = Integer.parseInt(splitStr[0]);
int height = Integer.parseInt(splitStr[1]);
return new Point(width, height);
}
if (type == Rectangle.class)
{
String[] splitStr = str.split(":");
int x = Integer.parseInt(splitStr[0]);
int y = Integer.parseInt(splitStr[1]);
int width = Integer.parseInt(splitStr[2]);
int height = Integer.parseInt(splitStr[3]);
return new Rectangle(x, y, width, height);
}
if (type.isEnum())
{
return Enum.valueOf((Class<? extends Enum>) type, str);
}
if (type == Instant.class)
{
return Instant.parse(str);
}
if (type == Keybind.class || type == ModifierlessKeybind.class)
{
String[] splitStr = str.split(":");
int code = Integer.parseInt(splitStr[0]);
int mods = Integer.parseInt(splitStr[1]);
if (type == ModifierlessKeybind.class)
{
return new ModifierlessKeybind(code, mods);
}
return new Keybind(code, mods);
}
if (type == WorldPoint.class)
{
String[] splitStr = str.split(":");
int x = Integer.parseInt(splitStr[0]);
int y = Integer.parseInt(splitStr[1]);
int plane = Integer.parseInt(splitStr[2]);
return new WorldPoint(x, y, plane);
}
if (type == Duration.class)
{
return Duration.ofMillis(Long.parseLong(str));
}
if (type == Map.class)
{
Map<String, String> 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 str;
}
static String objectToString(Object object)
{
if (object instanceof Color)
{
return String.valueOf(((Color) object).getRGB());
}
if (object instanceof Enum)
{
return ((Enum) object).name();
}
if (object instanceof Dimension)
{
Dimension d = (Dimension) object;
return d.width + "x" + d.height;
}
if (object instanceof Point)
{
Point p = (Point) object;
return p.x + ":" + p.y;
}
if (object instanceof Rectangle)
{
Rectangle r = (Rectangle) object;
return r.x + ":" + r.y + ":" + r.width + ":" + r.height;
}
if (object instanceof Instant)
{
return ((Instant) object).toString();
}
if (object instanceof Keybind)
{
Keybind k = (Keybind) object;
return k.getKeyCode() + ":" + k.getModifiers();
}
if (object instanceof WorldPoint)
{
WorldPoint wp = (WorldPoint) object;
return wp.getX() + ":" + wp.getY() + ":" + wp.getPlane();
}
if (object instanceof Duration)
{
return Long.toString(((Duration) object).toMillis());
}
return object.toString();
}
public void sendConfig()
{
boolean changed;
synchronized (pendingChanges)
{
changed = !pendingChanges.isEmpty();
pendingChanges.clear();
}
if (changed)
{
try
{
saveToFile();
}
catch (IOException ex)
{
log.warn("unable to save configuration file", ex);
}
}
}
private void syncLastModified()
{
File newestFile;
newestFile = STANDARD_SETTINGS_FILE;
for (File profileDir : PROFILES_DIR.listFiles())
{
if (!profileDir.isDirectory())
{
continue;
}
for (File settings : profileDir.listFiles())
{
if (!settings.getName().equals(STANDARD_SETTINGS_FILE_NAME) ||
settings.lastModified() < newestFile.lastModified())
{
continue;
}
newestFile = settings;
}
}
syncPropertiesFromFile(newestFile);
}
}

View File

@@ -1,91 +0,0 @@
/*
* Copyright (c) 2018, Craftiii4 <craftiii4@gmail.com>
* 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.util.ArrayList;
import java.util.List;
import lombok.AccessLevel;
import lombok.Getter;
public class ConfigPanelItem
{
@Getter(AccessLevel.PUBLIC)
private ConfigPanelItem parent;
@Getter(AccessLevel.PUBLIC)
private List<ConfigPanelItem> children;
@Getter(AccessLevel.PUBLIC)
private ConfigItemDescriptor item;
public ConfigPanelItem(ConfigPanelItem parent, ConfigItemDescriptor item)
{
this.parent = parent;
this.children = new ArrayList<>();
this.item = item;
}
public List<ConfigPanelItem> getItemsAsList()
{
List<ConfigPanelItem> items = new ArrayList<>();
items.add(this);
for (ConfigPanelItem child : children)
{
items.addAll(child.getItemsAsList());
}
return items;
}
public int getDepth()
{
return (parent == null ? 0 : parent.getDepth() + 1);
}
public boolean addChildIfMatchParent(ConfigItemDescriptor cid)
{
if (item != null && item.getItem().keyName().equals(cid.getItem().parent()))
{
children.add(new ConfigPanelItem(this, cid));
return true;
}
else
{
for (ConfigPanelItem child : children)
{
if (child.addChildIfMatchParent(cid))
{
return true;
}
}
return false;
}
}
}

View File

@@ -1,44 +0,0 @@
/*
* Copyright (c) 2018, Woox <https://github.com/wooxsolo>
* 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.Getter;
import lombok.RequiredArgsConstructor;
@Getter
@RequiredArgsConstructor
public enum ExpandResizeType
{
KEEP_WINDOW_SIZE("Keep window size"),
KEEP_GAME_SIZE("Keep game size");
private final String type;
@Override
public String toString()
{
return type;
}
}

View File

@@ -1,48 +0,0 @@
/*
* Copyright (c) 2018, Tanner <https://github.com/Reasel>
* 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.Font;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.runelite.client.ui.FontManager;
@Getter
@RequiredArgsConstructor
public enum FontType
{
REGULAR("Regular", FontManager.getRunescapeFont()),
BOLD("Bold", FontManager.getRunescapeBoldFont()),
SMALL("Small", FontManager.getRunescapeSmallFont());
private final String name;
private final Font font;
@Override
public String toString()
{
return name;
}
}

View File

@@ -1,212 +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.config;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import javax.annotation.Nullable;
import lombok.EqualsAndHashCode;
import lombok.Getter;
/**
* A combination of zero or more modifier keys (Ctrl, alt, shift, meta)
* and an optional non-modifier key
*/
@Getter
@EqualsAndHashCode
public class Keybind
{
private static final BiMap<Integer, Integer> MODIFIER_TO_KEY_CODE = new ImmutableBiMap.Builder<Integer, Integer>()
.put(InputEvent.CTRL_DOWN_MASK, KeyEvent.VK_CONTROL)
.put(InputEvent.ALT_DOWN_MASK, KeyEvent.VK_ALT)
.put(InputEvent.SHIFT_DOWN_MASK, KeyEvent.VK_SHIFT)
.put(InputEvent.META_DOWN_MASK, KeyEvent.VK_META)
.build();
// Bitmask of all supported modifers
private static final int KEYBOARD_MODIFIER_MASK = MODIFIER_TO_KEY_CODE.keySet().stream()
.reduce((a, b) -> a | b).get();
public static final Keybind NOT_SET = new Keybind(KeyEvent.VK_UNDEFINED, 0);
public static final Keybind CTRL = new Keybind(KeyEvent.VK_UNDEFINED, InputEvent.CTRL_DOWN_MASK);
public static final Keybind ALT = new Keybind(KeyEvent.VK_UNDEFINED, InputEvent.ALT_DOWN_MASK);
public static final Keybind SHIFT = new Keybind(KeyEvent.VK_UNDEFINED, InputEvent.SHIFT_DOWN_MASK);
private final int keyCode;
private final int modifiers;
protected Keybind(int keyCode, int modifiers, boolean ignoreModifiers)
{
modifiers &= KEYBOARD_MODIFIER_MASK;
// If the keybind is just modifiers we don't want the keyCode to contain the modifier too,
// becasue this breaks if you do the keycode backwards
Integer mf = getModifierForKeyCode(keyCode);
if (mf != null)
{
assert (modifiers & mf) != 0;
keyCode = KeyEvent.VK_UNDEFINED;
}
if (ignoreModifiers && keyCode != KeyEvent.VK_UNDEFINED)
{
modifiers = 0;
}
this.keyCode = keyCode;
this.modifiers = modifiers;
}
public Keybind(int keyCode, int modifiers)
{
this(keyCode, modifiers, false);
}
/**
* Constructs a keybind with that matches the passed KeyEvent
*/
public Keybind(KeyEvent e)
{
this(e.getExtendedKeyCode(), e.getModifiersEx());
assert matches(e);
}
/**
* If the KeyEvent is from a KeyPressed event this returns if the
* Event is this hotkey being pressed. If the KeyEvent is a
* KeyReleased event this returns if the event is this hotkey being
* released
*/
public boolean matches(KeyEvent e)
{
return matches(e, false);
}
protected boolean matches(KeyEvent e, boolean ignoreModifiers)
{
if (NOT_SET.equals(this))
{
return false;
}
int keyCode = e.getExtendedKeyCode();
int modifiers = e.getModifiersEx() & KEYBOARD_MODIFIER_MASK;
Integer mf = getModifierForKeyCode(keyCode);
if (mf != null)
{
modifiers |= mf;
keyCode = KeyEvent.VK_UNDEFINED;
}
if (e.getID() == KeyEvent.KEY_RELEASED && keyCode != KeyEvent.VK_UNDEFINED)
{
return this.keyCode == keyCode;
}
if (ignoreModifiers && keyCode != KeyEvent.VK_UNDEFINED)
{
return this.keyCode == keyCode;
}
return this.keyCode == keyCode && this.modifiers == modifiers;
}
@Override
public String toString()
{
if (keyCode == KeyEvent.VK_UNDEFINED && modifiers == 0)
{
return "Not set";
}
String key;
if (keyCode == KeyEvent.VK_UNDEFINED)
{
key = "";
}
else
{
key = KeyEvent.getKeyText(keyCode);
}
String mod = "";
if (modifiers != 0)
{
mod = getModifiersExText(modifiers);
}
if (mod.isEmpty() && key.isEmpty())
{
return "Not set";
}
if (!mod.isEmpty() && !key.isEmpty())
{
return mod + "+" + key;
}
if (mod.isEmpty())
{
return key;
}
return mod;
}
public static String getModifiersExText(int modifiers)
{
StringBuilder buf = new StringBuilder();
if ((modifiers & InputEvent.META_DOWN_MASK) != 0)
{
buf.append("Meta+");
}
if ((modifiers & InputEvent.CTRL_DOWN_MASK) != 0)
{
buf.append("Ctrl+");
}
if ((modifiers & InputEvent.ALT_DOWN_MASK) != 0)
{
buf.append("Alt+");
}
if ((modifiers & InputEvent.SHIFT_DOWN_MASK) != 0)
{
buf.append("Shift+");
}
if (buf.length() > 0)
{
buf.setLength(buf.length() - 1); // remove trailing '+'
}
return buf.toString();
}
@Nullable
public static Integer getModifierForKeyCode(int keyCode)
{
return MODIFIER_TO_KEY_CODE.inverse().get(keyCode);
}
}

View File

@@ -1,56 +0,0 @@
/*
* 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.config;
import java.awt.event.KeyEvent;
public class ModifierlessKeybind extends Keybind
{
public ModifierlessKeybind(int keyCode, int modifiers)
{
super(keyCode, modifiers, true);
}
/**
* Constructs a keybind with that matches the passed KeyEvent
*/
public ModifierlessKeybind(KeyEvent e)
{
this(e.getExtendedKeyCode(), e.getModifiersEx());
assert matches(e);
}
/**
* If the KeyEvent is from a KeyPressed event this returns if the
* Event is this hotkey being pressed. If the KeyEvent is a
* KeyReleased event this returns if the event is this hotkey being
* released
*/
public boolean matches(KeyEvent e)
{
return matches(e, true);
}
}

View File

@@ -1,44 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Used with ConfigItem, describes valid int range for a config item.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface Range
{
int min() default 0;
int max() default Integer.MAX_VALUE;
}

View File

@@ -1,286 +0,0 @@
/*
* Copyright (c) 2017, Tyler <https://github.com/tylerthardy>
* 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.Dimension;
import net.runelite.api.Constants;
@ConfigGroup("runelite")
public interface RuneLiteConfig extends Config
{
@ConfigItem(
keyName = "gameSize",
name = "Game size",
description = "The game will resize to this resolution upon starting the client",
position = 10
)
default Dimension gameSize()
{
return Constants.GAME_FIXED_SIZE;
}
@ConfigItem(
keyName = "automaticResizeType",
name = "Resize type",
description = "Choose how the window should resize when opening and closing panels",
position = 11
)
default ExpandResizeType automaticResizeType()
{
return ExpandResizeType.KEEP_GAME_SIZE;
}
@ConfigItem(
keyName = "lockWindowSize",
name = "Lock window size",
description = "Determines if the window resizing is allowed or not",
position = 12
)
default boolean lockWindowSize()
{
return false;
}
@ConfigItem(
keyName = "enablePlugins",
name = "Enable loading of external plugins",
description = "Enable loading of external plugins",
position = 10
)
default boolean enablePlugins()
{
return true;
}
@ConfigItem(
keyName = "containInScreen",
name = "Contain in screen",
description = "Makes the client stay contained in the screen when attempted to move out of it.<br>Note: Only works if custom chrome is enabled.",
position = 13
)
default boolean containInScreen()
{
return false;
}
@ConfigItem(
keyName = "rememberScreenBounds",
name = "Remember client position",
description = "Save the position and size of the client after exiting",
position = 14
)
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
)
default boolean enableCustomChrome()
{
return true;
}
@ConfigItem(
keyName = "gameAlwaysOnTop",
name = "Enable client always on top",
description = "The game will always be on the top of the screen",
position = 16
)
default boolean gameAlwaysOnTop()
{
return false;
}
@ConfigItem(
keyName = "warningOnExit",
name = "Display warning on exit",
description = "Toggles a warning popup when trying to exit the client",
position = 17
)
default WarningOnExit warningOnExit()
{
return WarningOnExit.LOGGED_IN;
}
@ConfigItem(
keyName = "usernameInTitle",
name = "Show display name in title",
description = "Toggles displaying of local player's display name in client title",
position = 18
)
default boolean usernameInTitle()
{
return true;
}
@ConfigItem(
keyName = "notificationTray",
name = "Enable tray notifications",
description = "Enables tray notifications",
position = 20
)
default boolean enableTrayNotifications()
{
return true;
}
@ConfigItem(
keyName = "notificationRequestFocus",
name = "Request focus on notification",
description = "Toggles window focus request",
position = 21
)
default boolean requestFocusOnNotification()
{
return true;
}
@ConfigItem(
keyName = "notificationSound",
name = "Enable sound on notifications",
description = "Enables the playing of a beep sound when notifications are displayed",
position = 22
)
default boolean enableNotificationSound()
{
return true;
}
@ConfigItem(
keyName = "notificationGameMessage",
name = "Enable game message notifications",
description = "Puts a notification message in the chatbox",
position = 23
)
default boolean enableGameMessageNotification()
{
return false;
}
@ConfigItem(
keyName = "notificationFlash",
name = "Enable flash notification",
description = "Flashes the game frame as a notification",
position = 24
)
default boolean enableFlashNotification()
{
return false;
}
@ConfigItem(
keyName = "notificationFocused",
name = "Send notifications when focused",
description = "Toggles all notifications for when the client is focused",
position = 25
)
default boolean sendNotificationsWhenFocused()
{
return false;
}
@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 = 30
)
default FontType fontType()
{
return FontType.SMALL;
}
@ConfigItem(
keyName = "tooltipFontType",
name = "Tooltip Font",
description = "Configures what font type is used for in-game tooltips such as food stats, NPC names, etc.",
position = 31
)
default FontType tooltipFontType()
{
return FontType.SMALL;
}
@ConfigItem(
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 = 32
)
default FontType interfaceFontType()
{
return FontType.REGULAR;
}
@ConfigItem(
keyName = "menuEntryShift",
name = "Require Shift for overlay menu",
description = "Overlay right-click menu will require shift to be added",
position = 33
)
default boolean menuEntryShift()
{
return true;
}
@ConfigItem(
keyName = "infoBoxVertical",
name = "Display infoboxes vertically",
description = "Toggles the infoboxes to display vertically",
position = 40
)
default boolean infoBoxVertical()
{
return false;
}
@ConfigItem(
keyName = "infoBoxWrap",
name = "Infobox wrap count",
description = "Configures the amount of infoboxes shown before wrapping",
position = 41
)
default int infoBoxWrap()
{
return 4;
}
@ConfigItem(
keyName = "infoBoxSize",
name = "Infobox size (px)",
description = "Configures the size of each infobox in pixels",
position = 42
)
default int infoBoxSize()
{
return 35;
}
}

View File

@@ -1,5 +0,0 @@
package net.runelite.client.config;
public class Stub
{
}

View File

@@ -1,45 +0,0 @@
/*
* Copyright (c) 2018, Matt <https://github.com/ms813>
* 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.Getter;
import lombok.RequiredArgsConstructor;
@Getter
@RequiredArgsConstructor
public enum WarningOnExit
{
ALWAYS("Always"),
LOGGED_IN("Logged in"),
NEVER("Never");
private final String type;
@Override
public String toString()
{
return type;
}
}

View File

@@ -1,147 +0,0 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* 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.discord;
import java.time.Instant;
import lombok.Builder;
import lombok.Value;
/**
* Represents Discord Rich Presence RPC data
*/
@Builder
@Value
public class DiscordPresence
{
/**
* The user's current party status.
* Example: "Looking to Play", "Playing Solo", "In a Group"
*
* <b>Maximum: 128 characters</b>
*/
private String state;
/**
* What the player is currently doing.
* Example: "Competitive - Captain's Mode", "In Queue", "Unranked PvP"
*
* <b>Maximum: 128 characters</b>
*/
private String details;
/**
* Unix timestamp (seconds) for the start of the game.
*/
private Instant startTimestamp;
/**
* Unix timestamp (seconds) for the end of the game.
*/
private Instant endTimestamp;
/**
* Name of the uploaded image for the large profile artwork.
* Example: "default"
*
* <b>Maximum: 32 characters</b>
*/
private String largeImageKey;
/**
* Tooltip for the largeImageKey.
* Example: "Blade's Edge Arena", "Numbani", "Danger Zone"
*
* <b>Maximum: 128 characters</b>
*/
private String largeImageText;
/**
* Name of the uploaded image for the small profile artwork.
* Example: "rogue"
*
* <b>Maximum: 32 characters</b>
*/
private String smallImageKey;
/**
* Tooltip for the smallImageKey.
* Example: "Rogue - Level 100"
*
* <b>Maximum: 128 characters</b>
*/
private String smallImageText;
/**
* ID of the player's party, lobby, or group.
* Example: "ae488379-351d-4a4f-ad32-2b9b01c91657"
*
* <b>Maximum: 128 characters</b>
*/
private String partyId;
/**
* Current size of the player's party, lobby, or group.
* Example: 1
*/
private int partySize;
/**
* Maximum size of the player's party, lobby, or group.
* Example: 5
*/
private int partyMax;
/**
* Unique hashed string for Spectate and Join.
* Required to enable match interactive buttons in the user's presence.
* Example: "MmhuZToxMjMxMjM6cWl3amR3MWlqZA=="
*
* <b>Maximum: 128 characters</b>
*/
private String matchSecret;
/**
* Unique hashed string for Spectate button.
* This will enable the "Spectate" button on the user's presence if whitelisted.
* Example: "MTIzNDV8MTIzNDV8MTMyNDU0"
*
* <b>Maximum: 128 characters</b>
*/
private String joinSecret;
/**
* Unique hashed string for chat invitations and Ask to Join.
* This will enable the "Ask to Join" button on the user's presence if whitelisted.
* Example: "MTI4NzM0OjFpMmhuZToxMjMxMjM="
*
* <b>Maximum: 128 characters</b>
*/
private String spectateSecret;
/**
* Marks the matchSecret as a game session with a specific beginning and end.
*/
private boolean instance;
}

View File

@@ -1,46 +0,0 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* 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.discord;
/**
* Discord reply type for request
*/
public enum DiscordReplyType
{
/**
* Used to decline a request
*/
NO,
/**
* Used to accept a request
*/
YES,
/**
* Currently unused response, treated like NO.
*/
IGNORE
}

View File

@@ -1,237 +0,0 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* 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.discord;
import com.google.common.base.Strings;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.client.RuneLiteProperties;
import net.runelite.client.discord.events.DiscordDisconnected;
import net.runelite.client.discord.events.DiscordErrored;
import net.runelite.client.discord.events.DiscordJoinGame;
import net.runelite.client.discord.events.DiscordJoinRequest;
import net.runelite.client.discord.events.DiscordReady;
import net.runelite.client.discord.events.DiscordSpectateGame;
import net.runelite.client.eventbus.EventBus;
import net.runelite.discord.DiscordEventHandlers;
import net.runelite.discord.DiscordRPC;
import net.runelite.discord.DiscordRichPresence;
import net.runelite.discord.DiscordUser;
@Singleton
@Slf4j
public class DiscordService implements AutoCloseable
{
private final EventBus eventBus;
public final RuneLiteProperties runeLiteProperties;
private final ScheduledExecutorService executorService;
private final DiscordRPC discordRPC;
// Hold a reference to the event handlers to prevent the garbage collector from deleting them
private final DiscordEventHandlers discordEventHandlers;
@Getter
private DiscordUser currentUser;
@Inject
private DiscordService(
final EventBus eventBus,
final RuneLiteProperties runeLiteProperties,
final ScheduledExecutorService executorService)
{
this.eventBus = eventBus;
this.runeLiteProperties = runeLiteProperties;
this.executorService = executorService;
DiscordRPC discordRPC = null;
DiscordEventHandlers discordEventHandlers = null;
try
{
discordRPC = DiscordRPC.INSTANCE;
discordEventHandlers = new DiscordEventHandlers();
}
catch (Error e)
{
log.warn("Failed to load Discord library, Discord support will be disabled.");
}
this.discordRPC = discordRPC;
this.discordEventHandlers = discordEventHandlers;
}
/**
* Initializes the Discord service, sets up the event handlers and starts worker thread that will poll discord
* events every 2 seconds.
* Before closing the application it is recommended to call {@link #close()}
*/
public void init()
{
if (discordEventHandlers == null)
{
return;
}
log.info("Initializing Discord RPC service.");
discordEventHandlers.ready = this::ready;
discordEventHandlers.disconnected = this::disconnected;
discordEventHandlers.errored = this::errored;
discordEventHandlers.joinGame = this::joinGame;
discordEventHandlers.spectateGame = this::spectateGame;
discordEventHandlers.joinRequest = this::joinRequest;
discordRPC.Discord_Initialize(RuneLiteProperties.discordAppID, discordEventHandlers, true, null);
executorService.scheduleAtFixedRate(discordRPC::Discord_RunCallbacks, 0, 2, TimeUnit.SECONDS);
}
/**
* Shuts the RPC connection down.
* If not currently connected, this does nothing.
*/
@Override
public void close()
{
if (discordRPC != null)
{
discordRPC.Discord_Shutdown();
}
}
/**
* Updates the currently set presence of the logged in user.
* <br>Note that the client only updates its presence every <b>15 seconds</b>
* and queues all additional presence updates.
*
* @param discordPresence The new presence to use
*/
public void updatePresence(DiscordPresence discordPresence)
{
if (discordRPC == null)
{
return;
}
final DiscordRichPresence discordRichPresence = new DiscordRichPresence();
discordRichPresence.state = discordPresence.getState();
discordRichPresence.details = discordPresence.getDetails();
discordRichPresence.startTimestamp = discordPresence.getStartTimestamp() != null
? discordPresence.getStartTimestamp().getEpochSecond()
: 0;
discordRichPresence.endTimestamp = discordPresence.getEndTimestamp() != null
? discordPresence.getEndTimestamp().getEpochSecond()
: 0;
discordRichPresence.largeImageKey = Strings.isNullOrEmpty(discordPresence.getLargeImageKey())
? "default"
: discordPresence.getLargeImageKey();
discordRichPresence.largeImageText = discordPresence.getLargeImageText();
if (!Strings.isNullOrEmpty(discordPresence.getSmallImageKey()))
{
discordRichPresence.smallImageKey = discordPresence.getSmallImageKey();
}
discordRichPresence.smallImageText = discordPresence.getSmallImageText();
discordRichPresence.partyId = discordPresence.getPartyId();
discordRichPresence.partySize = discordPresence.getPartySize();
discordRichPresence.partyMax = discordPresence.getPartyMax();
discordRichPresence.matchSecret = discordPresence.getMatchSecret();
discordRichPresence.joinSecret = discordPresence.getJoinSecret();
discordRichPresence.spectateSecret = discordPresence.getSpectateSecret();
discordRichPresence.instance = (byte) (discordPresence.isInstance() ? 1 : 0);
log.debug("Sending presence update {}", discordPresence);
discordRPC.Discord_UpdatePresence(discordRichPresence);
}
/**
* Clears the currently set presence.
*/
public void clearPresence()
{
if (discordRPC != null)
{
discordRPC.Discord_ClearPresence();
}
}
/**
* Responds to the given user with the specified reply type.
*
* @param userId The id of the user to respond to
* @param reply The reply type
*/
public void respondToRequest(String userId, DiscordReplyType reply)
{
if (discordRPC != null)
{
discordRPC.Discord_Respond(userId, reply.ordinal());
}
}
private void ready(DiscordUser user)
{
log.info("Discord RPC service is ready with user {}.", user.username);
currentUser = user;
eventBus.post(new DiscordReady(
user.userId,
user.username,
user.discriminator,
user.avatar));
}
private void disconnected(int errorCode, String message)
{
eventBus.post(new DiscordDisconnected(errorCode, message));
}
private void errored(int errorCode, String message)
{
log.warn("Discord error: {} - {}", errorCode, message);
eventBus.post(new DiscordErrored(errorCode, message));
}
private void joinGame(String joinSecret)
{
eventBus.post(new DiscordJoinGame(joinSecret));
}
private void spectateGame(String spectateSecret)
{
eventBus.post(new DiscordSpectateGame(spectateSecret));
}
private void joinRequest(DiscordUser user)
{
eventBus.post(new DiscordJoinRequest(
user.userId,
user.username,
user.discriminator,
user.avatar));
}
}

View File

@@ -1,44 +0,0 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* 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.discord.events;
import lombok.Value;
/**
* Called when the RPC connection has been severed
*/
@Value
public class DiscordDisconnected
{
/**
* Discord error code
*/
private int errorCode;
/**
* Error message
*/
private String message;
}

View File

@@ -1,44 +0,0 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* 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.discord.events;
import lombok.Value;
/**
* Called when an internal error is caught within the SDK
*/
@Value
public class DiscordErrored
{
/**
* Discord error code.
*/
private int errorCode;
/**
* Error message
*/
private String message;
}

View File

@@ -1,39 +0,0 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* 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.discord.events;
import lombok.Value;
/**
* Called when the logged in user joined a game
*/
@Value
public class DiscordJoinGame
{
/**
* Obfuscated data of your choosing used as join secret
*/
private String joinSecret;
}

View File

@@ -1,54 +0,0 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* 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.discord.events;
import lombok.Value;
/**
* Called when another discord user wants to join the game of the logged in user
*/
@Value
public class DiscordJoinRequest
{
/**
* The userId for the user that requests to join
*/
private String userId;
/**
* The username of the user that requests to join
*/
private String username;
/**
* The discriminator of the user that requests to join
*/
private String discriminator;
/**
* The avatar of the user that requests to join
*/
private String avatar;
}

View File

@@ -1,54 +0,0 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* 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.discord.events;
import lombok.Value;
/**
* Called when the RPC connection has been established
*/
@Value
public class DiscordReady
{
/**
* The userId for the active user
*/
private String userId;
/**
* The username of the active user
*/
private String username;
/**
* The discriminator of the active user
*/
private String discriminator;
/**
* The avatar of the active user
*/
private String avatar;
}

View File

@@ -1,39 +0,0 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* 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.discord.events;
import lombok.Value;
/**
* Called when the logged in user joined to spectate a game
*/
@Value
public class DiscordSpectateGame
{
/**
* Obfuscated data of your choosing used as spectate secret
*/
private String spectateSecret;
}

View File

@@ -1,249 +0,0 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* 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.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.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
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;
@Slf4j
@RequiredArgsConstructor
@ThreadSafe
public class EventBus
{
@FunctionalInterface
public interface SubscriberMethod
{
void invoke(Object event);
}
@Value
private static class Subscriber
{
private final Object object;
private final Method method;
@EqualsAndHashCode.Exclude
private final SubscriberMethod lamda;
void invoke(final Object arg) throws Exception
{
if (lamda != null)
{
lamda.invoke(arg);
}
else
{
method.invoke(object, arg);
}
}
}
private final Consumer<Throwable> exceptionHandler;
private ImmutableMultimap<Class, Subscriber> 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<Class, Subscriber> builder = ImmutableMultimap.builder();
if (subscribers != null)
{
builder.putAll(subscribers);
}
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 = 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, lambda);
builder.put(parameterClazz, subscriber);
log.debug("Registering {} - {}", parameterClazz, subscriber);
}
}
subscribers = builder.build();
}
/**
* Unregisters all subscribed methods from provided subscriber object.
*
* @param object object to unsubscribe from
*/
public synchronized void unregister(@Nonnull final Object object)
{
if (subscribers == null)
{
return;
}
final Multimap<Class, Subscriber> map = HashMultimap.create();
map.putAll(subscribers);
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, null));
}
}
subscribers = ImmutableMultimap.copyOf(map);
}
/**
* Posts provided event to all registered subscribers. Subscriber calls are invoked immediately and in order
* in which subscribers were registered.
*
* @param event event to post
*/
public void post(@Nonnull final Object event)
{
for (final Subscriber subscriber : subscribers.get(event.getClass()))
{
try
{
subscriber.invoke(event);
}
catch (Exception e)
{
exceptionHandler.accept(e);
}
}
}
private static MethodHandles.Lookup privateLookupIn(Class clazz) throws IllegalAccessException, NoSuchFieldException, InvocationTargetException
{
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 (NoSuchMethodException e)
{
// 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;
}
}
}

View File

@@ -1,41 +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.eventbus;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marks a method as an event subscriber.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface Subscribe
{
}

View File

@@ -1,30 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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;
public abstract class ChatInput
{
public abstract void resume();
}

View File

@@ -1,36 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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 lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public abstract class ChatboxInput extends ChatInput
{
private final String value;
private final int chatType;
}

View File

@@ -1,34 +0,0 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* 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.Value;
import net.runelite.client.ui.NavigationButton;
@Value
public class NavigationButtonAdded
{
private NavigationButton button;
}

View File

@@ -1,34 +0,0 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* 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.Value;
import net.runelite.client.ui.NavigationButton;
@Value
public class NavigationButtonRemoved
{
private NavigationButton button;
}

View File

@@ -1,37 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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 java.util.Collection;
import lombok.Value;
import net.runelite.api.NPC;
import net.runelite.client.game.ItemStack;
@Value
public class NpcLootReceived
{
private final NPC npc;
private final Collection<ItemStack> items;
}

View File

@@ -1,41 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.AllArgsConstructor;
import lombok.Data;
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
{
private OverlayMenuEntry entry;
private Overlay overlay;
}

View File

@@ -1,34 +0,0 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* 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 java.util.UUID;
import lombok.Value;
@Value
public class PartyChanged
{
private final UUID partyId;
}

View File

@@ -1,37 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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 java.util.Collection;
import lombok.Value;
import net.runelite.api.Player;
import net.runelite.client.game.ItemStack;
@Value
public class PlayerLootReceived
{
private final Player player;
private final Collection<ItemStack> items;
}

View File

@@ -1,35 +0,0 @@
/*
* Copyright (c) 2017, Tomas Slusny <slusnucky@gmail.com>
* 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.client.plugins.Plugin;
@Data
public class PluginChanged
{
private final Plugin plugin;
private final boolean loaded;
}

View File

@@ -1,36 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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 lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public abstract class PrivateMessageInput extends ChatInput
{
private final String target;
private final String message;
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* 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;
/**
* An event where a new RuneLite account session has been closed,
* typically when logging out of the account.
* <p>
* Note: This event is not to be confused with a RuneScape session,
* it has nothing to do with whether an account is being logged out.
*/
@Data
public class SessionClose
{
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* 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;
/**
* An event where a new RuneLite account session has been opened
* with the server.
* <p>
* Note: This event is not to be confused with a RuneScape session,
* it has nothing to do with whether an account is being logged in.
*/
@Data
public class SessionOpen
{
}

View File

@@ -1,338 +0,0 @@
/*
*
* Copyright (c) 2019, Zeruth <TheRealNull@gmail.com>
* 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.
*
*/
/*
Modified java.awt.Robot for use with RuneLitePlus. Hopefully we can make it stand far apart.
Uses
https://github.com/JoonasVali/NaturalMouseMotion
for mouse motion.
*/
package net.runelite.client.flexo;
import com.github.joonasvali.naturalmouse.api.MouseMotionFactory;
import java.awt.AWTException;
import java.awt.Color;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.peer.RobotPeer;
import java.util.Random;
import java.util.logging.Logger;
import net.runelite.api.Client;
import net.runelite.api.Constants;
import net.runelite.client.ui.ClientUI;
import sun.awt.ComponentFactory;
public class Flexo extends Robot
{
public ThreadGroup flexoThreads = new ThreadGroup("flexo");
public static boolean isActive;
public static double scale;
public static Client client;
public static ClientUI clientUI;
public static final int fixedWidth = Constants.GAME_FIXED_WIDTH;
public static final int fixedHeight = Constants.GAME_FIXED_HEIGHT;
public static boolean isStretched;
public static int minDelay = 45;
public static MouseMotionFactory currentMouseMotionFactory;
public boolean pausedIndefinitely = false;
private RobotPeer peer;
public Flexo() throws AWTException
{
if (GraphicsEnvironment.isHeadless())
{
throw new AWTException("headless environment");
}
init(GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice());
}
private void init(GraphicsDevice screen) throws AWTException
{
Toolkit toolkit = Toolkit.getDefaultToolkit();
if (toolkit instanceof ComponentFactory)
{
peer = ((ComponentFactory) toolkit).createRobot(this, screen);
RobotDisposer disposer = new RobotDisposer(peer);
sun.java2d.Disposer.addRecord(anchor, disposer);
}
}
private transient Object anchor = new Object();
static class RobotDisposer implements sun.java2d.DisposerRecord
{
private final RobotPeer peer;
private RobotDisposer(RobotPeer peer)
{
this.peer = peer;
}
public void dispose()
{
if (peer != null)
{
peer.dispose();
}
}
}
private void pauseMS(int delayMS)
{
long initialMS = System.currentTimeMillis();
while (System.currentTimeMillis() < initialMS + delayMS)
{
try
{
Thread.sleep(10);
}
catch (Exception e)
{
e.printStackTrace();
}
}
isActive = false;
}
@Override
public synchronized void mouseMove(int x, int y)
{
try
{
//TODO: Must be better way to determine titlebar width
currentMouseMotionFactory.build(ClientUI.frame.getX() + x + determineHorizontalOffset(), ClientUI.frame.getY() + y + determineVerticalOffset()).move();
this.delay(getMinDelay());
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
public synchronized void mouseMove(Point p)
{
mouseMove((int) p.getX(), (int) p.getY());
try
{
Thread.sleep(150);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
@Override
public synchronized void mousePress(int buttonID)
{
if (buttonID < 1 || buttonID > 5)
{
Logger.getAnonymousLogger().warning("Invalid mouse button ID. please use 1-5.");
return;
}
peer.mousePress(InputEvent.getMaskForButton(buttonID));
this.delay(getMinDelay());
}
public synchronized void mousePressAndRelease(int buttonID)
{
if (buttonID < 1 || buttonID > 5)
{
Logger.getAnonymousLogger().warning("Invalid mouse button ID. please use 1-5.");
return;
}
peer.mousePress(InputEvent.getMaskForButton(buttonID));
this.delay(getMinDelay());
peer.mouseRelease(InputEvent.getMaskForButton(buttonID));
this.delay(getMinDelay());
}
//TODO: Symbols are nut supported at this time
public synchronized void typeMessage(String message)
{
Random r = new Random();
char[] charArray = message.toCharArray();
for (char c : charArray)
{
keyPress(KeyEvent.getExtendedKeyCodeForChar(c));
this.delay(93 + r.nextInt(getMinDelay()));
}
keyPress(KeyEvent.VK_ENTER);
this.delay(93 + r.nextInt(getMinDelay()));
ClientUI.allowInput = true;
}
@Override
public synchronized void mouseRelease(int buttonID)
{
if (buttonID < 1 || buttonID > 5)
{
Logger.getAnonymousLogger().warning("Invalid mouse button ID. please use 1-5.");
return;
}
peer.mouseRelease(InputEvent.getMaskForButton(buttonID));
this.delay(getMinDelay());
}
private int getMinDelay()
{
Random random = new Random();
int random1 = random.nextInt(minDelay);
if (random1 < minDelay / 2)
{
random1 = random.nextInt(minDelay / 2) + minDelay / 2 + random.nextInt(minDelay / 2);
}
return random1;
}
private int getWheelDelay()
{
Random random = new Random();
int random1 = random.nextInt(minDelay);
if (random1 < minDelay / 2)
{
random1 = random.nextInt(minDelay / 2) + minDelay / 2 + random.nextInt(minDelay / 2);
}
return random1;
}
/**
* Rotates the scroll wheel on wheel-equipped mice.
*
* @param wheelAmt number of "notches" to move the mouse wheel
* Negative values indicate movement up/away from the user,
* positive values indicate movement down/towards the user.
* @since 1.4
*/
@Override
public synchronized void mouseWheel(int wheelAmt)
{
for (int i : new int[wheelAmt])
{
peer.mouseWheel(wheelAmt);
this.delay(getWheelDelay());
}
}
/**
* Presses a given key. The key should be released using the
* <code>keyRelease</code> method.
* <p>
* Key codes that have more than one physical key associated with them
* (e.g. <code>KeyEvent.VK_SHIFT</code> could mean either the
* left or right shift key) will map to the left key.
*
* @param keycode Key to press (e.g. <code>KeyEvent.VK_A</code>)
* @throws IllegalArgumentException if <code>keycode</code> is not
* a valid key
* @see #keyRelease(int)
* @see java.awt.event.KeyEvent
*/
@Override
public synchronized void keyPress(int keycode)
{
peer.keyPress(keycode);
this.delay(getMinDelay());
}
@Override
public synchronized void keyRelease(int keycode)
{
peer.keyRelease(keycode);
this.delay(getMinDelay());
}
public synchronized void holdKey(int keycode, int timeMS)
{
new Thread(() ->
{
peer.keyPress(keycode);
long startTime = System.currentTimeMillis();
while ((startTime + timeMS) > System.currentTimeMillis())
{
}
peer.keyRelease(keycode);
this.delay(getMinDelay());
}).start();
}
public synchronized void holdKeyIndefinitely(int keycode)
{
Thread holdKeyThread = new Thread(() ->
{
pausedIndefinitely = true;
peer.keyPress(keycode);
while (pausedIndefinitely)
{
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
peer.keyRelease(keycode);
this.delay(getMinDelay());
});
holdKeyThread.start();
}
@Override
public Color getPixelColor(int x, int y)
{
return new Color(peer.getRGBPixel(x, y));
}
@Override
public void delay(int ms)
{
pauseMS(ms);
}
public int determineHorizontalOffset()
{
return clientUI.getCanvasOffset().getX();
}
public int determineVerticalOffset()
{
return clientUI.getCanvasOffset().getY();
}
}

View File

@@ -1,225 +0,0 @@
/*
*
* Copyright (c) 2019, Zeruth <TheRealNull@gmail.com>
* 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.flexo;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.Random;
import java.util.logging.Logger;
import net.runelite.api.Constants;
import net.runelite.client.ui.ClientUI;
public class FlexoMouse
{
/*
Should pass unstretched coords, handles all conversions here.
*/
public static Point getClickPoint(Rectangle rect)
{
if (rect != null)
{
Random r = new Random();
int x = -1;
int y = -1;
x = rect.x + r.nextInt(rect.width);
y = rect.y + r.nextInt(rect.height);
if (Flexo.isStretched)
{
double wScale;
double hScale;
if (Flexo.client.isResized())
{
wScale = (Flexo.client.getStretchedDimensions().width / (double) Flexo.client.getRealDimensions().width);
hScale = (Flexo.client.getStretchedDimensions().height / (double) Flexo.client.getRealDimensions().height);
int newX = (int) (x * wScale);
int newY = (int) (y * hScale);
if (newX > 0 && newX < ClientUI.frame.getWidth())
{
if (newY > 0 && newY < ClientUI.frame.getHeight())
{
return new Point(newX, newY);
}
}
Logger.getAnonymousLogger().warning("[RuneLit]Flexo - Off screen point attempted. Split the step, or rotate the screen.");
return null;
}
else
{
if (x > 0 && x < ClientUI.frame.getWidth())
{
if (y > 0 && y < ClientUI.frame.getHeight())
{
return new Point(x, y);
}
}
Logger.getAnonymousLogger().warning("[RuneLit]Flexo - Off screen point attempted. Split the step, or rotate the screen.");
return null;
}
}
else if (!Flexo.client.isResized())
{
final int fixedWidth = Constants.GAME_FIXED_WIDTH;
int widthDif = ClientUI.frame.getWidth();
if (ClientUI.pluginToolbar.isVisible())
{
widthDif -= ClientUI.pluginToolbar.getWidth();
}
if (ClientUI.pluginPanel != null)
{
widthDif -= ClientUI.pluginPanel.getWidth();
}
widthDif -= fixedWidth;
if (x + (widthDif / 2) > 0 && x + (widthDif / 2) < ClientUI.frame.getWidth())
{
if (y > 0 && y < ClientUI.frame.getHeight())
{
return new Point(x, y);
}
}
Logger.getAnonymousLogger().warning("[RuneLit]Flexo - Off screen point attempted. Split the step, or rotate the screen.");
return null;
}
else
{
if (x > 0 && x < ClientUI.frame.getWidth())
{
if (y > 0 && y < ClientUI.frame.getHeight())
{
return new Point(x, y);
}
}
Logger.getAnonymousLogger().warning("[RuneLit]Flexo - Off screen point attempted. Split the step, or rotate the screen.");
return null;
}
}
return null;
}
public static Rectangle getClickArea(Rectangle rect)
{
if (Flexo.isStretched)
{
double wScale;
double hScale;
if (Flexo.client.isResized())
{
wScale = (Flexo.client.getStretchedDimensions().width / (double) Flexo.client.getRealDimensions().width);
hScale = (Flexo.client.getStretchedDimensions().height / (double) Flexo.client.getRealDimensions().height);
}
else
{
wScale = (Flexo.client.getStretchedDimensions().width) / (double) Flexo.fixedWidth;
hScale = (Flexo.client.getStretchedDimensions().height) / (double) Flexo.fixedHeight;
}
int xPadding = (int) rect.getWidth() / 5;
int yPadding = (int) rect.getHeight() / 5;
Random r = new Random();
Rectangle clickRect = new Rectangle();
clickRect.width = rect.width - xPadding * 2;
clickRect.height = rect.height - yPadding * 2;
clickRect.x = rect.x + xPadding;
clickRect.y = rect.y + yPadding;
if (clickRect.width > 0 && clickRect.height > 0)
{
int x = clickRect.x + r.nextInt(clickRect.width);
int y = clickRect.y + r.nextInt(clickRect.height);
double tScale = 1 + (Flexo.scale / 100);
if (Flexo.client.isResized())
{
return new Rectangle((int) (clickRect.x * wScale), (int) (clickRect.y * wScale), (int) (clickRect.width * wScale), (int) (clickRect.getHeight() * hScale));
}
else
{
return new Rectangle(clickRect.x, clickRect.y, clickRect.width, (int) (clickRect.getHeight()));
}
}
}
//Fixed, not stretched
else if (!Flexo.client.isResized())
{
int fixedWidth = 765;
int widthDif = ClientUI.frame.getWidth();
if (ClientUI.pluginToolbar.isVisible())
{
widthDif -= ClientUI.pluginToolbar.getWidth();
}
if (ClientUI.pluginPanel != null)
{
widthDif -= ClientUI.pluginPanel.getWidth();
}
widthDif -= fixedWidth;
int xPadding = (int) rect.getWidth() / 5;
int yPadding = (int) rect.getHeight() / 5;
Random r = new Random();
Rectangle clickRect = new Rectangle();
clickRect.width = rect.width - xPadding;
clickRect.height = rect.height - yPadding;
clickRect.x = rect.x + xPadding;
clickRect.y = rect.y + yPadding;
if (clickRect.height > 0 && clickRect.width > 0)
{
int x = clickRect.x + r.nextInt(clickRect.width);
int y = clickRect.y + r.nextInt(clickRect.height);
return new Rectangle(clickRect.x, clickRect.y, clickRect.width, (int) (clickRect.getHeight()));
}
}
//Resizable, not stretched
else
{
int xPadding = (int) rect.getWidth() / 5;
int yPadding = (int) rect.getHeight() / 5;
Random r = new Random();
Rectangle clickRect = new Rectangle();
clickRect.width = rect.width - xPadding * 2;
clickRect.height = rect.height - yPadding * 2;
clickRect.x = rect.x + xPadding;
clickRect.y = rect.y + yPadding;
if (clickRect.height > 0 && clickRect.width > 0)
{
int x = clickRect.x + r.nextInt(clickRect.width);
int y = clickRect.y + r.nextInt(clickRect.height);
return new Rectangle(clickRect.x, clickRect.y, clickRect.width, (int) (clickRect.getHeight()));
}
}
return null;
}
}

View File

@@ -1,452 +0,0 @@
/*
* Copyright (c) 2018, SomeoneWithAnInternetConnection
* Copyright (c) 2019, MrGroggle
* 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 lombok.Getter;
import static net.runelite.api.NullObjectID.NULL_25337;
import static net.runelite.api.NullObjectID.NULL_26371;
import static net.runelite.api.NullObjectID.NULL_26375;
import static net.runelite.api.NullObjectID.NULL_26884;
import static net.runelite.api.NullObjectID.NULL_26886;
import static net.runelite.api.NullObjectID.NULL_29868;
import static net.runelite.api.NullObjectID.NULL_29869;
import static net.runelite.api.NullObjectID.NULL_29870;
import static net.runelite.api.NullObjectID.NULL_31823;
import static net.runelite.api.NullObjectID.NULL_31849;
import static net.runelite.api.NullObjectID.NULL_33327;
import static net.runelite.api.NullObjectID.NULL_33328;
import static net.runelite.api.ObjectID.A_WOODEN_LOG;
import static net.runelite.api.ObjectID.BALANCING_LEDGE_23548;
import static net.runelite.api.ObjectID.BIG_WINDOW;
import static net.runelite.api.ObjectID.BOULDER_27990;
import static net.runelite.api.ObjectID.BROKEN_FENCE;
import static net.runelite.api.ObjectID.BROKEN_FENCE_2618;
import static net.runelite.api.ObjectID.BROKEN_RAFT;
import static net.runelite.api.ObjectID.BROKEN_WALL_33344;
import static net.runelite.api.ObjectID.CASTLE_WALL;
import static net.runelite.api.ObjectID.CLIMBING_ROCKS;
import static net.runelite.api.ObjectID.CLIMBING_ROCKS_11948;
import static net.runelite.api.ObjectID.CLIMBING_ROCKS_11949;
import static net.runelite.api.ObjectID.CREVICE_16465;
import static net.runelite.api.ObjectID.CREVICE_16539;
import static net.runelite.api.ObjectID.CREVICE_16543;
import static net.runelite.api.ObjectID.CREVICE_19043;
import static net.runelite.api.ObjectID.CREVICE_30198;
import static net.runelite.api.ObjectID.CREVICE_9739;
import static net.runelite.api.ObjectID.CREVICE_9740;
import static net.runelite.api.ObjectID.CROSSBOW_TREE_17062;
import static net.runelite.api.ObjectID.CRUMBLING_WALL_24222;
import static net.runelite.api.ObjectID.DARK_TUNNEL_10047;
import static net.runelite.api.ObjectID.DENSE_FOREST;
import static net.runelite.api.ObjectID.DENSE_FOREST_3938;
import static net.runelite.api.ObjectID.DENSE_FOREST_3939;
import static net.runelite.api.ObjectID.DENSE_FOREST_3998;
import static net.runelite.api.ObjectID.DENSE_FOREST_3999;
import static net.runelite.api.ObjectID.FALLEN_TREE_33192;
import static net.runelite.api.ObjectID.FENCE_16518;
import static net.runelite.api.ObjectID.GAP;
import static net.runelite.api.ObjectID.GAP_2831;
import static net.runelite.api.ObjectID.GAP_29326;
import static net.runelite.api.ObjectID.HOLE_16520;
import static net.runelite.api.ObjectID.HOLE_30966;
import static net.runelite.api.ObjectID.HOLE_31481;
import static net.runelite.api.ObjectID.HOLE_31482;
import static net.runelite.api.ObjectID.ICE_CHUNKS_31822;
import static net.runelite.api.ObjectID.ICE_CHUNKS_31990;
import static net.runelite.api.ObjectID.JUTTING_WALL_17002;
import static net.runelite.api.ObjectID.LADDER_30938;
import static net.runelite.api.ObjectID.LADDER_30939;
import static net.runelite.api.ObjectID.LADDER_30940;
import static net.runelite.api.ObjectID.LADDER_30941;
import static net.runelite.api.ObjectID.LEAVES;
import static net.runelite.api.ObjectID.LEAVES_3924;
import static net.runelite.api.ObjectID.LEAVES_3925;
import static net.runelite.api.ObjectID.LEDGE_33190;
import static net.runelite.api.ObjectID.LITTLE_BOULDER;
import static net.runelite.api.ObjectID.LOG_BALANCE_16540;
import static net.runelite.api.ObjectID.LOG_BALANCE_16541;
import static net.runelite.api.ObjectID.LOG_BALANCE_16542;
import static net.runelite.api.ObjectID.LOG_BALANCE_16546;
import static net.runelite.api.ObjectID.LOG_BALANCE_16547;
import static net.runelite.api.ObjectID.LOG_BALANCE_16548;
import static net.runelite.api.ObjectID.LOG_BALANCE_20882;
import static net.runelite.api.ObjectID.LOG_BALANCE_20884;
import static net.runelite.api.ObjectID.LOG_BALANCE_23274;
import static net.runelite.api.ObjectID.LOG_BALANCE_3929;
import static net.runelite.api.ObjectID.LOG_BALANCE_3930;
import static net.runelite.api.ObjectID.LOG_BALANCE_3931;
import static net.runelite.api.ObjectID.LOG_BALANCE_3932;
import static net.runelite.api.ObjectID.LOG_BALANCE_3933;
import static net.runelite.api.ObjectID.LOOSE_RAILING;
import static net.runelite.api.ObjectID.LOOSE_RAILING_2186;
import static net.runelite.api.ObjectID.LOOSE_RAILING_28849;
import static net.runelite.api.ObjectID.LOW_FENCE;
import static net.runelite.api.ObjectID.MONKEYBARS_23566;
import static net.runelite.api.ObjectID.MONKEYBARS_23567;
import static net.runelite.api.ObjectID.MYSTERIOUS_PIPE;
import static net.runelite.api.ObjectID.OBSTACLE;
import static net.runelite.api.ObjectID.OBSTACLE_30767;
import static net.runelite.api.ObjectID.OBSTACLE_30962;
import static net.runelite.api.ObjectID.OBSTACLE_30964;
import static net.runelite.api.ObjectID.OBSTACLE_PIPE_16509;
import static net.runelite.api.ObjectID.OBSTACLE_PIPE_16511;
import static net.runelite.api.ObjectID.OBSTACLE_PIPE_23140;
import static net.runelite.api.ObjectID.ORNATE_RAILING;
import static net.runelite.api.ObjectID.ORNATE_RAILING_17000;
import static net.runelite.api.ObjectID.PILE_OF_RUBBLE_23563;
import static net.runelite.api.ObjectID.PILE_OF_RUBBLE_23564;
import static net.runelite.api.ObjectID.PILLAR_31561;
import static net.runelite.api.ObjectID.PILLAR_31809;
import static net.runelite.api.ObjectID.PIPE_21727;
import static net.runelite.api.ObjectID.PIPE_21728;
import static net.runelite.api.ObjectID.ROCKS;
import static net.runelite.api.ObjectID.ROCKSLIDE_33184;
import static net.runelite.api.ObjectID.ROCKSLIDE_33185;
import static net.runelite.api.ObjectID.ROCKSLIDE_33191;
import static net.runelite.api.ObjectID.ROCKS_14106;
import static net.runelite.api.ObjectID.ROCKS_16464;
import static net.runelite.api.ObjectID.ROCKS_16514;
import static net.runelite.api.ObjectID.ROCKS_16515;
import static net.runelite.api.ObjectID.ROCKS_16521;
import static net.runelite.api.ObjectID.ROCKS_16522;
import static net.runelite.api.ObjectID.ROCKS_16523;
import static net.runelite.api.ObjectID.ROCKS_16524;
import static net.runelite.api.ObjectID.ROCKS_16534;
import static net.runelite.api.ObjectID.ROCKS_16535;
import static net.runelite.api.ObjectID.ROCKS_16545;
import static net.runelite.api.ObjectID.ROCKS_16549;
import static net.runelite.api.ObjectID.ROCKS_16550;
import static net.runelite.api.ObjectID.ROCKS_16998;
import static net.runelite.api.ObjectID.ROCKS_16999;
import static net.runelite.api.ObjectID.ROCKS_17042;
import static net.runelite.api.ObjectID.ROCKS_19849;
import static net.runelite.api.ObjectID.ROCKS_2231;
import static net.runelite.api.ObjectID.ROCKS_27984;
import static net.runelite.api.ObjectID.ROCKS_27985;
import static net.runelite.api.ObjectID.ROCKS_27987;
import static net.runelite.api.ObjectID.ROCKS_27988;
import static net.runelite.api.ObjectID.ROCKS_31757;
import static net.runelite.api.ObjectID.ROCKS_31758;
import static net.runelite.api.ObjectID.ROCKS_31759;
import static net.runelite.api.ObjectID.ROCKS_34396;
import static net.runelite.api.ObjectID.ROCKS_34397;
import static net.runelite.api.ObjectID.ROCKS_34741;
import static net.runelite.api.ObjectID.ROCKS_3748;
import static net.runelite.api.ObjectID.ROCKS_3790;
import static net.runelite.api.ObjectID.ROCKS_3791;
import static net.runelite.api.ObjectID.ROCKS_3803;
import static net.runelite.api.ObjectID.ROCKS_3804;
import static net.runelite.api.ObjectID.ROCKS_6673;
import static net.runelite.api.ObjectID.ROCKY_HANDHOLDS_26400;
import static net.runelite.api.ObjectID.ROCKY_HANDHOLDS_26401;
import static net.runelite.api.ObjectID.ROCKY_HANDHOLDS_26402;
import static net.runelite.api.ObjectID.ROCKY_HANDHOLDS_26404;
import static net.runelite.api.ObjectID.ROCKY_HANDHOLDS_26405;
import static net.runelite.api.ObjectID.ROCKY_HANDHOLDS_26406;
import static net.runelite.api.ObjectID.ROCK_16115;
import static net.runelite.api.ObjectID.ROPESWING_23568;
import static net.runelite.api.ObjectID.ROPESWING_23569;
import static net.runelite.api.ObjectID.ROPE_ANCHOR;
import static net.runelite.api.ObjectID.ROPE_ANCHOR_30917;
import static net.runelite.api.ObjectID.ROPE_BRIDGE_21306;
import static net.runelite.api.ObjectID.ROPE_BRIDGE_21307;
import static net.runelite.api.ObjectID.ROPE_BRIDGE_21308;
import static net.runelite.api.ObjectID.ROPE_BRIDGE_21309;
import static net.runelite.api.ObjectID.ROPE_BRIDGE_21310;
import static net.runelite.api.ObjectID.ROPE_BRIDGE_21311;
import static net.runelite.api.ObjectID.ROPE_BRIDGE_21312;
import static net.runelite.api.ObjectID.ROPE_BRIDGE_21313;
import static net.runelite.api.ObjectID.ROPE_BRIDGE_21314;
import static net.runelite.api.ObjectID.ROPE_BRIDGE_21315;
import static net.runelite.api.ObjectID.RUBBER_CAP_MUSHROOM;
import static net.runelite.api.ObjectID.SPIKEY_CHAIN;
import static net.runelite.api.ObjectID.SPIKEY_CHAIN_16538;
import static net.runelite.api.ObjectID.STAIRS_31485;
import static net.runelite.api.ObjectID.STEPPING_STONES;
import static net.runelite.api.ObjectID.STEPPING_STONES_23646;
import static net.runelite.api.ObjectID.STEPPING_STONES_23647;
import static net.runelite.api.ObjectID.STEPPING_STONE_10663;
import static net.runelite.api.ObjectID.STEPPING_STONE_11768;
import static net.runelite.api.ObjectID.STEPPING_STONE_13504;
import static net.runelite.api.ObjectID.STEPPING_STONE_14917;
import static net.runelite.api.ObjectID.STEPPING_STONE_14918;
import static net.runelite.api.ObjectID.STEPPING_STONE_16466;
import static net.runelite.api.ObjectID.STEPPING_STONE_16513;
import static net.runelite.api.ObjectID.STEPPING_STONE_16533;
import static net.runelite.api.ObjectID.STEPPING_STONE_19040;
import static net.runelite.api.ObjectID.STEPPING_STONE_19042;
import static net.runelite.api.ObjectID.STEPPING_STONE_21738;
import static net.runelite.api.ObjectID.STEPPING_STONE_21739;
import static net.runelite.api.ObjectID.STEPPING_STONE_29728;
import static net.runelite.api.ObjectID.STEPPING_STONE_29729;
import static net.runelite.api.ObjectID.STEPPING_STONE_29730;
import static net.runelite.api.ObjectID.STEPPING_STONE_5948;
import static net.runelite.api.ObjectID.STEPPING_STONE_5949;
import static net.runelite.api.ObjectID.STEPS;
import static net.runelite.api.ObjectID.STEPS_29993;
import static net.runelite.api.ObjectID.STICKS;
import static net.runelite.api.ObjectID.STILE;
import static net.runelite.api.ObjectID.STILE_12982;
import static net.runelite.api.ObjectID.STRANGE_FLOOR;
import static net.runelite.api.ObjectID.STRANGE_FLOOR_16544;
import static net.runelite.api.ObjectID.STRONG_TREE_17074;
import static net.runelite.api.ObjectID.TIGHTGAP;
import static net.runelite.api.ObjectID.TRELLIS_20056;
import static net.runelite.api.ObjectID.TRIPWIRE;
import static net.runelite.api.ObjectID.TUNNEL_30174;
import static net.runelite.api.ObjectID.TUNNEL_30175;
import static net.runelite.api.ObjectID.TUNNEL_30959;
import static net.runelite.api.ObjectID.UNDERWALL_TUNNEL;
import static net.runelite.api.ObjectID.UNDERWALL_TUNNEL_16528;
import static net.runelite.api.ObjectID.UNDERWALL_TUNNEL_16529;
import static net.runelite.api.ObjectID.UNDERWALL_TUNNEL_16530;
import static net.runelite.api.ObjectID.UNDERWALL_TUNNEL_19032;
import static net.runelite.api.ObjectID.UNDERWALL_TUNNEL_19036;
import static net.runelite.api.ObjectID.VINE_26880;
import static net.runelite.api.ObjectID.VINE_26882;
import static net.runelite.api.ObjectID.WALL_17047;
import static net.runelite.api.ObjectID.WALL_17049;
import static net.runelite.api.ObjectID.WALL_17050;
import static net.runelite.api.ObjectID.WEATHERED_WALL;
import static net.runelite.api.ObjectID.WEATHERED_WALL_16526;
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,
// Tree Gnome Village
LOOSE_RAILING_2186,
// Burgh de Rott
LOW_FENCE,
// Taverley
STILE,
// Asgarnian Ice Dungeon
STEPS,
// Fossil Island Wyvern Cave
STAIRS_31485),
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),
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),
KARAMJA_MOSS_GIANT_SWING(10, "Rope", null, ROPESWING_23568, ROPESWING_23569),
FALADOR_GRAPPLE_WALL(11, "Grapple Wall", new WorldPoint(3031, 3391, 0), WALL_17049, WALL_17050),
BRIMHAVEN_DUNGEON_STEPPING_STONES(12, "Stepping Stones", null, STEPPING_STONE_21738),
VARROCK_SOUTH_FENCE(13, "Fence", new WorldPoint(3239, 3334, 0), FENCE_16518),
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
YANILLE_UNDERWALL_TUNNEL(16, "Underwall Tunnel", new WorldPoint(2574, 3109, 0), HOLE_16520, CASTLE_WALL),
YANILLE_WATCHTOWER_TRELLIS(18, "Trellis", null, TRELLIS_20056),
COAL_TRUCKS_LOG_BALANCE(20, "Log Balance", new WorldPoint(2598, 3475, 0), LOG_BALANCE_23274),
GRAND_EXCHANGE_UNDERWALL_TUNNEL(21, "Underwall Tunnel", new WorldPoint(3139, 3515, 0), UNDERWALL_TUNNEL_16529, UNDERWALL_TUNNEL_16530),
BRIMHAVEN_DUNGEON_PIPE(22, "Pipe Squeeze", new WorldPoint(2654, 9569, 0), PIPE_21728),
OBSERVATORY_SCALE_CLIFF(23, "Grapple Rocks", new WorldPoint(2447, 3155, 0), NULL_31849),
EAGLES_PEAK_ROCK_CLIMB(25, "Rock Climb", new WorldPoint(2320, 3499, 0), ROCKS_19849),
FALADOR_UNDERWALL_TUNNEL(26, "Underwall Tunnel", new WorldPoint(2947, 3313, 0), UNDERWALL_TUNNEL, UNDERWALL_TUNNEL_16528),
MOUNT_KARUULM_LOWER(29, "Rocks", new WorldPoint(1324, 3782, 0), ROCKS_34397),
CORSAIR_COVE_RESOURCE_ROCKS(30, "Rocks", new WorldPoint(2486, 2898, 0), ROCKS_31758, ROCKS_31759),
SOUTHEAST_KARAJMA_STEPPING_STONES(30, "Stepping Stones", new WorldPoint(2924, 2946, 0), STEPPING_STONES, STEPPING_STONES_23646, STEPPING_STONES_23647),
BRIMHAVEN_DUNGEON_LOG_BALANCE(30, "Log Balance", null, LOG_BALANCE_20882),
AGILITY_PYRAMID_ROCKS_EAST(30, "Rocks", null, CLIMBING_ROCKS_11949),
DRAYNOR_MANOR_STEPPING_STONES(31, "Stepping Stones", new WorldPoint(3150, 3362, 0), STEPPING_STONE_16533),
CATHERBY_CLIFFSIDE_GRAPPLE(32, "Grapple Rock", new WorldPoint(2868, 3429, 0), ROCKS_17042),
CAIRN_ISLE_ROCKS(32, "Rocks", null, ROCKS_2231),
ARDOUGNE_LOG_BALANCE(33, "Log Balance", new WorldPoint(2602, 3336, 0), LOG_BALANCE_16546, LOG_BALANCE_16547, LOG_BALANCE_16548),
BRIMHAVEN_DUNGEON_MEDIUM_PIPE(34, "Pipe Squeeze", null, new WorldPoint(2698, 9501, 0), PIPE_21727),
CATHERBY_OBELISK_GRAPPLE(36, "Grapple Rock", new WorldPoint(2841, 3434, 0), CROSSBOW_TREE_17062),
GNOME_STRONGHOLD_ROCKS(37, "Rocks", new WorldPoint(2485, 3515, 0), ROCKS_16534, ROCKS_16535),
AL_KHARID_MINING_PITCLIFF_SCRAMBLE(38, "Rocks", new WorldPoint(3305, 3315, 0), ROCKS_16549, ROCKS_16550),
YANILLE_WALL_GRAPPLE(39, "Grapple Wall", new WorldPoint(2552, 3072, 0), WALL_17047),
NEITIZNOT_BRIDGE_REPAIR(40, "Bridge Repair - Quest", new WorldPoint(2315, 3828, 0), ROPE_BRIDGE_21306, ROPE_BRIDGE_21307),
NEITIZNOT_BRIDGE_SOUTHEAST(40, "Rope Bridge", null, ROPE_BRIDGE_21308, ROPE_BRIDGE_21309),
NEITIZNOT_BRIDGE_NORTHWEST(40, "Rope Bridge", null, ROPE_BRIDGE_21310, ROPE_BRIDGE_21311),
NEITIZNOT_BRIDGE_NORTH(40, "Rope Bridge", null, ROPE_BRIDGE_21312, ROPE_BRIDGE_21313),
NEITIZNOT_BRIDGE_NORTHEAST(40, "Broken Rope bridge", null, ROPE_BRIDGE_21314, ROPE_BRIDGE_21315),
KOUREND_LAKE_JUMP_EAST(40, "Stepping Stones", new WorldPoint(1612, 3570, 0), STEPPING_STONE_29729, STEPPING_STONE_29730),
KOUREND_LAKE_JUMP_WEST(40, "Stepping Stones", new WorldPoint(1604, 3572, 0), STEPPING_STONE_29729, STEPPING_STONE_29730),
YANILLE_DUNGEON_BALANCE(40, "Balancing Ledge", null, BALANCING_LEDGE_23548),
TROLLHEIM_EASY_CLIFF_SCRAMBLE(41, "Rocks", new WorldPoint(2869, 3670, 0), ROCKS_16521),
DWARVEN_MINE_NARROW_CREVICE(42, "Narrow Crevice", new WorldPoint(3034, 9806, 0), CREVICE_16543),
DRAYNOR_UNDERWALL_TUNNEL(42, "Underwall Tunnel", new WorldPoint(3068, 3261, 0), UNDERWALL_TUNNEL_19032, UNDERWALL_TUNNEL_19036),
TROLLHEIM_MEDIUM_CLIFF_SCRAMBLE_NORTH(43, "Rocks", new WorldPoint(2886, 3684, 0), ROCKS_3803, ROCKS_3804, ROCKS_16522),
TROLLHEIM_MEDIUM_CLIFF_SCRAMBLE_SOUTH(43, "Rocks", new WorldPoint(2876, 3666, 0), ROCKS_3803, ROCKS_3804, ROCKS_16522),
TROLLHEIM_ADVANCED_CLIFF_SCRAMBLE(44, "Rocks", new WorldPoint(2907, 3686, 0), ROCKS_16523, ROCKS_3748),
KOUREND_RIVER_STEPPING_STONES(45, "Stepping Stones", new WorldPoint(1721, 3509, 0), STEPPING_STONE_29728),
TIRANNWN_LOG_BALANCE(45, "Log Balance", null, LOG_BALANCE_3933, LOG_BALANCE_3931, LOG_BALANCE_3930, LOG_BALANCE_3929, LOG_BALANCE_3932),
COSMIC_ALTAR_MEDIUM_WALKWAY(46, "Narrow Walkway", new WorldPoint(2399, 4403, 0), JUTTING_WALL_17002),
DEEP_WILDERNESS_DUNGEON_CREVICE_NORTH(46, "Narrow Crevice", new WorldPoint(3047, 10335, 0), CREVICE_19043),
DEEP_WILDERNESS_DUNGEON_CREVICE_SOUTH(46, "Narrow Crevice", new WorldPoint(3045, 10327, 0), CREVICE_19043),
TROLLHEIM_HARD_CLIFF_SCRAMBLE(47, "Rocks", new WorldPoint(2902, 3680, 0), ROCKS_16524),
FREMENNIK_LOG_BALANCE(48, "Log Balance", new WorldPoint(2721, 3591, 0), LOG_BALANCE_16540, LOG_BALANCE_16541, LOG_BALANCE_16542),
YANILLE_DUNGEON_PIPE_SQUEEZE(49, "Pipe Squeeze", null, OBSTACLE_PIPE_23140),
ARCEUUS_ESSENCE_MINE_BOULDER(49, "Boulder", new WorldPoint(1774, 3888, 0), BOULDER_27990),
MORYTANIA_STEPPING_STONE(50, "Stepping Stone", new WorldPoint(3418, 3326, 0), STEPPING_STONE_13504),
VARROCK_SEWERS_PIPE_SQUEEZE(51, "Pipe Squeeze", new WorldPoint(3152, 9905, 0), OBSTACLE_PIPE_16511),
ARCEUUS_ESSENCE_MINE_EAST_SCRAMBLE(52, "Rock Climb", new WorldPoint(1770, 3851, 0), ROCKS_27987, ROCKS_27988),
KARAMJA_VOLCANO_GRAPPLE_NORTH(53, "Grapple Rock", new WorldPoint(2873, 3143, 0), STRONG_TREE_17074),
KARAMJA_VOLCANO_GRAPPLE_SOUTH(53, "Grapple Rock", new WorldPoint(2874, 3128, 0), STRONG_TREE_17074),
MOTHERLODE_MINE_WALL_EAST(54, "Wall", new WorldPoint(3124, 9703, 0), DARK_TUNNEL_10047),
MOTHERLODE_MINE_WALL_WEST(54, "Wall", new WorldPoint(3118, 9702, 0), DARK_TUNNEL_10047),
MISCELLANIA_DOCK_STEPPING_STONE(55, "Stepping Stone", new WorldPoint(2572, 3862, 0), STEPPING_STONE_11768),
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),
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),
ELVEN_OVERPASS_CLIFF_SCRAMBLE(59, "Rocks", new WorldPoint(2345, 3300, 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),
WILDERNESS_GWD_CLIMB_WEST(60, "Rocks", new WorldPoint(2928, 3760, 0), ROCKY_HANDHOLDS_26400, ROCKY_HANDHOLDS_26401, ROCKY_HANDHOLDS_26402, ROCKY_HANDHOLDS_26404, ROCKY_HANDHOLDS_26405, ROCKY_HANDHOLDS_26406),
MOS_LEHARMLESS_STEPPING_STONE(60, "Stepping Stone", new WorldPoint(3710, 2970, 0), STEPPING_STONE_19042),
WINTERTODT_GAP(60, "Gap", new WorldPoint(1629, 4023, 0), GAP_29326),
UNGAEL_ICE(60, "Ice Chunks", null, NULL_25337, NULL_29868, NULL_29869, NULL_29870, ICE_CHUNKS_31822, NULL_31823, ICE_CHUNKS_31990),
SLAYER_TOWER_MEDIUM_CHAIN_FIRST(61, "Spiked Chain (Floor 1)", new WorldPoint(3421, 3550, 0), SPIKEY_CHAIN),
SLAYER_TOWER_MEDIUM_CHAIN_SECOND(61, "Spiked Chain (Floor 2)", new WorldPoint(3420, 3551, 0), SPIKEY_CHAIN_16538),
SLAYER_DUNGEON_CREVICE(62, "Narrow Crevice", new WorldPoint(2729, 10008, 0), CREVICE_16539),
MOUNT_KARUULM_UPPER(62, "Rocks", new WorldPoint(1322, 3791, 0), ROCKS_34396),
TAVERLEY_DUNGEON_RAILING(63, "Loose Railing", new WorldPoint(2935, 9811, 0), LOOSE_RAILING_28849),
TROLLHEIM_WILDERNESS_ROCKS_EAST(64, "Rocks", new WorldPoint(2945, 3678, 0), ROCKS_16545),
TROLLHEIM_WILDERNESS_ROCKS_WEST(64, "Rocks", new WorldPoint(2917, 3672, 0), ROCKS_16545),
FOSSIL_ISLAND_VOLCANO(64, "Rope", new WorldPoint(3780, 3822, 0), ROPE_ANCHOR, ROPE_ANCHOR_30917),
MORYTANIA_TEMPLE(65, "Loose Railing", new WorldPoint(3422, 3476, 0), ROCKS_16998, ROCKS_16999, ORNATE_RAILING, ORNATE_RAILING_17000),
REVENANT_CAVES_GREEN_DRAGONS(65, "Jump", new WorldPoint(3220, 10086, 0), PILLAR_31561),
COSMIC_ALTAR_ADVANCED_WALKWAY(66, "Narrow Walkway", new WorldPoint(2408, 4401, 0), JUTTING_WALL_17002),
LUMBRIDGE_DESERT_STEPPING_STONE(66, "Stepping Stone", new WorldPoint(3210, 3135, 0), STEPPING_STONE_16513),
HEROES_GUILD_TUNNEL_EAST(67, "Crevice", new WorldPoint(2898, 9901, 0), CREVICE_9739, CREVICE_9740),
HEROES_GUILD_TUNNEL_WEST(67, "Crevice", new WorldPoint(2913, 9895, 0), CREVICE_9739, CREVICE_9740),
YANILLE_DUNGEON_RUBBLE_CLIMB(67, "Pile of Rubble", null, PILE_OF_RUBBLE_23563, PILE_OF_RUBBLE_23564),
ELVEN_OVERPASS_MEDIUM_CLIFF(68, "Rocks", new WorldPoint(2337, 3288, 0), ROCKS_16514, ROCKS_16515),
WEISS_OBSTACLES(68, "Shortcut", null, LITTLE_BOULDER, ROCKSLIDE_33184, ROCKSLIDE_33185, NULL_33327, NULL_33328, LEDGE_33190, ROCKSLIDE_33191, FALLEN_TREE_33192),
ARCEUUS_ESSENSE_NORTH(69, "Rock Climb", new WorldPoint(1761, 3873, 0), ROCKS_34741),
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(3713, 3827, 0), HOLE_31481, HOLE_31482),
FOSSIL_ISLAND_HARDWOOD_SOUTH(70, "Hole", new WorldPoint(3715, 3817, 0), HOLE_31481, HOLE_31482),
AL_KHARID_WINDOW(70, "Window", new WorldPoint(3293, 3158, 0), BROKEN_WALL_33344, BIG_WINDOW),
GWD_SARADOMIN_ROPE_NORTH(70, "Rope Descent", new WorldPoint(2912, 5300, 0), NULL_26371),
GWD_SARADOMIN_ROPE_SOUTH(70, "Rope Descent", new WorldPoint(2951, 5267, 0), NULL_26375),
SLAYER_TOWER_ADVANCED_CHAIN_FIRST(71, "Spiked Chain (Floor 2)", new WorldPoint(3447, 3578, 0), SPIKEY_CHAIN),
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),
LAVA_DRAGON_ISLE_JUMP(74, "Stepping Stone", new WorldPoint(3200, 3807, 0), STEPPING_STONE_14918),
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),
KHARAZI_JUNGLE_VINE_CLIMB(79, "Vine", new WorldPoint(2897, 2939, 0), NULL_26884, NULL_26886),
TAVERLEY_DUNGEON_SPIKED_BLADES(80, "Strange Floor", new WorldPoint(2877, 9813, 0), STRANGE_FLOOR),
SLAYER_DUNGEON_CHASM_JUMP(81, "Spiked Blades", new WorldPoint(2770, 10003, 0), STRANGE_FLOOR_16544),
LAVA_MAZE_NORTH_JUMP(82, "Stepping Stone", new WorldPoint(3092, 3880, 0), STEPPING_STONE_14917),
BRIMHAVEN_DUNGEON_EAST_STEPPING_STONES_NORTH(83, "Stepping Stones", new WorldPoint(2685, 9547, 0), STEPPING_STONE_19040),
BRIMHAVEN_DUNGEON_EAST_STEPPING_STONES_SOUTH(83, "Stepping Stones", new WorldPoint(2693, 9529, 0), STEPPING_STONE_19040),
ELVEN_ADVANCED_CLIFF_SCRAMBLE(85, "Rocks", new WorldPoint(2337, 3253, 0), ROCKS_16514, ROCKS_16514),
KALPHITE_WALL(86, "Crevice", new WorldPoint(3214, 9508, 0), CREVICE_16465),
BRIMHAVEN_DUNGEON_VINE_EAST(87, "Vine", new WorldPoint(2672, 9582, 0), VINE_26880, VINE_26882),
BRIMHAVEN_DUNGEON_VINE_WEST(87, "Vine", new WorldPoint(2606, 9584, 0), VINE_26880, VINE_26882),
MOUNT_KARUULM_PIPE_SOUTH(88, "Pipe", new WorldPoint(1316, 10214, 0), MYSTERIOUS_PIPE),
MOUNT_KARUULM_PIPE_NORTH(88, "Pipe", new WorldPoint(1346, 10231, 0), MYSTERIOUS_PIPE),
REVENANT_CAVES_CHAMBER_JUMP(89, "Jump", new WorldPoint(3240, 10144, 0), PILLAR_31561);
/**
* The agility level required to pass the shortcut
*/
@Getter
private final int level;
/**
* Brief description of the shortcut (e.g. 'Rocks', 'Stepping Stones', 'Jump')
*/
@Getter
private final String description;
/**
* The location of the Shortcut icon on the world map (null if there is no icon)
*/
@Getter
private final WorldPoint worldMapLocation;
/**
* An optional location in case the location of the shortcut icon is either
* null or isn't close enough to the obstacle
*/
@Getter
private final WorldPoint worldLocation;
/**
* Array of obstacles, null objects, decorations etc. that this shortcut uses.
* Typically an ObjectID/NullObjectID
*/
@Getter
private final int[] obstacleIds;
AgilityShortcut(int level, String description, WorldPoint mapLocation, WorldPoint worldLocation, int... obstacleIds)
{
this.level = level;
this.description = description;
this.worldMapLocation = mapLocation;
this.worldLocation = worldLocation;
this.obstacleIds = obstacleIds;
}
AgilityShortcut(int level, String description, WorldPoint location, int... obstacleIds)
{
this(level, description, location, location, obstacleIds);
}
public String getTooltip()
{
return description + " - Level " + level;
}
}

View File

@@ -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.game;
import java.awt.image.BufferedImage;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
public class AsyncBufferedImage extends BufferedImage
{
private final List<Runnable> listeners = new CopyOnWriteArrayList<>();
public AsyncBufferedImage(int width, int height, int imageType)
{
super(width, height, imageType);
}
/**
* Call when the buffer has been changed
*/
public void changed()
{
for (Runnable r : listeners)
{
r.run();
}
}
/**
* Register a function to be ran when the buffer has changed
*/
public void onChanged(Runnable r)
{
listeners.add(r);
}
/**
* Calls setIcon on c, ensuring it is repainted when this changes
*/
public void addTo(JButton c)
{
c.setIcon(makeIcon(c));
}
/**
* Calls setIcon on c, ensuring it is repainted when this changes
*/
public void addTo(JLabel c)
{
c.setIcon(makeIcon(c));
}
private ImageIcon makeIcon(JComponent c)
{
listeners.add(c::repaint);
return new ImageIcon(this);
}
}

View File

@@ -1,170 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;
import javax.inject.Inject;
import javax.inject.Singleton;
import net.runelite.api.ClanMember;
import net.runelite.api.ClanMemberRank;
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.ClanChanged;
import net.runelite.api.events.GameStateChanged;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.util.ImageUtil;
import net.runelite.client.util.Text;
@Singleton
public class ClanManager
{
private static final int[] CLANCHAT_IMAGES =
{
SpriteID.CLAN_CHAT_RANK_SMILEY_FRIEND,
SpriteID.CLAN_CHAT_RANK_SINGLE_CHEVRON_RECRUIT,
SpriteID.CLAN_CHAT_RANK_DOUBLE_CHEVRON_CORPORAL,
SpriteID.CLAN_CHAT_RANK_TRIPLE_CHEVRON_SERGEANT,
SpriteID.CLAN_CHAT_RANK_BRONZE_STAR_LIEUTENANT,
SpriteID.CLAN_CHAT_RANK_SILVER_STAR_CAPTAIN,
SpriteID.CLAN_CHAT_RANK_GOLD_STAR_GENERAL,
SpriteID.CLAN_CHAT_RANK_KEY_CHANNEL_OWNER,
SpriteID.CLAN_CHAT_RANK_CROWN_JAGEX_MODERATOR,
};
private static final Dimension CLANCHAT_IMAGE_DIMENSION = new Dimension(11, 11);
private static final Color CLANCHAT_IMAGE_OUTLINE_COLOR = new Color(33, 33, 33);
private final Client client;
private final SpriteManager spriteManager;
private final BufferedImage[] clanChatImages = new BufferedImage[CLANCHAT_IMAGES.length];
private final LoadingCache<String, ClanMemberRank> clanRanksCache = CacheBuilder.newBuilder()
.maximumSize(100)
.expireAfterWrite(1, TimeUnit.MINUTES)
.build(new CacheLoader<String, ClanMemberRank>()
{
@Override
public ClanMemberRank load(@Nonnull String key)
{
final ClanMember[] clanMembersArr = client.getClanMembers();
if (clanMembersArr == null || clanMembersArr.length == 0)
{
return ClanMemberRank.UNRANKED;
}
return Arrays.stream(clanMembersArr)
.filter(Objects::nonNull)
.filter(clanMember -> sanitize(clanMember.getUsername()).equals(sanitize(key)))
.map(ClanMember::getRank)
.findAny()
.orElse(ClanMemberRank.UNRANKED);
}
});
private int modIconsLength;
@Inject
private ClanManager(Client client, SpriteManager spriteManager)
{
this.client = client;
this.spriteManager = spriteManager;
}
public ClanMemberRank getRank(String playerName)
{
return clanRanksCache.getUnchecked(playerName);
}
public BufferedImage getClanImage(final ClanMemberRank clanMemberRank)
{
if (clanMemberRank == ClanMemberRank.UNRANKED)
{
return null;
}
return clanChatImages[clanMemberRank.ordinal() - 1];
}
public int getIconNumber(final ClanMemberRank clanMemberRank)
{
return modIconsLength - CLANCHAT_IMAGES.length + clanMemberRank.ordinal() - 1;
}
@Subscribe
public void onGameStateChanged(GameStateChanged gameStateChanged)
{
if (gameStateChanged.getGameState() == GameState.LOGGED_IN
&& modIconsLength == 0)
{
loadClanChatIcons();
}
}
@Subscribe
public void onClanChanged(ClanChanged clanChanged)
{
clanRanksCache.invalidateAll();
}
private void loadClanChatIcons()
{
final IndexedSprite[] modIcons = client.getModIcons();
final IndexedSprite[] newModIcons = Arrays.copyOf(modIcons, modIcons.length + CLANCHAT_IMAGES.length);
int curPosition = newModIcons.length - CLANCHAT_IMAGES.length;
for (int i = 0; i < CLANCHAT_IMAGES.length; i++, curPosition++)
{
final int resource = CLANCHAT_IMAGES[i];
clanChatImages[i] = clanChatImageFromSprite(spriteManager.getSprite(resource, 0));
newModIcons[curPosition] = ImageUtil.getImageIndexedSprite(clanChatImages[i], client);
}
client.setModIcons(newModIcons);
modIconsLength = newModIcons.length;
}
private static String sanitize(String lookup)
{
final String cleaned = Text.removeTags(lookup);
return cleaned.replace('\u00A0', ' ');
}
private static BufferedImage clanChatImageFromSprite(final BufferedImage clanSprite)
{
final BufferedImage clanChatCanvas = ImageUtil.resizeCanvas(clanSprite, CLANCHAT_IMAGE_DIMENSION.width, CLANCHAT_IMAGE_DIMENSION.height);
return ImageUtil.outlineImage(clanChatCanvas, CLANCHAT_IMAGE_OUTLINE_COLOR);
}
}

View File

@@ -1,88 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.cache.CacheLoader;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import java.io.IOException;
import java.util.concurrent.ScheduledExecutorService;
import lombok.extern.slf4j.Slf4j;
import static net.runelite.client.game.HiscoreManager.EMPTY;
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;
@Slf4j
class HiscoreLoader extends CacheLoader<HiscoreManager.HiscoreKey, HiscoreResult>
{
private final ListeningExecutorService executorService;
private final HiscoreClient hiscoreClient;
HiscoreLoader(ScheduledExecutorService executor, HiscoreClient client)
{
this.executorService = MoreExecutors.listeningDecorator(executor);
this.hiscoreClient = client;
}
@Override
public HiscoreResult load(HiscoreManager.HiscoreKey hiscoreKey) throws Exception
{
return EMPTY;
}
@Override
public ListenableFuture<HiscoreResult> reload(HiscoreManager.HiscoreKey hiscoreKey, HiscoreResult oldValue)
{
log.debug("Submitting hiscore lookup for {} type {}", hiscoreKey.getUsername(), hiscoreKey.getType());
return executorService.submit(() -> fetch(hiscoreKey));
}
private HiscoreResult fetch(HiscoreManager.HiscoreKey hiscoreKey)
{
String username = hiscoreKey.getUsername();
HiscoreEndpoint endpoint = hiscoreKey.getType();
try
{
HiscoreResult result = hiscoreClient.lookup(username, endpoint);
if (result == null)
{
return NONE;
}
return result;
}
catch (IOException ex)
{
log.warn("Unable to look up hiscore!", ex);
return NONE;
}
}
}

View File

@@ -1,115 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.cache.CacheBuilder;
import com.google.common.cache.LoadingCache;
import java.io.IOException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.AllArgsConstructor;
import lombok.Data;
import net.runelite.api.Client;
import net.runelite.client.callback.ClientThread;
import net.runelite.http.api.hiscore.HiscoreClient;
import net.runelite.http.api.hiscore.HiscoreEndpoint;
import net.runelite.http.api.hiscore.HiscoreResult;
@Singleton
public class HiscoreManager
{
@AllArgsConstructor
@Data
static class HiscoreKey
{
String username;
HiscoreEndpoint type;
}
static final HiscoreResult EMPTY = new HiscoreResult();
static final HiscoreResult NONE = new HiscoreResult();
private final HiscoreClient hiscoreClient = new HiscoreClient();
private final LoadingCache<HiscoreKey, HiscoreResult> hiscoreCache;
@Inject
public HiscoreManager(Client client, ScheduledExecutorService executor, ClientThread clientThread)
{
hiscoreCache = CacheBuilder.newBuilder()
.maximumSize(128L)
.expireAfterWrite(1, TimeUnit.HOURS)
.build(new HiscoreLoader(executor, hiscoreClient));
}
/**
* Synchronously look up a players hiscore from a specified endpoint
*
* @param username Players username
* @param endpoint Hiscore endpoint
* @return HiscoreResult or null
* @throws IOException Upon error in fetching hiscore
*/
public HiscoreResult lookup(String username, HiscoreEndpoint endpoint) throws IOException
{
HiscoreKey hiscoreKey = new HiscoreKey(username, endpoint);
HiscoreResult hiscoreResult = hiscoreCache.getIfPresent(hiscoreKey);
if (hiscoreResult != null && hiscoreResult != EMPTY)
{
return hiscoreResult == NONE ? null : hiscoreResult;
}
hiscoreResult = hiscoreClient.lookup(username, endpoint);
if (hiscoreResult == null)
{
hiscoreCache.put(hiscoreKey, NONE);
return null;
}
hiscoreCache.put(hiscoreKey, hiscoreResult);
return hiscoreResult;
}
/**
* Asynchronously look up a players hiscore from a specified endpoint
*
* @param username Players username
* @param endpoint Hiscore endpoint
* @return HiscoreResult or null
*/
public HiscoreResult lookupAsync(String username, HiscoreEndpoint endpoint)
{
HiscoreKey hiscoreKey = new HiscoreKey(username, endpoint);
HiscoreResult hiscoreResult = hiscoreCache.getIfPresent(hiscoreKey);
if (hiscoreResult != null && hiscoreResult != EMPTY)
{
return hiscoreResult == NONE ? null : hiscoreResult;
}
hiscoreCache.refresh(hiscoreKey);
return null;
}
}

View File

@@ -1,615 +0,0 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* 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.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.Value;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import static net.runelite.api.Constants.CLIENT_DEFAULT_ZOOM;
import static net.runelite.api.Constants.HIGH_ALCHEMY_CONSTANT;
import net.runelite.api.GameState;
import net.runelite.api.ItemComposition;
import net.runelite.api.ItemID;
import static net.runelite.api.ItemID.AGILITY_CAPE;
import static net.runelite.api.ItemID.AGILITY_CAPET;
import static net.runelite.api.ItemID.AGILITY_CAPET_13341;
import static net.runelite.api.ItemID.AGILITY_CAPE_13340;
import static net.runelite.api.ItemID.BOOTS_OF_LIGHTNESS;
import static net.runelite.api.ItemID.BOOTS_OF_LIGHTNESS_89;
import static net.runelite.api.ItemID.GRACEFUL_BOOTS;
import static net.runelite.api.ItemID.GRACEFUL_BOOTS_11861;
import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13589;
import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13590;
import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13601;
import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13602;
import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13613;
import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13614;
import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13625;
import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13626;
import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13637;
import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13638;
import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13677;
import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13678;
import static net.runelite.api.ItemID.GRACEFUL_BOOTS_21076;
import static net.runelite.api.ItemID.GRACEFUL_BOOTS_21078;
import static net.runelite.api.ItemID.GRACEFUL_CAPE;
import static net.runelite.api.ItemID.GRACEFUL_CAPE_11853;
import static net.runelite.api.ItemID.GRACEFUL_CAPE_13581;
import static net.runelite.api.ItemID.GRACEFUL_CAPE_13582;
import static net.runelite.api.ItemID.GRACEFUL_CAPE_13593;
import static net.runelite.api.ItemID.GRACEFUL_CAPE_13594;
import static net.runelite.api.ItemID.GRACEFUL_CAPE_13605;
import static net.runelite.api.ItemID.GRACEFUL_CAPE_13606;
import static net.runelite.api.ItemID.GRACEFUL_CAPE_13617;
import static net.runelite.api.ItemID.GRACEFUL_CAPE_13618;
import static net.runelite.api.ItemID.GRACEFUL_CAPE_13629;
import static net.runelite.api.ItemID.GRACEFUL_CAPE_13630;
import static net.runelite.api.ItemID.GRACEFUL_CAPE_13669;
import static net.runelite.api.ItemID.GRACEFUL_CAPE_13670;
import static net.runelite.api.ItemID.GRACEFUL_CAPE_21064;
import static net.runelite.api.ItemID.GRACEFUL_CAPE_21066;
import static net.runelite.api.ItemID.GRACEFUL_GLOVES;
import static net.runelite.api.ItemID.GRACEFUL_GLOVES_11859;
import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13587;
import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13588;
import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13599;
import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13600;
import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13611;
import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13612;
import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13623;
import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13624;
import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13635;
import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13636;
import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13675;
import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13676;
import static net.runelite.api.ItemID.GRACEFUL_GLOVES_21073;
import static net.runelite.api.ItemID.GRACEFUL_GLOVES_21075;
import static net.runelite.api.ItemID.GRACEFUL_HOOD;
import static net.runelite.api.ItemID.GRACEFUL_HOOD_11851;
import static net.runelite.api.ItemID.GRACEFUL_HOOD_13579;
import static net.runelite.api.ItemID.GRACEFUL_HOOD_13580;
import static net.runelite.api.ItemID.GRACEFUL_HOOD_13591;
import static net.runelite.api.ItemID.GRACEFUL_HOOD_13592;
import static net.runelite.api.ItemID.GRACEFUL_HOOD_13603;
import static net.runelite.api.ItemID.GRACEFUL_HOOD_13604;
import static net.runelite.api.ItemID.GRACEFUL_HOOD_13615;
import static net.runelite.api.ItemID.GRACEFUL_HOOD_13616;
import static net.runelite.api.ItemID.GRACEFUL_HOOD_13627;
import static net.runelite.api.ItemID.GRACEFUL_HOOD_13628;
import static net.runelite.api.ItemID.GRACEFUL_HOOD_13667;
import static net.runelite.api.ItemID.GRACEFUL_HOOD_13668;
import static net.runelite.api.ItemID.GRACEFUL_HOOD_21061;
import static net.runelite.api.ItemID.GRACEFUL_HOOD_21063;
import static net.runelite.api.ItemID.GRACEFUL_LEGS;
import static net.runelite.api.ItemID.GRACEFUL_LEGS_11857;
import static net.runelite.api.ItemID.GRACEFUL_LEGS_13585;
import static net.runelite.api.ItemID.GRACEFUL_LEGS_13586;
import static net.runelite.api.ItemID.GRACEFUL_LEGS_13597;
import static net.runelite.api.ItemID.GRACEFUL_LEGS_13598;
import static net.runelite.api.ItemID.GRACEFUL_LEGS_13609;
import static net.runelite.api.ItemID.GRACEFUL_LEGS_13610;
import static net.runelite.api.ItemID.GRACEFUL_LEGS_13621;
import static net.runelite.api.ItemID.GRACEFUL_LEGS_13622;
import static net.runelite.api.ItemID.GRACEFUL_LEGS_13633;
import static net.runelite.api.ItemID.GRACEFUL_LEGS_13634;
import static net.runelite.api.ItemID.GRACEFUL_LEGS_13673;
import static net.runelite.api.ItemID.GRACEFUL_LEGS_13674;
import static net.runelite.api.ItemID.GRACEFUL_LEGS_21070;
import static net.runelite.api.ItemID.GRACEFUL_LEGS_21072;
import static net.runelite.api.ItemID.GRACEFUL_TOP;
import static net.runelite.api.ItemID.GRACEFUL_TOP_11855;
import static net.runelite.api.ItemID.GRACEFUL_TOP_13583;
import static net.runelite.api.ItemID.GRACEFUL_TOP_13584;
import static net.runelite.api.ItemID.GRACEFUL_TOP_13595;
import static net.runelite.api.ItemID.GRACEFUL_TOP_13596;
import static net.runelite.api.ItemID.GRACEFUL_TOP_13607;
import static net.runelite.api.ItemID.GRACEFUL_TOP_13608;
import static net.runelite.api.ItemID.GRACEFUL_TOP_13619;
import static net.runelite.api.ItemID.GRACEFUL_TOP_13620;
import static net.runelite.api.ItemID.GRACEFUL_TOP_13631;
import static net.runelite.api.ItemID.GRACEFUL_TOP_13632;
import static net.runelite.api.ItemID.GRACEFUL_TOP_13671;
import static net.runelite.api.ItemID.GRACEFUL_TOP_13672;
import static net.runelite.api.ItemID.GRACEFUL_TOP_21067;
import static net.runelite.api.ItemID.GRACEFUL_TOP_21069;
import static net.runelite.api.ItemID.MAX_CAPE;
import static net.runelite.api.ItemID.MAX_CAPE_13342;
import static net.runelite.api.ItemID.PENANCE_GLOVES;
import static net.runelite.api.ItemID.PENANCE_GLOVES_10554;
import static net.runelite.api.ItemID.SPOTTED_CAPE;
import static net.runelite.api.ItemID.SPOTTED_CAPE_10073;
import static net.runelite.api.ItemID.SPOTTIER_CAPE;
import static net.runelite.api.ItemID.SPOTTIER_CAPE_10074;
import net.runelite.api.SpritePixels;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.PostItemComposition;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.http.api.item.ItemClient;
import net.runelite.http.api.item.ItemPrice;
import net.runelite.http.api.item.ItemStats;
@Singleton
@Slf4j
public class ItemManager
{
@Value
private static class ImageKey
{
private final int itemId;
private final int itemQuantity;
private final boolean stackable;
}
@Value
private static class OutlineKey
{
private final int itemId;
private final int itemQuantity;
private final Color outlineColor;
}
private final Client client;
private final ScheduledExecutorService scheduledExecutorService;
private final ClientThread clientThread;
private final ItemClient itemClient = new ItemClient();
private Map<Integer, ItemPrice> itemPrices = Collections.emptyMap();
private Map<Integer, ItemStats> itemStats = Collections.emptyMap();
private final LoadingCache<ImageKey, AsyncBufferedImage> itemImages;
private final LoadingCache<Integer, ItemComposition> itemCompositions;
private final LoadingCache<OutlineKey, BufferedImage> itemOutlines;
// Worn items with weight reducing property have a different worn and inventory ItemID
private static final ImmutableMap<Integer, Integer> WORN_ITEMS = ImmutableMap.<Integer, Integer>builder().
put(BOOTS_OF_LIGHTNESS_89, BOOTS_OF_LIGHTNESS).
put(PENANCE_GLOVES_10554, PENANCE_GLOVES).
put(GRACEFUL_HOOD_11851, GRACEFUL_HOOD).
put(GRACEFUL_CAPE_11853, GRACEFUL_CAPE).
put(GRACEFUL_TOP_11855, GRACEFUL_TOP).
put(GRACEFUL_LEGS_11857, GRACEFUL_LEGS).
put(GRACEFUL_GLOVES_11859, GRACEFUL_GLOVES).
put(GRACEFUL_BOOTS_11861, GRACEFUL_BOOTS).
put(GRACEFUL_HOOD_13580, GRACEFUL_HOOD_13579).
put(GRACEFUL_CAPE_13582, GRACEFUL_CAPE_13581).
put(GRACEFUL_TOP_13584, GRACEFUL_TOP_13583).
put(GRACEFUL_LEGS_13586, GRACEFUL_LEGS_13585).
put(GRACEFUL_GLOVES_13588, GRACEFUL_GLOVES_13587).
put(GRACEFUL_BOOTS_13590, GRACEFUL_BOOTS_13589).
put(GRACEFUL_HOOD_13592, GRACEFUL_HOOD_13591).
put(GRACEFUL_CAPE_13594, GRACEFUL_CAPE_13593).
put(GRACEFUL_TOP_13596, GRACEFUL_TOP_13595).
put(GRACEFUL_LEGS_13598, GRACEFUL_LEGS_13597).
put(GRACEFUL_GLOVES_13600, GRACEFUL_GLOVES_13599).
put(GRACEFUL_BOOTS_13602, GRACEFUL_BOOTS_13601).
put(GRACEFUL_HOOD_13604, GRACEFUL_HOOD_13603).
put(GRACEFUL_CAPE_13606, GRACEFUL_CAPE_13605).
put(GRACEFUL_TOP_13608, GRACEFUL_TOP_13607).
put(GRACEFUL_LEGS_13610, GRACEFUL_LEGS_13609).
put(GRACEFUL_GLOVES_13612, GRACEFUL_GLOVES_13611).
put(GRACEFUL_BOOTS_13614, GRACEFUL_BOOTS_13613).
put(GRACEFUL_HOOD_13616, GRACEFUL_HOOD_13615).
put(GRACEFUL_CAPE_13618, GRACEFUL_CAPE_13617).
put(GRACEFUL_TOP_13620, GRACEFUL_TOP_13619).
put(GRACEFUL_LEGS_13622, GRACEFUL_LEGS_13621).
put(GRACEFUL_GLOVES_13624, GRACEFUL_GLOVES_13623).
put(GRACEFUL_BOOTS_13626, GRACEFUL_BOOTS_13625).
put(GRACEFUL_HOOD_13628, GRACEFUL_HOOD_13627).
put(GRACEFUL_CAPE_13630, GRACEFUL_CAPE_13629).
put(GRACEFUL_TOP_13632, GRACEFUL_TOP_13631).
put(GRACEFUL_LEGS_13634, GRACEFUL_LEGS_13633).
put(GRACEFUL_GLOVES_13636, GRACEFUL_GLOVES_13635).
put(GRACEFUL_BOOTS_13638, GRACEFUL_BOOTS_13637).
put(GRACEFUL_HOOD_13668, GRACEFUL_HOOD_13667).
put(GRACEFUL_CAPE_13670, GRACEFUL_CAPE_13669).
put(GRACEFUL_TOP_13672, GRACEFUL_TOP_13671).
put(GRACEFUL_LEGS_13674, GRACEFUL_LEGS_13673).
put(GRACEFUL_GLOVES_13676, GRACEFUL_GLOVES_13675).
put(GRACEFUL_BOOTS_13678, GRACEFUL_BOOTS_13677).
put(GRACEFUL_HOOD_21063, GRACEFUL_HOOD_21061).
put(GRACEFUL_CAPE_21066, GRACEFUL_CAPE_21064).
put(GRACEFUL_TOP_21069, GRACEFUL_TOP_21067).
put(GRACEFUL_LEGS_21072, GRACEFUL_LEGS_21070).
put(GRACEFUL_GLOVES_21075, GRACEFUL_GLOVES_21073).
put(GRACEFUL_BOOTS_21078, GRACEFUL_BOOTS_21076).
put(MAX_CAPE_13342, MAX_CAPE).
put(SPOTTED_CAPE_10073, SPOTTED_CAPE).
put(SPOTTIER_CAPE_10074, SPOTTIER_CAPE).
put(AGILITY_CAPET_13341, AGILITY_CAPET).
put(AGILITY_CAPE_13340, AGILITY_CAPE).
build();
@Inject
public ItemManager(Client client, ScheduledExecutorService executor, ClientThread clientThread)
{
this.client = client;
this.scheduledExecutorService = executor;
this.clientThread = clientThread;
scheduledExecutorService.scheduleWithFixedDelay(this::loadPrices, 0, 30, TimeUnit.MINUTES);
scheduledExecutorService.submit(this::loadStats);
itemImages = CacheBuilder.newBuilder()
.maximumSize(128L)
.expireAfterAccess(1, TimeUnit.HOURS)
.build(new CacheLoader<ImageKey, AsyncBufferedImage>()
{
@Override
public AsyncBufferedImage load(ImageKey key) throws Exception
{
return loadImage(key.itemId, key.itemQuantity, key.stackable);
}
});
itemCompositions = CacheBuilder.newBuilder()
.maximumSize(1024L)
.expireAfterAccess(1, TimeUnit.HOURS)
.build(new CacheLoader<Integer, ItemComposition>()
{
@Override
public ItemComposition load(Integer key) throws Exception
{
return client.getItemDefinition(key);
}
});
itemOutlines = CacheBuilder.newBuilder()
.maximumSize(128L)
.expireAfterAccess(1, TimeUnit.HOURS)
.build(new CacheLoader<OutlineKey, BufferedImage>()
{
@Override
public BufferedImage load(OutlineKey key) throws Exception
{
return loadItemOutline(key.itemId, key.itemQuantity, key.outlineColor);
}
});
}
private void loadPrices()
{
try
{
ItemPrice[] prices = itemClient.getPrices();
if (prices != null)
{
ImmutableMap.Builder<Integer, ItemPrice> 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()
{
try
{
final Map<Integer, ItemStats> 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);
}
}
@Subscribe
public void onGameStateChanged(final GameStateChanged event)
{
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#getItemCompositionCache()
*/
public void invalidateItemCompositionCache()
{
itemCompositions.invalidateAll();
}
/**
* Look up an item's price
*
* @param itemID item id
* @return item price
*/
public int getItemPrice(int itemID)
{
if (itemID == ItemID.COINS_995)
{
return 1;
}
if (itemID == ItemID.PLATINUM_TOKEN)
{
return 1000;
}
UntradeableItemMapping p = UntradeableItemMapping.map(ItemVariationMapping.map(itemID));
if (p != null)
{
return getItemPrice(p.getPriceID()) * p.getQuantity();
}
int price = 0;
for (int mappedID : ItemMapping.map(itemID))
{
ItemPrice ip = itemPrices.get(mappedID);
if (ip != null)
{
price += ip.getPrice();
}
}
return price;
}
public int getAlchValue(ItemComposition composition)
{
if (composition.getId() == ItemID.COINS_995)
{
return 1;
}
if (composition.getId() == ItemID.PLATINUM_TOKEN)
{
return 1000;
}
return (int) Math.max(1, composition.getPrice() * HIGH_ALCHEMY_CONSTANT);
}
public int getAlchValue(int itemID)
{
if (itemID == ItemID.COINS_995)
{
return 1;
}
if (itemID == ItemID.PLATINUM_TOKEN)
{
return 1000;
}
return (int) Math.max(1, getItemComposition(itemID).getPrice() * HIGH_ALCHEMY_CONSTANT);
}
/**
* Look up an item's stats
*
* @param itemId item id
* @return item stats
*/
@Nullable
public ItemStats getItemStats(int itemId, boolean allowNote)
{
ItemComposition itemComposition = getItemComposition(itemId);
if (itemComposition == null || itemComposition.getName() == null || (!allowNote && itemComposition.getNote() != -1))
{
return null;
}
return itemStats.get(canonicalize(itemId));
}
/**
* Search for tradeable items based on item name
*
* @param itemName item name
* @return
*/
public List<ItemPrice> search(String itemName)
{
itemName = itemName.toLowerCase();
List<ItemPrice> result = new ArrayList<>();
for (ItemPrice itemPrice : itemPrices.values())
{
final String name = itemPrice.getName();
if (name.toLowerCase().contains(itemName))
{
result.add(itemPrice);
}
}
return result;
}
/**
* Look up an item's composition
*
* @param itemId item id
* @return item composition
*/
public ItemComposition getItemComposition(int itemId)
{
assert client.isClientThread() : "getItemComposition must be called on client thread";
return itemCompositions.getUnchecked(itemId);
}
/**
* Get an item's un-noted, un-placeholdered ID
*/
public int canonicalize(int itemID)
{
ItemComposition itemComposition = getItemComposition(itemID);
if (itemComposition.getNote() != -1)
{
return itemComposition.getLinkedNoteId();
}
if (itemComposition.getPlaceholderTemplateId() != -1)
{
return itemComposition.getPlaceholderId();
}
return WORN_ITEMS.getOrDefault(itemID, itemID);
}
/**
* Loads item sprite from game, makes transparent, and generates image
*
* @param itemId
* @return
*/
private AsyncBufferedImage loadImage(int itemId, int quantity, boolean stackable)
{
AsyncBufferedImage img = new AsyncBufferedImage(36, 32, BufferedImage.TYPE_INT_ARGB);
clientThread.invoke(() ->
{
if (client.getGameState().ordinal() < GameState.LOGIN_SCREEN.ordinal())
{
return false;
}
SpritePixels sprite = client.createItemSprite(itemId, quantity, 1, SpritePixels.DEFAULT_SHADOW_COLOR,
stackable ? 1 : 0, false, CLIENT_DEFAULT_ZOOM);
if (sprite == null)
{
return false;
}
sprite.toBufferedImage(img);
img.changed();
return true;
});
return img;
}
/**
* Get item sprite image as BufferedImage.
* <p>
* This method may return immediately with a blank image if not called on the game thread.
* The image will be filled in later. If this is used for a UI label/button, it should be added
* using AsyncBufferedImage::addTo to ensure it is painted properly
*
* @param itemId
* @return
*/
public AsyncBufferedImage getImage(int itemId)
{
return getImage(itemId, 1, false);
}
/**
* Get item sprite image as BufferedImage.
* <p>
* This method may return immediately with a blank image if not called on the game thread.
* The image will be filled in later. If this is used for a UI label/button, it should be added
* using AsyncBufferedImage::addTo to ensure it is painted properly
*
* @param itemId
* @param quantity
* @return
*/
public AsyncBufferedImage getImage(int itemId, int quantity, boolean stackable)
{
try
{
return itemImages.get(new ImageKey(itemId, quantity, stackable));
}
catch (ExecutionException ex)
{
return null;
}
}
/**
* Create item sprite and applies an outline.
*
* @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 SpritePixels itemSprite = client.createItemSprite(itemId, itemQuantity, 1, 0, 0, true, 710);
return itemSprite.toBufferedOutline(outlineColor);
}
/**
* Get item outline with a specific color.
*
* @param itemId item id
* @param itemQuantity item quantity
* @param outlineColor outline color
* @return image
*/
public BufferedImage getItemOutline(final int itemId, final int itemQuantity, final Color outlineColor)
{
try
{
return itemOutlines.get(new OutlineKey(itemId, itemQuantity, outlineColor));
}
catch (ExecutionException e)
{
return null;
}
}
}

View File

@@ -1,273 +0,0 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* Copyright (c) 2018, Seth <Sethtroll3@gmail.com>
* 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.HashMultimap;
import com.google.common.collect.Multimap;
import java.util.Collection;
import java.util.Collections;
import static net.runelite.api.ItemID.*;
/**
* Converts untradeable items to it's tradeable counterparts
*/
public enum ItemMapping
{
// Barrows equipment
ITEM_AHRIMS_HOOD(AHRIMS_HOOD, AHRIMS_HOOD_25, AHRIMS_HOOD_50, AHRIMS_HOOD_75, AHRIMS_HOOD_100),
ITEM_AHRIMS_ROBETOP(AHRIMS_ROBETOP, AHRIMS_ROBETOP_25, AHRIMS_ROBETOP_50, AHRIMS_ROBETOP_75, AHRIMS_ROBETOP_100),
ITEM_AHRIMS_ROBEBOTTOM(AHRIMS_ROBESKIRT, AHRIMS_ROBESKIRT_25, AHRIMS_ROBESKIRT_50, AHRIMS_ROBESKIRT_75, AHRIMS_ROBESKIRT_100),
ITEM_AHRIMS_STAFF(AHRIMS_STAFF, AHRIMS_STAFF_25, AHRIMS_STAFF_50, AHRIMS_STAFF_75, AHRIMS_STAFF_100),
ITEM_KARILS_COIF(KARILS_COIF, KARILS_COIF_25, KARILS_COIF_50, KARILS_COIF_75, KARILS_COIF_100),
ITEM_KARILS_LEATHERTOP(KARILS_LEATHERTOP, KARILS_LEATHERTOP_25, KARILS_LEATHERTOP_50, KARILS_LEATHERTOP_75, KARILS_LEATHERTOP_100),
ITEM_KARILS_LEATHERSKIRT(KARILS_LEATHERSKIRT, KARILS_LEATHERSKIRT_25, KARILS_LEATHERSKIRT_50, KARILS_LEATHERSKIRT_75, KARILS_LEATHERSKIRT_100),
ITEM_KARILS_CROSSBOW(KARILS_CROSSBOW, KARILS_CROSSBOW_25, KARILS_CROSSBOW_50, KARILS_CROSSBOW_75, KARILS_CROSSBOW_100),
ITEM_DHAROKS_HELM(DHAROKS_HELM, DHAROKS_HELM_25, DHAROKS_HELM_50, DHAROKS_HELM_75, DHAROKS_HELM_100),
ITEM_DHAROKS_PLATEBODY(DHAROKS_PLATEBODY, DHAROKS_PLATEBODY_25, DHAROKS_PLATEBODY_50, DHAROKS_PLATEBODY_75, DHAROKS_PLATEBODY_100),
ITEM_DHAROKS_PLATELEGS(DHAROKS_PLATELEGS, DHAROKS_PLATELEGS_25, DHAROKS_PLATELEGS_50, DHAROKS_PLATELEGS_75, DHAROKS_PLATELEGS_100),
ITEM_DHARKS_GREATEAXE(DHAROKS_GREATAXE, DHAROKS_GREATAXE_25, DHAROKS_GREATAXE_50, DHAROKS_GREATAXE_75, DHAROKS_GREATAXE_100),
ITEM_GUTHANS_HELM(GUTHANS_HELM, GUTHANS_HELM_25, GUTHANS_HELM_50, GUTHANS_HELM_75, GUTHANS_HELM_100),
ITEM_GUTHANS_PLATEBODY(GUTHANS_PLATEBODY, GUTHANS_PLATEBODY_25, GUTHANS_PLATEBODY_50, GUTHANS_PLATEBODY_75, GUTHANS_PLATEBODY_100),
ITEM_GUTHANS_CHAINSKIRT(GUTHANS_CHAINSKIRT, GUTHANS_CHAINSKIRT_25, GUTHANS_CHAINSKIRT_50, GUTHANS_CHAINSKIRT_75, GUTHANS_CHAINSKIRT_100),
ITEM_GUTHANS_WARSPEAR(GUTHANS_WARSPEAR, GUTHANS_WARSPEAR_25, GUTHANS_WARSPEAR_50, GUTHANS_WARSPEAR_75, GUTHANS_WARSPEAR_100),
ITEM_TORAGS_HELM(TORAGS_HELM, TORAGS_HELM_25, TORAGS_HELM_50, TORAGS_HELM_75, TORAGS_HELM_100),
ITEM_TORAGS_PLATEBODY(TORAGS_PLATEBODY, TORAGS_PLATEBODY_25, TORAGS_PLATEBODY_50, TORAGS_PLATEBODY_75, TORAGS_PLATEBODY_100),
ITEM_TORAGS_PLATELEGS(TORAGS_PLATELEGS, TORAGS_PLATELEGS_25, TORAGS_PLATELEGS_50, TORAGS_PLATELEGS_75, TORAGS_PLATELEGS_100),
ITEM_TORAGS_HAMMERS(TORAGS_HAMMERS, TORAGS_HAMMERS_25, TORAGS_HAMMERS_50, TORAGS_HAMMERS_75, TORAGS_HAMMERS_100),
ITEM_VERACS_HELM(VERACS_HELM, VERACS_HELM_25, VERACS_HELM_50, VERACS_HELM_75, VERACS_HELM_100),
ITEM_VERACS_BRASSARD(VERACS_BRASSARD, VERACS_BRASSARD_25, VERACS_BRASSARD_50, VERACS_BRASSARD_75, VERACS_BRASSARD_100),
ITEM_VERACS_PLATESKIRT(VERACS_PLATESKIRT, VERACS_PLATESKIRT_25, VERACS_PLATESKIRT_50, VERACS_PLATESKIRT_75, VERACS_PLATESKIRT_100),
ITEM_VERACS_FLAIL(VERACS_FLAIL, VERACS_FLAIL_25, VERACS_FLAIL_50, VERACS_FLAIL_75, VERACS_FLAIL_100),
// Dragon equipment ornament kits
ITEM_DRAGON_SCIMITAR(DRAGON_SCIMITAR, DRAGON_SCIMITAR_OR),
ITEM_DRAGON_SCIMITAR_ORNAMENT_KIT(DRAGON_SCIMITAR_ORNAMENT_KIT, DRAGON_SCIMITAR_OR),
ITEM_DRAGON_DEFENDER(DRAGON_DEFENDER_ORNAMENT_KIT, DRAGON_DEFENDER_T),
ITEM_DRAGON_PICKAXE(DRAGON_PICKAXE, DRAGON_PICKAXE_12797),
ITEM_DRAGON_KITESHIELD(DRAGON_KITESHIELD, DRAGON_KITESHIELD_G),
ITEM_DRAGON_KITESHIELD_ORNAMENT_KIT(DRAGON_KITESHIELD_ORNAMENT_KIT, DRAGON_KITESHIELD_G),
ITEM_DRAGON_FULL_HELM(DRAGON_FULL_HELM, DRAGON_FULL_HELM_G),
ITEM_DRAGON_FULL_HELM_ORNAMENT_KIT(DRAGON_FULL_HELM_ORNAMENT_KIT, DRAGON_FULL_HELM_G),
ITEM_DRAGON_CHAINBODY(DRAGON_CHAINBODY_3140, DRAGON_CHAINBODY_G),
ITEM_DRAGON_CHAINBODY_ORNAMENT_KIT(DRAGON_CHAINBODY_ORNAMENT_KIT, DRAGON_CHAINBODY_G),
ITEM_DRAGON_PLATEBODY(DRAGON_PLATEBODY, DRAGON_PLATEBODY_G),
ITEM_DRAGON_PLATEBODY_ORNAMENT_KIT(DRAGON_PLATEBODY_ORNAMENT_KIT, DRAGON_PLATEBODY_G),
ITEM_DRAGON_PLATESKIRT(DRAGON_PLATESKIRT, DRAGON_PLATESKIRT_G),
ITEM_DRAGON_SKIRT_ORNAMENT_KIT(DRAGON_LEGSSKIRT_ORNAMENT_KIT, DRAGON_PLATESKIRT_G),
ITEM_DRAGON_PLATELEGS(DRAGON_PLATELEGS, DRAGON_PLATELEGS_G),
ITEM_DRAGON_LEGS_ORNAMENT_KIT(DRAGON_LEGSSKIRT_ORNAMENT_KIT, DRAGON_PLATELEGS_G),
ITEM_DRAGON_SQ_SHIELD(DRAGON_SQ_SHIELD, DRAGON_SQ_SHIELD_G),
ITEM_DRAGON_SQ_SHIELD_ORNAMENT_KIT(DRAGON_SQ_SHIELD_ORNAMENT_KIT, DRAGON_SQ_SHIELD_G),
ITEM_DRAGON_BOOTS(DRAGON_BOOTS, DRAGON_BOOTS_G),
ITEM_DRAGON_BOOTS_ORNAMENT_KIT(DRAGON_BOOTS_ORNAMENT_KIT, DRAGON_BOOTS_G),
// Godsword ornament kits
ITEM_ARMADYL_GODSWORD(ARMADYL_GODSWORD, ARMADYL_GODSWORD_OR),
ITEM_ARMADYL_GODSWORD_ORNAMENT_KIT(ARMADYL_GODSWORD_ORNAMENT_KIT, ARMADYL_GODSWORD_OR),
ITEM_BANDOS_GODSWORD(BANDOS_GODSWORD, BANDOS_GODSWORD_OR),
ITEM_BANDOS_GODSWORD_ORNAMENT_KIT(BANDOS_GODSWORD_ORNAMENT_KIT, BANDOS_GODSWORD_OR),
ITEM_ZAMORAK_GODSWORD(ZAMORAK_GODSWORD, ZAMORAK_GODSWORD_OR),
ITEM_ZAMORAK_GODSWORD_ORNAMENT_KIT(ZAMORAK_GODSWORD_ORNAMENT_KIT, ZAMORAK_GODSWORD_OR),
ITEM_SARADOMIN_GODSWORD(SARADOMIN_GODSWORD, SARADOMIN_GODSWORD_OR),
ITEM_SARADOMIN_GODSWORD_ORNAMENT_KIT(SARADOMIN_GODSWORD_ORNAMENT_KIT, SARADOMIN_GODSWORD_OR),
// Jewellery ornament kits
ITEM_AMULET_OF_TORTURE(AMULET_OF_TORTURE, AMULET_OF_TORTURE_OR),
ITEM_TORTURE_ORNAMENT_KIT(TORTURE_ORNAMENT_KIT, AMULET_OF_TORTURE_OR),
ITEM_NECKLACE_OF_ANGUISH(NECKLACE_OF_ANGUISH, NECKLACE_OF_ANGUISH_OR),
ITEM_ANGUISH_ORNAMENT_KIT(ANGUISH_ORNAMENT_KIT, NECKLACE_OF_ANGUISH_OR),
ITEM_OCCULT_NECKLACE(OCCULT_NECKLACE, OCCULT_NECKLACE_OR),
ITEM_OCCULT_ORNAMENT_KIT(OCCULT_ORNAMENT_KIT, OCCULT_NECKLACE_OR),
ITEM_AMULET_OF_FURY(AMULET_OF_FURY, AMULET_OF_FURY_OR),
ITEM_FURY_ORNAMENT_KIT(FURY_ORNAMENT_KIT, AMULET_OF_FURY_OR),
ITEM_TORMENTED_BRACELET(TORMENTED_BRACELET, TORMENTED_BRACELET_OR),
ITEM_TORMENTED_ORNAMENT_KIT(TORMENTED_ORNAMENT_KIT, TORMENTED_BRACELET_OR),
// Ensouled heads
ITEM_ENSOULED_GOBLIN_HEAD(ENSOULED_GOBLIN_HEAD_13448, ENSOULED_GOBLIN_HEAD),
ITEM_ENSOULED_MONKEY_HEAD(ENSOULED_MONKEY_HEAD_13451, ENSOULED_MONKEY_HEAD),
ITEM_ENSOULED_IMP_HEAD(ENSOULED_IMP_HEAD_13454, ENSOULED_IMP_HEAD),
ITEM_ENSOULED_MINOTAUR_HEAD(ENSOULED_MINOTAUR_HEAD_13457, ENSOULED_MINOTAUR_HEAD),
ITEM_ENSOULED_SCORPION_HEAD(ENSOULED_SCORPION_HEAD_13460, ENSOULED_SCORPION_HEAD),
ITEM_ENSOULED_BEAR_HEAD(ENSOULED_BEAR_HEAD_13463, ENSOULED_BEAR_HEAD),
ITEM_ENSOULED_UNICORN_HEAD(ENSOULED_UNICORN_HEAD_13466, ENSOULED_UNICORN_HEAD),
ITEM_ENSOULED_DOG_HEAD(ENSOULED_DOG_HEAD_13469, ENSOULED_DOG_HEAD),
ITEM_ENSOULED_CHAOS_DRUID_HEAD(ENSOULED_CHAOS_DRUID_HEAD_13472, ENSOULED_CHAOS_DRUID_HEAD),
ITEM_ENSOULED_GIANT_HEAD(ENSOULED_GIANT_HEAD_13475, ENSOULED_GIANT_HEAD),
ITEM_ENSOULED_OGRE_HEAD(ENSOULED_OGRE_HEAD_13478, ENSOULED_OGRE_HEAD),
ITEM_ENSOULED_ELF_HEAD(ENSOULED_ELF_HEAD_13481, ENSOULED_ELF_HEAD),
ITEM_ENSOULED_TROLL_HEAD(ENSOULED_TROLL_HEAD_13484, ENSOULED_TROLL_HEAD),
ITEM_ENSOULED_HORROR_HEAD(ENSOULED_HORROR_HEAD_13487, ENSOULED_HORROR_HEAD),
ITEM_ENSOULED_KALPHITE_HEAD(ENSOULED_KALPHITE_HEAD_13490, ENSOULED_KALPHITE_HEAD),
ITEM_ENSOULED_DAGANNOTH_HEAD(ENSOULED_DAGANNOTH_HEAD_13493, ENSOULED_DAGANNOTH_HEAD),
ITEM_ENSOULED_BLOODVELD_HEAD(ENSOULED_BLOODVELD_HEAD_13496, ENSOULED_BLOODVELD_HEAD),
ITEM_ENSOULED_TZHAAR_HEAD(ENSOULED_TZHAAR_HEAD_13499, ENSOULED_TZHAAR_HEAD),
ITEM_ENSOULED_DEMON_HEAD(ENSOULED_DEMON_HEAD_13502, ENSOULED_DEMON_HEAD),
ITEM_ENSOULED_AVIANSIE_HEAD(ENSOULED_AVIANSIE_HEAD_13505, ENSOULED_AVIANSIE_HEAD),
ITEM_ENSOULED_ABYSSAL_HEAD(ENSOULED_ABYSSAL_HEAD_13508, ENSOULED_ABYSSAL_HEAD),
ITEM_ENSOULED_DRAGON_HEAD(ENSOULED_DRAGON_HEAD_13511, ENSOULED_DRAGON_HEAD),
// Imbued rings
ITEM_BERSERKER_RING(BERSERKER_RING, BERSERKER_RING_I),
ITEM_SEERS_RING(SEERS_RING, SEERS_RING_I),
ITEM_WARRIOR_RING(WARRIOR_RING, WARRIOR_RING_I),
ITEM_ARCHERS_RING(ARCHERS_RING, ARCHERS_RING_I),
ITEM_TREASONOUS_RING(TREASONOUS_RING, TREASONOUS_RING_I),
ITEM_TYRANNICAL_RING(TYRANNICAL_RING, TYRANNICAL_RING_I),
ITEM_RING_OF_THE_GODS(RING_OF_THE_GODS, RING_OF_THE_GODS_I),
ITEM_RING_OF_SUFFERING(RING_OF_SUFFERING, RING_OF_SUFFERING_I, RING_OF_SUFFERING_R, RING_OF_SUFFERING_RI),
ITEM_GRANITE_RING(GRANITE_RING, GRANITE_RING_I),
// Bounty hunter
ITEM_GRANITE_MAUL(GRANITE_MAUL, GRANITE_MAUL_12848),
ITEM_MAGIC_SHORTBOW(MAGIC_SHORTBOW, MAGIC_SHORTBOW_I),
ITEM_SARADOMINS_BLESSED_SWORD(SARADOMINS_TEAR, SARADOMINS_BLESSED_SWORD),
// Jewellery with charges
ITEM_RING_OF_WEALTH(RING_OF_WEALTH, RING_OF_WEALTH_I, RING_OF_WEALTH_1, RING_OF_WEALTH_I1, RING_OF_WEALTH_2, RING_OF_WEALTH_I2, RING_OF_WEALTH_3, RING_OF_WEALTH_I3, RING_OF_WEALTH_4, RING_OF_WEALTH_I4, RING_OF_WEALTH_I5),
ITEM_AMULET_OF_GLORY(AMULET_OF_GLORY, AMULET_OF_GLORY1, AMULET_OF_GLORY2, AMULET_OF_GLORY3, AMULET_OF_GLORY5),
ITEM_AMULET_OF_GLORY_T(AMULET_OF_GLORY_T, AMULET_OF_GLORY_T1, AMULET_OF_GLORY_T2, AMULET_OF_GLORY_T3, AMULET_OF_GLORY_T5),
ITEM_SKILLS_NECKLACE(SKILLS_NECKLACE, SKILLS_NECKLACE1, SKILLS_NECKLACE2, SKILLS_NECKLACE3, SKILLS_NECKLACE5),
ITEM_RING_OF_DUELING(RING_OF_DUELING8, RING_OF_DUELING1, RING_OF_DUELING2, RING_OF_DUELING3, RING_OF_DUELING4, RING_OF_DUELING5, RING_OF_DUELING6, RING_OF_DUELING7),
ITEM_GAMES_NECKLACE(GAMES_NECKLACE8, GAMES_NECKLACE1, GAMES_NECKLACE2, GAMES_NECKLACE3, GAMES_NECKLACE4, GAMES_NECKLACE5, GAMES_NECKLACE6, GAMES_NECKLACE7),
// Degradable/charged weaponry/armour
ITEM_ABYSSAL_WHIP(ABYSSAL_WHIP, VOLCANIC_ABYSSAL_WHIP, FROZEN_ABYSSAL_WHIP),
ITEM_KRAKEN_TENTACLE(KRAKEN_TENTACLE, ABYSSAL_TENTACLE),
ITEM_TRIDENT_OF_THE_SEAS(UNCHARGED_TRIDENT, TRIDENT_OF_THE_SEAS),
ITEM_TRIDENT_OF_THE_SEAS_E(UNCHARGED_TRIDENT_E, TRIDENT_OF_THE_SEAS_E),
ITEM_TRIDENT_OF_THE_SWAMP(UNCHARGED_TOXIC_TRIDENT, TRIDENT_OF_THE_SWAMP),
ITEM_TRIDENT_OF_THE_SWAMP_E(UNCHARGED_TOXIC_TRIDENT_E, TRIDENT_OF_THE_SWAMP_E),
ITEM_TOXIC_BLOWPIPE(TOXIC_BLOWPIPE_EMPTY, TOXIC_BLOWPIPE),
ITEM_TOXIC_STAFF_OFF_THE_DEAD(TOXIC_STAFF_UNCHARGED, TOXIC_STAFF_OF_THE_DEAD),
ITEM_SERPENTINE_HELM(SERPENTINE_HELM_UNCHARGED, SERPENTINE_HELM, TANZANITE_HELM_UNCHARGED, TANZANITE_HELM, MAGMA_HELM_UNCHARGED, MAGMA_HELM),
ITEM_DRAGONFIRE_SHIELD(DRAGONFIRE_SHIELD_11284, DRAGONFIRE_SHIELD),
ITEM_DRAGONFIRE_WARD(DRAGONFIRE_WARD_22003, DRAGONFIRE_WARD),
ITEM_ANCIENT_WYVERN_SHIELD(ANCIENT_WYVERN_SHIELD_21634, ANCIENT_WYVERN_SHIELD),
ITEM_SANGUINESTI_STAFF(SANGUINESTI_STAFF_UNCHARGED, SANGUINESTI_STAFF),
ITEM_SCYTHE_OF_VITUR(SCYTHE_OF_VITUR_UNCHARGED, SCYTHE_OF_VITUR),
ITEM_TOME_OF_FIRE(TOME_OF_FIRE_EMPTY, TOME_OF_FIRE),
ITEM_CRAWS_BOW(CRAWS_BOW_U, CRAWS_BOW),
ITEM_VIGGORAS_CHAINMACE(VIGGORAS_CHAINMACE_U, VIGGORAS_CHAINMACE),
ITEM_THAMMARONS_SCEPTRE(THAMMARONS_SCEPTRE_U, THAMMARONS_SCEPTRE),
// Infinity colour kits
ITEM_INFINITY_TOP(INFINITY_TOP, INFINITY_TOP_10605, INFINITY_TOP_20574, DARK_INFINITY_TOP, LIGHT_INFINITY_TOP),
ITEM_INFINITY_TOP_LIGHT_COLOUR_KIT(LIGHT_INFINITY_COLOUR_KIT, LIGHT_INFINITY_TOP),
ITEM_INFINITY_TOP_DARK_COLOUR_KIT(DARK_INFINITY_COLOUR_KIT, DARK_INFINITY_TOP),
ITEM_INFINITY_BOTTOMS(INFINITY_BOTTOMS, INFINITY_BOTTOMS_20575, DARK_INFINITY_BOTTOMS, LIGHT_INFINITY_BOTTOMS),
ITEM_INFINITY_BOTTOMS_LIGHT_COLOUR_KIT(LIGHT_INFINITY_COLOUR_KIT, LIGHT_INFINITY_BOTTOMS),
ITEM_INFINITY_BOTTOMS_DARK_COLOUR_KIT(DARK_INFINITY_COLOUR_KIT, DARK_INFINITY_BOTTOMS),
ITEM_INFINITY_HAT(INFINITY_HAT, DARK_INFINITY_HAT, LIGHT_INFINITY_HAT),
ITEM_INFINITY_HAT_LIGHT_COLOUR_KIT(LIGHT_INFINITY_COLOUR_KIT, LIGHT_INFINITY_HAT),
ITEM_INFINITY_HAT_DARK_COLOUR_KIT(DARK_INFINITY_COLOUR_KIT, DARK_INFINITY_HAT),
// Miscellaneous ornament kits
ITEM_DARK_BOW(DARK_BOW, DARK_BOW_12765, DARK_BOW_12766, DARK_BOW_12767, DARK_BOW_12768, DARK_BOW_20408),
ITEM_ODIUM_WARD(ODIUM_WARD, ODIUM_WARD_12807),
ITEM_MALEDICTION_WARD(MALEDICTION_WARD, MALEDICTION_WARD_12806),
ITEM_STEAM_BATTLESTAFF(STEAM_BATTLESTAFF, STEAM_BATTLESTAFF_12795),
ITEM_LAVA_BATTLESTAFF(LAVA_BATTLESTAFF, LAVA_BATTLESTAFF_21198),
// Slayer helm/black mask
ITEM_BLACK_MASK(
BLACK_MASK, BLACK_MASK_I, BLACK_MASK_1, BLACK_MASK_1_I, BLACK_MASK_2, BLACK_MASK_2_I, BLACK_MASK_3, BLACK_MASK_3_I, BLACK_MASK_4, BLACK_MASK_4_I, BLACK_MASK_5,
BLACK_MASK_5_I, BLACK_MASK_6, BLACK_MASK_6_I, BLACK_MASK_7, BLACK_MASK_7_I, BLACK_MASK_8, BLACK_MASK_8_I, BLACK_MASK_9, BLACK_MASK_9_I, BLACK_MASK_10_I,
SLAYER_HELMET, SLAYER_HELMET_I, BLACK_SLAYER_HELMET, BLACK_SLAYER_HELMET_I, PURPLE_SLAYER_HELMET, PURPLE_SLAYER_HELMET_I, RED_SLAYER_HELMET, RED_SLAYER_HELMET_I,
GREEN_SLAYER_HELMET, GREEN_SLAYER_HELMET_I, TURQUOISE_SLAYER_HELMET, TURQUOISE_SLAYER_HELMET_I, HYDRA_SLAYER_HELMET, HYDRA_SLAYER_HELMET_I),
// Pharaoh's Sceptres
ITEM_PHARAOHS_SCEPTRE_1(PHARAOHS_SCEPTRE, PHARAOHS_SCEPTRE_1),
ITEM_PHARAOHS_SCEPTRE_2(PHARAOHS_SCEPTRE, PHARAOHS_SCEPTRE_2),
ITEM_PHARAOHS_SCEPTRE_4(PHARAOHS_SCEPTRE, PHARAOHS_SCEPTRE_4),
ITEM_PHARAOHS_SCEPTRE_5(PHARAOHS_SCEPTRE, PHARAOHS_SCEPTRE_5),
ITEM_PHARAOHS_SCEPTRE_6(PHARAOHS_SCEPTRE, PHARAOHS_SCEPTRE_6),
ITEM_PHARAOHS_SCEPTRE_7(PHARAOHS_SCEPTRE, PHARAOHS_SCEPTRE_7),
ITEM_PHARAOHS_SCEPTRE_8(PHARAOHS_SCEPTRE, PHARAOHS_SCEPTRE_8),
// Revertible items
ITEM_HYDRA_LEATHER(HYDRA_LEATHER, FEROCIOUS_GLOVES),
ITEM_HYDRA_TAIL(HYDRA_TAIL, BONECRUSHER_NECKLACE),
ITEM_DRAGONBONE_NECKLACE(DRAGONBONE_NECKLACE, BONECRUSHER_NECKLACE),
ITEM_BOTTOMLESS_COMPOST_BUCKET(BOTTOMLESS_COMPOST_BUCKET, BOTTOMLESS_COMPOST_BUCKET_22997);
private static final Multimap<Integer, Integer> MAPPINGS = HashMultimap.create();
private final int tradeableItem;
private final int[] untradableItems;
static
{
for (final ItemMapping item : values())
{
for (int itemId : item.untradableItems)
{
MAPPINGS.put(itemId, item.tradeableItem);
}
}
}
ItemMapping(int tradeableItem, int... untradableItems)
{
this.tradeableItem = tradeableItem;
this.untradableItems = untradableItems;
}
/**
* Get collection of items that are mapped from single item id.
*
* @param itemId the item id
* @return the collection
*/
public static Collection<Integer> map(int itemId)
{
final Collection<Integer> mapping = MAPPINGS.get(itemId);
if (mapping == null || mapping.isEmpty())
{
return Collections.singleton(itemId);
}
return mapping;
}
/**
* Map an item from its untradeable version to its tradeable version
*
* @param itemId
* @return
*/
public static int mapFirst(int itemId)
{
final Collection<Integer> mapping = MAPPINGS.get(itemId);
if (mapping == null || mapping.isEmpty())
{
return itemId;
}
return mapping.iterator().next();
}
}

View File

@@ -1,36 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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 lombok.Value;
import net.runelite.api.coords.LocalPoint;
@Value
public class ItemStack
{
private final int id;
private final int quantity;
private final LocalPoint location;
}

View File

@@ -1,78 +0,0 @@
/*
* Copyright (c) 2018, Ron Young <https://github.com/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.game;
import com.google.common.collect.ImmutableMap;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
/**
* Converts variation items to it's base item counterparts
*/
public class ItemVariationMapping
{
private static final Map<Integer, Integer> MAPPINGS;
static
{
final Gson gson = new Gson();
final TypeToken<Map<String, Collection<Integer>>> typeToken = new TypeToken<Map<String, Collection<Integer>>>()
{
};
final InputStream geLimitData = ItemVariationMapping.class.getResourceAsStream("/item_variations.json");
final Map<String, Collection<Integer>> itemVariations = gson.fromJson(new InputStreamReader(geLimitData), typeToken.getType());
ImmutableMap.Builder<Integer, Integer> builder = new ImmutableMap.Builder<>();
for (Collection<Integer> value : itemVariations.values())
{
final Iterator<Integer> iterator = value.iterator();
final int base = iterator.next();
while (iterator.hasNext())
{
builder.put(iterator.next(), base);
}
}
MAPPINGS = builder.build();
}
/**
* Get base item id for provided variation item id.
*
* @param itemId the item id
* @return the base item id
*/
public static int map(int itemId)
{
return MAPPINGS.getOrDefault(itemId, itemId);
}
}

View File

@@ -1,319 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.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.Item;
import net.runelite.api.ItemID;
import net.runelite.api.NPC;
import net.runelite.api.NpcID;
import net.runelite.api.Player;
import net.runelite.api.Tile;
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.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<Integer, Integer> NPC_DEATH_ANIMATIONS = ImmutableMap.of(
NpcID.CAVE_KRAKEN, AnimationID.CAVE_KRAKEN_DEATH
);
private final EventBus eventBus;
private final Client client;
private final ListMultimap<Integer, ItemStack> itemSpawns = ArrayListMultimap.create();
private final Set<LocalPoint> killPoints = new HashSet<>();
private WorldPoint playerLocationLastTick;
private WorldPoint krakenPlayerLocation;
@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.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_474:
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<ItemStack> items = itemSpawns.get(packed);
if (items.isEmpty())
{
return;
}
killPoints.add(location);
eventBus.post(new PlayerLootReceived(player, items));
}
@Subscribe
public void onItemSpawned(ItemSpawned itemSpawned)
{
final Item 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 Item 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 Item 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 onGameTick(GameTick gameTick)
{
playerLocationLastTick = client.getLocalPlayer().getWorldLocation();
itemSpawns.clear();
killPoints.clear();
}
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<ItemStack> 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<ItemStack> 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<Integer, ItemStack> 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;
}
}

View File

@@ -1,103 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* Copyright (c) 2019, TheStonedTurtle <https://github.com/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 com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.util.Map;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Singleton
public class NPCManager
{
private final ImmutableMap<Integer, NPCStats> statsMap;
@Inject
private NPCManager()
{
final Gson gson = new Gson();
final Type typeToken = new TypeToken<Map<Integer, NPCStats>>()
{
}.getType();
final InputStream statsFile = getClass().getResourceAsStream("/npc_stats.json");
final Map<Integer, NPCStats> stats = gson.fromJson(new InputStreamReader(statsFile), typeToken);
statsMap = ImmutableMap.copyOf(stats);
}
/**
* 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)
{
return statsMap.get(npcId);
}
/**
* Returns health for target NPC ID
* @param npcId NPC id
* @return health or null if unknown
*/
@Nullable
public Integer getHealth(final int npcId)
{
final NPCStats s = statsMap.get(npcId);
if (s == null || s.getHitpoints() == -1)
{
return null;
}
return s.getHitpoints();
}
/**
* 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)
{
final NPCStats s = statsMap.get(npcId);
if (s == null)
{
return 1;
}
return s.calculateXpModifier();
}
}

View File

@@ -1,79 +0,0 @@
/*
* Copyright (c) 2019, TheStonedTurtle <https://github.com/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 lombok.Value;
@Value
public class NPCStats
{
private final String name;
private final int hitpoints;
private final int combatLevel;
private final int slayerLevel;
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);
}
}

View File

@@ -1,64 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.image.BufferedImage;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Skill;
import net.runelite.client.util.ImageUtil;
@Singleton
@Slf4j
public class SkillIconManager
{
// * 2 to account for the small version of each icon
private final BufferedImage[] imgCache = new BufferedImage[Skill.values().length * 2];
public BufferedImage getSkillImage(Skill skill, boolean small)
{
int skillIdx = skill.ordinal() + (small ? Skill.values().length : 0);
BufferedImage skillImage = null;
if (imgCache[skillIdx] != null)
{
return imgCache[skillIdx];
}
String skillIconPath = (small ? "/skill_icons_small/" : "/skill_icons/")
+ skill.getName().toLowerCase() + ".png";
log.debug("Loading skill icon from {}", skillIconPath);
skillImage = ImageUtil.getResourceStreamFromClass(getClass(), skillIconPath);
imgCache[skillIdx] = skillImage;
return skillImage;
}
public BufferedImage getSkillImage(Skill skill)
{
return getSkillImage(skill, false);
}
}

View File

@@ -1,166 +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.game;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.inject.Inject;
import java.awt.image.BufferedImage;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import javax.annotation.Nullable;
import javax.inject.Singleton;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.SpritePixels;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.util.ImageUtil;
@Slf4j
@Singleton
public class SpriteManager
{
@Inject
private Client client;
@Inject
private ClientThread clientThread;
public Cache<Long, BufferedImage> cache = CacheBuilder.newBuilder()
.maximumSize(128L)
.expireAfterAccess(1, TimeUnit.HOURS)
.build();
@Nullable
public BufferedImage getSprite(int archive, int file)
{
assert client.isClientThread();
if (client.getGameState().ordinal() < GameState.LOGIN_SCREEN.ordinal())
{
return null;
}
Long key = (long) archive << 32 | file;
BufferedImage cached = cache.getIfPresent(key);
if (cached != null)
{
return cached;
}
SpritePixels[] sp = client.getSprites(client.getIndexSprites(), archive, 0);
BufferedImage img = sp[file].toBufferedImage();
cache.put(key, img);
return img;
}
public void getSpriteAsync(int archive, int file, Consumer<BufferedImage> user)
{
BufferedImage cached = cache.getIfPresent((long) archive << 32 | file);
if (cached != null)
{
user.accept(cached);
return;
}
clientThread.invoke(() ->
{
BufferedImage img = getSprite(archive, file);
if (img == null)
{
// Cache isn't loaded yet
return false;
}
user.accept(img);
return true;
});
}
/**
* Calls setIcon on c, ensuring it is repainted when this changes
*/
public void addSpriteTo(JButton c, int archive, int file)
{
getSpriteAsync(archive, file, img ->
{
SwingUtilities.invokeLater(() ->
{
c.setIcon(new ImageIcon(img));
});
});
}
/**
* Calls setIcon on c, ensuring it is repainted when this changes
*/
public void addSpriteTo(JLabel c, int archive, int file)
{
getSpriteAsync(archive, file, img ->
{
SwingUtilities.invokeLater(() ->
{
c.setIcon(new ImageIcon(img));
});
});
}
public void addSpriteOverrides(SpriteOverride[] add)
{
if (add.length <= 0)
{
return;
}
clientThread.invokeLater(() ->
{
Map<Integer, SpritePixels> overrides = client.getSpriteOverrides();
Class<?> owner = add[0].getClass();
for (SpriteOverride o : add)
{
BufferedImage image = ImageUtil.getResourceStreamFromClass(owner, o.getFileName());
SpritePixels sp = ImageUtil.getImageSpritePixels(image, client);
overrides.put(o.getSpriteId(), sp);
}
});
}
public void removeSpriteOverrides(SpriteOverride[] remove)
{
clientThread.invokeLater(() ->
{
Map<Integer, SpritePixels> overrides = client.getSpriteOverrides();
for (SpriteOverride o : remove)
{
overrides.remove(o.getSpriteId());
}
});
}
}

View File

@@ -1,42 +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.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();
/**
* The file name for the resource to be loaded, relative to the implementing class
*/
String getFileName();
}

View File

@@ -1,72 +0,0 @@
/*
* Copyright (c) 2018, TheStonedTurtle <https://github.com/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 lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.runelite.api.ItemID;
@Getter
@RequiredArgsConstructor
public enum UntradeableItemMapping
{
MARK_OF_GRACE(ItemID.MARK_OF_GRACE, 10, ItemID.AMYLASE_CRYSTAL),
GRACEFUL_HOOD(ItemID.GRACEFUL_HOOD, 28, ItemID.MARK_OF_GRACE),
GRACEFUL_TOP(ItemID.GRACEFUL_TOP, 44, ItemID.MARK_OF_GRACE),
GRACEFUL_LEGS(ItemID.GRACEFUL_LEGS, 48, ItemID.MARK_OF_GRACE),
GRACEFUL_GLOVES(ItemID.GRACEFUL_GLOVES, 24, ItemID.MARK_OF_GRACE),
GRACEFUL_BOOTS(ItemID.GRACEFUL_BOOTS, 32, ItemID.MARK_OF_GRACE),
GRACEFUL_CAPE(ItemID.GRACEFUL_CAPE, 32, ItemID.MARK_OF_GRACE),
// 10 golden nuggets = 100 soft clay
GOLDEN_NUGGET(ItemID.GOLDEN_NUGGET, 10, ItemID.SOFT_CLAY),
PROSPECTOR_HELMET(ItemID.PROSPECTOR_HELMET, 32, ItemID.GOLDEN_NUGGET),
PROSPECTOR_JACKET(ItemID.PROSPECTOR_JACKET, 48, ItemID.GOLDEN_NUGGET),
PROSPECTOR_LEGS(ItemID.PROSPECTOR_LEGS, 40, ItemID.GOLDEN_NUGGET),
PROSPECTOR_BOOTS(ItemID.PROSPECTOR_BOOTS, 24, ItemID.GOLDEN_NUGGET);
private static final ImmutableMap<Integer, UntradeableItemMapping> UNTRADEABLE_RECLAIM_MAP;
private final int itemID;
private final int quantity;
private final int priceID;
static
{
ImmutableMap.Builder<Integer, UntradeableItemMapping> map = ImmutableMap.builder();
for (UntradeableItemMapping p : values())
{
map.put(p.getItemID(), p);
}
UNTRADEABLE_RECLAIM_MAP = map.build();
}
public static UntradeableItemMapping map(int itemId)
{
return UNTRADEABLE_RECLAIM_MAP.get(itemId);
}
}

View File

@@ -1,39 +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.game.chatbox;
/**
* A modal input that lives in the chatbox panel.
*/
public abstract class ChatboxInput
{
protected void open()
{
}
protected void close()
{
}
}

View File

@@ -1,202 +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.game.chatbox;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.ScriptID;
import net.runelite.api.VarClientInt;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.ScriptCallbackEvent;
import net.runelite.api.vars.InputType;
import net.runelite.api.widgets.JavaScriptCallback;
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;
import net.runelite.client.input.MouseManager;
import net.runelite.client.input.MouseWheelListener;
@Singleton
@Slf4j
public class ChatboxPanelManager
{
private final Client client;
private final ClientThread clientThread;
private final EventBus eventBus;
private final KeyManager keyManager;
private final MouseManager mouseManager;
private final Provider<ChatboxTextMenuInput> chatboxTextMenuInputProvider;
private final Provider<ChatboxTextInput> chatboxTextInputProvider;
@Getter
private ChatboxInput currentInput = null;
@Inject
private ChatboxPanelManager(EventBus eventBus, Client client, ClientThread clientThread,
KeyManager keyManager, MouseManager mouseManager,
Provider<ChatboxTextMenuInput> chatboxTextMenuInputProvider, Provider<ChatboxTextInput> chatboxTextInputProvider)
{
this.client = client;
this.clientThread = clientThread;
this.eventBus = eventBus;
this.keyManager = keyManager;
this.mouseManager = mouseManager;
this.chatboxTextMenuInputProvider = chatboxTextMenuInputProvider;
this.chatboxTextInputProvider = chatboxTextInputProvider;
}
public void close()
{
clientThread.invokeLater(this::unsafeCloseInput);
}
private void unsafeCloseInput()
{
client.runScript(
ScriptID.RESET_CHATBOX_INPUT,
0,
1
);
if (currentInput != null)
{
killCurrentPanel();
}
}
private void unsafeOpenInput(ChatboxInput input)
{
client.runScript(ScriptID.CLEAR_CHATBOX_PANEL);
eventBus.register(input);
if (input instanceof KeyListener)
{
keyManager.registerKeyListener((KeyListener) input);
}
if (input instanceof MouseListener)
{
mouseManager.registerMouseListener((MouseListener) input);
}
if (input instanceof MouseWheelListener)
{
mouseManager.registerMouseWheelListener((MouseWheelListener) input);
}
if (currentInput != null)
{
killCurrentPanel();
}
currentInput = input;
client.setVar(VarClientInt.INPUT_TYPE, InputType.RUNELITE_CHATBOX_PANEL.getType());
client.getWidget(WidgetInfo.CHATBOX_TITLE).setHidden(true);
client.getWidget(WidgetInfo.CHATBOX_FULL_INPUT).setHidden(true);
Widget c = getContainerWidget();
c.deleteAllChildren();
c.setOnDialogAbortListener((JavaScriptCallback) ev -> this.unsafeCloseInput());
input.open();
}
public void openInput(ChatboxInput input)
{
clientThread.invokeLater(() -> unsafeOpenInput(input));
}
public ChatboxTextMenuInput openTextMenuInput(String title)
{
return chatboxTextMenuInputProvider.get()
.title(title);
}
public ChatboxTextInput openTextInput(String prompt)
{
return chatboxTextInputProvider.get()
.prompt(prompt);
}
@Subscribe
public void onScriptCallbackEvent(ScriptCallbackEvent ev)
{
if (currentInput != null && "resetChatboxInput".equals(ev.getEventName()))
{
killCurrentPanel();
}
}
@Subscribe
private void onGameStateChanged(GameStateChanged ev)
{
if (currentInput != null && ev.getGameState() == GameState.LOGIN_SCREEN)
{
killCurrentPanel();
}
}
private void killCurrentPanel()
{
try
{
currentInput.close();
}
catch (Exception e)
{
log.warn("Exception closing {}", currentInput.getClass(), e);
}
eventBus.unregister(currentInput);
if (currentInput instanceof KeyListener)
{
keyManager.unregisterKeyListener((KeyListener) currentInput);
}
if (currentInput instanceof MouseListener)
{
mouseManager.unregisterMouseListener((MouseListener) currentInput);
}
if (currentInput instanceof MouseWheelListener)
{
mouseManager.unregisterMouseWheelListener((MouseWheelListener) currentInput);
}
currentInput = null;
}
public Widget getContainerWidget()
{
return client.getWidget(WidgetInfo.CHATBOX_CONTAINER);
}
}

View File

@@ -1,865 +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.game.chatbox;
import com.google.common.base.Strings;
import com.google.inject.Inject;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.IntPredicate;
import java.util.function.Predicate;
import java.util.function.ToIntFunction;
import java.util.regex.Pattern;
import javax.swing.SwingUtilities;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.FontID;
import net.runelite.api.FontTypeFace;
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.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
{
private static final int CURSOR_FLASH_RATE_MILLIS = 1000;
private static final Pattern BREAK_MATCHER = Pattern.compile("[^a-zA-Z0-9']");
private final ChatboxPanelManager chatboxPanelManager;
private final ClientThread clientThread;
private static IntPredicate getDefaultCharValidator()
{
return i -> i >= 32 && i < 127;
}
@AllArgsConstructor
private static class Line
{
private final int start;
private final int end;
private final String text;
}
@Getter
private String prompt;
@Getter
private int lines;
private StringBuffer value = new StringBuffer();
@Getter
private int cursorStart = 0;
@Getter
private int cursorEnd = 0;
private int selectionStart = -1;
private int selectionEnd = -1;
@Getter
private IntPredicate charValidator = getDefaultCharValidator();
@Getter
private Runnable onClose = null;
@Getter
private Consumer<String> onDone = null;
@Getter
private Consumer<String> onChanged = null;
@Getter
private int fontID = FontID.QUILL_8;
@Getter
private boolean built = false;
// These are lambdas for atomic updates
private Predicate<MouseEvent> isInBounds = null;
private ToIntFunction<Integer> getLineOffset = null;
private ToIntFunction<Point> getPointCharOffset = null;
@Inject
protected ChatboxTextInput(ChatboxPanelManager chatboxPanelManager, ClientThread clientThread)
{
this.chatboxPanelManager = chatboxPanelManager;
this.clientThread = clientThread;
}
public ChatboxTextInput lines(int lines)
{
this.lines = lines;
if (built)
{
clientThread.invoke(this::update);
}
return this;
}
public ChatboxTextInput prompt(String prompt)
{
this.prompt = prompt;
if (built)
{
clientThread.invoke(this::update);
}
return this;
}
public ChatboxTextInput value(String value)
{
this.value = new StringBuffer(value);
cursorAt(this.value.length());
return this;
}
public ChatboxTextInput cursorAt(int index)
{
return cursorAt(index, index);
}
public ChatboxTextInput cursorAt(int indexA, int indexB)
{
if (indexA < 0)
{
indexA = 0;
}
if (indexB < 0)
{
indexB = 0;
}
if (indexA > value.length())
{
indexA = value.length();
}
if (indexB > value.length())
{
indexB = value.length();
}
int start = indexA;
int end = indexB;
if (start > end)
{
int v = start;
start = end;
end = v;
}
this.cursorStart = start;
this.cursorEnd = end;
if (built)
{
clientThread.invoke(this::update);
}
return this;
}
public String getValue()
{
return value.toString();
}
public ChatboxTextInput charValidator(IntPredicate val)
{
if (val == null)
{
val = getDefaultCharValidator();
}
this.charValidator = val;
return this;
}
public ChatboxTextInput onClose(Runnable onClose)
{
this.onClose = onClose;
return this;
}
public ChatboxTextInput onDone(Consumer<String> onDone)
{
this.onDone = onDone;
return this;
}
public ChatboxTextInput onChanged(Consumer<String> onChanged)
{
this.onChanged = onChanged;
return this;
}
public ChatboxTextInput fontID(int fontID)
{
this.fontID = fontID;
return this;
}
protected void update()
{
Widget container = chatboxPanelManager.getContainerWidget();
container.deleteAllChildren();
Widget promptWidget = container.createChild(-1, WidgetType.TEXT);
promptWidget.setText(this.prompt);
promptWidget.setTextColor(0x800000);
promptWidget.setFontId(fontID);
promptWidget.setXPositionMode(WidgetPositionMode.ABSOLUTE_CENTER);
promptWidget.setOriginalX(0);
promptWidget.setYPositionMode(WidgetPositionMode.ABSOLUTE_TOP);
promptWidget.setOriginalY(8);
promptWidget.setOriginalHeight(24);
promptWidget.setXTextAlignment(WidgetTextAlignment.CENTER);
promptWidget.setYTextAlignment(WidgetTextAlignment.CENTER);
promptWidget.setWidthMode(WidgetSizeMode.MINUS);
promptWidget.revalidate();
buildEdit(0, 50, container.getWidth(), 0);
}
protected void buildEdit(int x, int y, int w, int h)
{
final List<Line> editLines = new ArrayList<>();
Widget container = chatboxPanelManager.getContainerWidget();
final Widget cursor = container.createChild(-1, WidgetType.RECTANGLE);
long start = System.currentTimeMillis();
cursor.setOnTimerListener((JavaScriptCallback) ev ->
{
boolean on = (System.currentTimeMillis() - start) % CURSOR_FLASH_RATE_MILLIS > (CURSOR_FLASH_RATE_MILLIS / 2);
cursor.setOpacity(on ? 255 : 0);
});
cursor.setTextColor(0xFFFFFF);
cursor.setHasListener(true);
cursor.setFilled(true);
cursor.setFontId(fontID);
FontTypeFace font = cursor.getFont();
if (h <= 0)
{
h = font.getBaseline();
}
final int oy = y;
final int ox = x;
final int oh = h;
int breakIndex = -1;
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < value.length(); i++)
{
int count = i - sb.length();
final String c = value.charAt(i) + "";
sb.append(c);
if (BREAK_MATCHER.matcher(c).matches())
{
breakIndex = sb.length();
}
if (i == value.length() - 1)
{
Line line = new Line(count, count + sb.length() - 1, sb.toString());
editLines.add(line);
break;
}
if (font.getTextWidth(sb.toString() + value.charAt(i + 1)) < w)
{
continue;
}
if (editLines.size() < this.lines - 1 || this.lines == 0)
{
if (breakIndex > 1)
{
String str = sb.substring(0, breakIndex);
Line line = new Line(count, count + str.length() - 1, str);
editLines.add(line);
sb.replace(0, breakIndex, "");
breakIndex = -1;
continue;
}
Line line = new Line(count, count + sb.length() - 1, sb.toString());
editLines.add(line);
sb.replace(0, sb.length(), "");
}
}
Rectangle bounds = new Rectangle(container.getCanvasLocation().getX() + container.getWidth(), y, 0, editLines.size() * oh);
for (int i = 0; i < editLines.size() || i == 0; i++)
{
final Line line = editLines.size() > 0 ? editLines.get(i) : new Line(0, 0, "");
final String text = line.text;
final int len = text.length();
String lt = Text.escapeJagex(text);
String mt = "";
String rt = "";
final boolean isStartLine = cursorOnLine(cursorStart, line.start, line.end)
|| (cursorOnLine(cursorStart, line.start, line.end + 1) && i == editLines.size() - 1);
final boolean isEndLine = cursorOnLine(cursorEnd, line.start, line.end);
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);
lt = Text.escapeJagex(text.substring(0, cIdx));
mt = Text.escapeJagex(text.substring(cIdx, ceIdx));
rt = Text.escapeJagex(text.substring(ceIdx));
}
final int ltw = font.getTextWidth(lt);
final int mtw = font.getTextWidth(mt);
final int rtw = font.getTextWidth(rt);
final int fullWidth = ltw + mtw + rtw;
int ltx = ox;
if (w > 0)
{
ltx += (w - fullWidth) / 2;
}
final int mtx = ltx + ltw;
final int rtx = mtx + mtw;
if (ltx < bounds.x)
{
bounds.setLocation(ltx, bounds.y);
}
if (fullWidth > bounds.width)
{
bounds.setSize(fullWidth, bounds.height);
}
if (editLines.size() == 0 || isStartLine)
{
cursor.setOriginalX(mtx - 1);
cursor.setOriginalY(y);
cursor.setOriginalWidth(2);
cursor.setOriginalHeight(h);
cursor.revalidate();
}
if (!Strings.isNullOrEmpty(lt))
{
final Widget leftText = container.createChild(-1, WidgetType.TEXT);
leftText.setFontId(fontID);
leftText.setText(lt);
leftText.setOriginalX(ltx);
leftText.setOriginalY(y);
leftText.setOriginalWidth(ltw);
leftText.setOriginalHeight(h);
leftText.revalidate();
}
if (!Strings.isNullOrEmpty(mt))
{
final Widget background = container.createChild(-1, WidgetType.RECTANGLE);
background.setTextColor(0x113399);
background.setFilled(true);
background.setOriginalX(mtx - 1);
background.setOriginalY(y);
background.setOriginalWidth(2 + mtw);
background.setOriginalHeight(h);
background.revalidate();
final Widget middleText = container.createChild(-1, WidgetType.TEXT);
middleText.setText(mt);
middleText.setFontId(fontID);
middleText.setOriginalX(mtx);
middleText.setOriginalY(y);
middleText.setOriginalWidth(mtw);
middleText.setOriginalHeight(h);
middleText.setTextColor(0xFFFFFF);
middleText.revalidate();
}
if (!Strings.isNullOrEmpty(rt))
{
final Widget rightText = container.createChild(-1, WidgetType.TEXT);
rightText.setText(rt);
rightText.setFontId(fontID);
rightText.setOriginalX(rtx);
rightText.setOriginalY(y);
rightText.setOriginalWidth(rtw);
rightText.setOriginalHeight(h);
rightText.revalidate();
}
y += h;
}
net.runelite.api.Point ccl = container.getCanvasLocation();
isInBounds = ev -> bounds.contains(new Point(ev.getX() - ccl.getX(), ev.getY() - ccl.getY()));
getPointCharOffset = p ->
{
if (bounds.width <= 0)
{
return 0;
}
int cx = p.x - ccl.getX() - ox;
int cy = p.y - ccl.getY() - oy;
int currentLine = MiscUtils.clamp(cy / oh, 0, editLines.size() - 1);
final Line line = editLines.get(currentLine);
final String tsValue = line.text;
int charIndex = tsValue.length();
int fullWidth = font.getTextWidth(tsValue);
int tx = ox;
if (w > 0)
{
tx += (w - fullWidth) / 2;
}
cx -= tx;
// `i` is used to track max execution time incase there is a font with ligature width data that causes this to fail
for (int i = tsValue.length(); i >= 0 && charIndex >= 0 && charIndex <= tsValue.length(); i--)
{
int lcx = charIndex > 0 ? font.getTextWidth(Text.escapeJagex(tsValue.substring(0, charIndex - 1))) : 0;
int mcx = font.getTextWidth(Text.escapeJagex(tsValue.substring(0, charIndex)));
int rcx = charIndex + 1 <= tsValue.length() ? font.getTextWidth(Text.escapeJagex(tsValue.substring(0, charIndex + 1))) : mcx;
int leftBound = (lcx + mcx) / 2;
int rightBound = (mcx + rcx) / 2;
if (cx < leftBound)
{
charIndex--;
continue;
}
if (cx > rightBound)
{
charIndex++;
continue;
}
break;
}
charIndex = MiscUtils.clamp(charIndex, 0, tsValue.length());
return line.start + charIndex;
};
getLineOffset = code ->
{
if (editLines.size() < 2)
{
return cursorStart;
}
int currentLine = -1;
for (int i = 0; i < editLines.size(); i++)
{
Line l = editLines.get(i);
if (cursorOnLine(cursorStart, l.start, l.end)
|| (cursorOnLine(cursorStart, l.start, l.end + 1) && i == editLines.size() - 1))
{
currentLine = i;
break;
}
}
if (currentLine == -1
|| (code == KeyEvent.VK_UP && currentLine == 0)
|| (code == KeyEvent.VK_DOWN && currentLine == editLines.size() - 1))
{
return cursorStart;
}
final Line line = editLines.get(currentLine);
final int direction = code == KeyEvent.VK_UP ? -1 : 1;
final Point dest = new Point(cursor.getCanvasLocation().getX(), cursor.getCanvasLocation().getY() + (direction * oh));
final int charOffset = getPointCharOffset.applyAsInt(dest);
// Place cursor on right line if whitespace keep it on the same line or skip a line
final Line nextLine = editLines.get(currentLine + direction);
if ((direction == -1 && charOffset >= line.start)
|| (direction == 1 && (charOffset > nextLine.end && (currentLine + direction != editLines.size() - 1))))
{
return nextLine.end;
}
return charOffset;
};
}
private boolean cursorOnLine(final int cursor, final int start, final int end)
{
return (cursor >= start) && (cursor <= end);
}
private int getCharOffset(MouseEvent ev)
{
if (getPointCharOffset == null)
{
return 0;
}
return getPointCharOffset.applyAsInt(ev.getPoint());
}
@Override
protected void open()
{
this.built = true;
update();
}
@Override
protected void close()
{
if (this.onClose != null)
{
this.onClose.run();
}
}
public ChatboxTextInput build()
{
if (prompt == null)
{
throw new IllegalStateException("prompt must be non-null");
}
chatboxPanelManager.openInput(this);
return this;
}
@Override
public void keyTyped(KeyEvent e)
{
char c = e.getKeyChar();
if (charValidator.test(c))
{
if (cursorStart != cursorEnd)
{
value.delete(cursorStart, cursorEnd);
}
value.insert(cursorStart, c);
cursorAt(cursorStart + 1);
if (onChanged != null)
{
onChanged.accept(getValue());
}
}
}
@Override
public void keyPressed(KeyEvent ev)
{
int code = ev.getKeyCode();
if (ev.isControlDown())
{
switch (code)
{
case KeyEvent.VK_X:
case KeyEvent.VK_C:
if (cursorStart != cursorEnd)
{
String s = value.substring(cursorStart, cursorEnd);
if (code == KeyEvent.VK_X)
{
value.delete(cursorStart, cursorEnd);
cursorAt(cursorStart);
}
Toolkit.getDefaultToolkit()
.getSystemClipboard()
.setContents(new StringSelection(s), null);
}
return;
case KeyEvent.VK_V:
try
{
String s = Toolkit.getDefaultToolkit()
.getSystemClipboard()
.getData(DataFlavor.stringFlavor)
.toString();
if (cursorStart != cursorEnd)
{
value.delete(cursorStart, cursorEnd);
}
for (int i = 0; i < s.length(); i++)
{
char ch = s.charAt(i);
if (charValidator.test(ch))
{
value.insert(cursorStart, ch);
cursorStart++;
}
}
cursorAt(cursorStart);
if (onChanged != null)
{
onChanged.accept(getValue());
}
}
catch (IOException | UnsupportedFlavorException ex)
{
log.warn("Unable to get clipboard", ex);
}
return;
}
return;
}
int newPos = cursorStart;
if (ev.isShiftDown())
{
if (selectionEnd == -1 || selectionStart == -1)
{
selectionStart = cursorStart;
selectionEnd = cursorStart;
}
newPos = selectionEnd;
}
else
{
selectionStart = -1;
selectionEnd = -1;
}
switch (code)
{
case KeyEvent.VK_DELETE:
if (cursorStart != cursorEnd)
{
value.delete(cursorStart, cursorEnd);
cursorAt(cursorStart);
if (onChanged != null)
{
onChanged.accept(getValue());
}
return;
}
if (cursorStart < value.length())
{
value.deleteCharAt(cursorStart);
cursorAt(cursorStart);
if (onChanged != null)
{
onChanged.accept(getValue());
}
}
return;
case KeyEvent.VK_BACK_SPACE:
if (cursorStart != cursorEnd)
{
value.delete(cursorStart, cursorEnd);
cursorAt(cursorStart);
if (onChanged != null)
{
onChanged.accept(getValue());
}
return;
}
if (cursorStart > 0)
{
value.deleteCharAt(cursorStart - 1);
cursorAt(cursorStart - 1);
if (onChanged != null)
{
onChanged.accept(getValue());
}
}
return;
case KeyEvent.VK_LEFT:
ev.consume();
newPos--;
break;
case KeyEvent.VK_RIGHT:
ev.consume();
newPos++;
break;
case KeyEvent.VK_UP:
ev.consume();
newPos = getLineOffset.applyAsInt(code);
break;
case KeyEvent.VK_DOWN:
ev.consume();
newPos = getLineOffset.applyAsInt(code);
break;
case KeyEvent.VK_HOME:
ev.consume();
newPos = 0;
break;
case KeyEvent.VK_END:
ev.consume();
newPos = value.length();
break;
case KeyEvent.VK_ENTER:
ev.consume();
if (onDone != null)
{
onDone.accept(getValue());
}
chatboxPanelManager.close();
return;
case KeyEvent.VK_ESCAPE:
ev.consume();
if (cursorStart != cursorEnd)
{
cursorAt(cursorStart);
return;
}
chatboxPanelManager.close();
return;
default:
return;
}
if (newPos > value.length())
{
newPos = value.length();
}
if (newPos < 0)
{
newPos = 0;
}
if (ev.isShiftDown())
{
selectionEnd = newPos;
cursorAt(selectionStart, newPos);
}
else
{
cursorAt(newPos);
}
}
@Override
public void keyReleased(KeyEvent e)
{
}
@Override
public MouseEvent mouseClicked(MouseEvent mouseEvent)
{
return mouseEvent;
}
@Override
public MouseEvent mousePressed(MouseEvent mouseEvent)
{
if (mouseEvent.getButton() != MouseEvent.BUTTON1)
{
return mouseEvent;
}
if (isInBounds == null || !isInBounds.test(mouseEvent))
{
if (cursorStart != cursorEnd)
{
selectionStart = -1;
selectionEnd = -1;
cursorAt(getCharOffset(mouseEvent));
}
return mouseEvent;
}
int nco = getCharOffset(mouseEvent);
if (mouseEvent.isShiftDown() && selectionEnd != -1)
{
selectionEnd = nco;
cursorAt(selectionStart, selectionEnd);
}
else
{
selectionStart = nco;
cursorAt(nco);
}
return mouseEvent;
}
@Override
public MouseEvent mouseReleased(MouseEvent mouseEvent)
{
return mouseEvent;
}
@Override
public MouseEvent mouseEntered(MouseEvent mouseEvent)
{
return mouseEvent;
}
@Override
public MouseEvent mouseExited(MouseEvent mouseEvent)
{
return mouseEvent;
}
@Override
public MouseEvent mouseDragged(MouseEvent mouseEvent)
{
if (!SwingUtilities.isLeftMouseButton(mouseEvent))
{
return mouseEvent;
}
int nco = getCharOffset(mouseEvent);
if (selectionStart != -1)
{
selectionEnd = nco;
cursorAt(selectionStart, selectionEnd);
}
return mouseEvent;
}
@Override
public MouseEvent mouseMoved(MouseEvent mouseEvent)
{
return mouseEvent;
}
}

View File

@@ -1,213 +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.game.chatbox;
import com.google.inject.Inject;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.FontID;
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;
@Slf4j
public class ChatboxTextMenuInput extends ChatboxInput implements KeyListener
{
@Data
@AllArgsConstructor
private static final class Entry
{
private String text;
private Runnable callback;
}
private final ChatboxPanelManager chatboxPanelManager;
@Getter
private String title;
@Getter
private List<Entry> options = new ArrayList<>();
@Getter
private Runnable onClose;
@Inject
protected ChatboxTextMenuInput(ChatboxPanelManager chatboxPanelManager)
{
this.chatboxPanelManager = chatboxPanelManager;
}
public ChatboxTextMenuInput title(String title)
{
this.title = title;
return this;
}
public ChatboxTextMenuInput option(String text, Runnable callback)
{
options.add(new Entry(text, callback));
return this;
}
public ChatboxTextMenuInput onClose(Runnable onClose)
{
this.onClose = onClose;
return this;
}
public ChatboxTextMenuInput build()
{
if (title == null)
{
throw new IllegalStateException("Title must be set");
}
if (options.size() < 1)
{
throw new IllegalStateException("You must have atleast 1 option");
}
chatboxPanelManager.openInput(this);
return this;
}
@Override
protected void open()
{
Widget container = chatboxPanelManager.getContainerWidget();
Widget prompt = container.createChild(-1, WidgetType.TEXT);
prompt.setText(title);
prompt.setTextColor(0x800000);
prompt.setFontId(FontID.QUILL_8);
prompt.setXPositionMode(WidgetPositionMode.ABSOLUTE_CENTER);
prompt.setOriginalX(0);
prompt.setYPositionMode(WidgetPositionMode.ABSOLUTE_TOP);
prompt.setOriginalY(8);
prompt.setOriginalHeight(24);
prompt.setXTextAlignment(WidgetTextAlignment.CENTER);
prompt.setYTextAlignment(WidgetTextAlignment.CENTER);
prompt.setWidthMode(WidgetSizeMode.MINUS);
prompt.revalidate();
int y = prompt.getRelativeX() + prompt.getHeight() + 6;
int height = container.getHeight() - y - 8;
int step = height / options.size();
int maxStep = options.size() >= 3 ? 25 : 30;
if (step > maxStep)
{
int ds = step - maxStep;
step = maxStep;
y += (ds * options.size()) / 2;
}
for (Entry option : options)
{
Widget optWidget = container.createChild(-1, WidgetType.TEXT);
optWidget.setText(option.text);
optWidget.setFontId(FontID.QUILL_8);
optWidget.setXPositionMode(WidgetPositionMode.ABSOLUTE_CENTER);
optWidget.setOriginalX(0);
optWidget.setYPositionMode(WidgetPositionMode.ABSOLUTE_TOP);
optWidget.setOriginalY(y);
optWidget.setOriginalHeight(24);
optWidget.setXTextAlignment(WidgetTextAlignment.CENTER);
optWidget.setYTextAlignment(WidgetTextAlignment.CENTER);
optWidget.setWidthMode(WidgetSizeMode.MINUS);
optWidget.setAction(0, "Continue");
optWidget.setOnOpListener((JavaScriptCallback) ev -> callback(option));
optWidget.setOnMouseOverListener((JavaScriptCallback) ev -> optWidget.setTextColor(0xFFFFFF));
optWidget.setOnMouseLeaveListener((JavaScriptCallback) ev -> optWidget.setTextColor(0));
optWidget.setHasListener(true);
optWidget.revalidate();
y += step;
}
}
private void callback(Entry entry)
{
Widget container = chatboxPanelManager.getContainerWidget();
container.setOnKeyListener((Object[]) null);
chatboxPanelManager.close();
entry.callback.run();
}
@Override
protected void close()
{
if (onClose != null)
{
onClose.run();
}
}
@Override
public void keyTyped(KeyEvent e)
{
char c = e.getKeyChar();
if (c == '\033')
{
chatboxPanelManager.close();
e.consume();
return;
}
int n = c - '1';
if (n >= 0 && n < options.size())
{
callback(options.get(n));
e.consume();
}
}
@Override
public void keyPressed(KeyEvent e)
{
if (e.getKeyCode() == KeyEvent.VK_ESCAPE)
{
e.consume();
}
}
@Override
public void keyReleased(KeyEvent e)
{
}
}

View File

@@ -1,973 +0,0 @@
/*
* Copyright (c) 2018, Woox <https://github.com/wooxsolo>
* 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.MainBufferProvider;
import net.runelite.api.Model;
import net.runelite.api.NPC;
import net.runelite.api.NPCComposition;
import net.runelite.api.Perspective;
import net.runelite.api.Player;
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<List<PixelDistanceAlpha>> 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<PixelDistanceAlpha> 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<PixelDistanceAlpha> 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<PixelDistanceAlpha> 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;
NPCComposition composition = npc.getTransformedComposition();
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.getOrientation(), 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.getOrientation(), outlineWidth, innerColor, outerColor);
}
}
}

View File

@@ -1,36 +0,0 @@
/*
* Copyright (c) 2018, Woox <https://github.com/wooxsolo>
* 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 lombok.RequiredArgsConstructor;
import lombok.Value;
@Value
@RequiredArgsConstructor
class PixelDistanceAlpha
{
private final int outerAlpha;
private final int distArrayPos;
}

View File

@@ -1,29 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.input;
public interface KeyListener extends java.awt.event.KeyListener
{
}

View File

@@ -1,73 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.input;
import java.awt.event.KeyEvent;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.inject.Singleton;
@Singleton
public class KeyManager
{
private final List<KeyListener> keyListeners = new CopyOnWriteArrayList<>();
public void registerKeyListener(KeyListener keyListener)
{
if (!keyListeners.contains(keyListener))
{
keyListeners.add(keyListener);
}
}
public void unregisterKeyListener(KeyListener keyListener)
{
keyListeners.remove(keyListener);
}
public void processKeyPressed(KeyEvent keyEvent)
{
for (KeyListener keyListener : keyListeners)
{
keyListener.keyPressed(keyEvent);
}
}
public void processKeyReleased(KeyEvent keyEvent)
{
for (KeyListener keyListener : keyListeners)
{
keyListener.keyReleased(keyEvent);
}
}
public void processKeyTyped(KeyEvent keyEvent)
{
for (KeyListener keyListener : keyListeners)
{
keyListener.keyTyped(keyEvent);
}
}
}

View File

@@ -1,72 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.input;
import java.awt.event.MouseEvent;
public abstract class MouseAdapter implements MouseListener
{
@Override
public MouseEvent mouseClicked(MouseEvent mouseEvent)
{
return mouseEvent;
}
@Override
public MouseEvent mousePressed(MouseEvent mouseEvent)
{
return mouseEvent;
}
@Override
public MouseEvent mouseReleased(MouseEvent mouseEvent)
{
return mouseEvent;
}
@Override
public MouseEvent mouseEntered(MouseEvent mouseEvent)
{
return mouseEvent;
}
@Override
public MouseEvent mouseExited(MouseEvent mouseEvent)
{
return mouseEvent;
}
@Override
public MouseEvent mouseDragged(MouseEvent mouseEvent)
{
return mouseEvent;
}
@Override
public MouseEvent mouseMoved(MouseEvent mouseEvent)
{
return mouseEvent;
}
}

View File

@@ -1,44 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.input;
import java.awt.event.MouseEvent;
public interface MouseListener
{
MouseEvent mouseClicked(MouseEvent mouseEvent);
MouseEvent mousePressed(MouseEvent mouseEvent);
MouseEvent mouseReleased(MouseEvent mouseEvent);
MouseEvent mouseEntered(MouseEvent mouseEvent);
MouseEvent mouseExited(MouseEvent mouseEvent);
MouseEvent mouseDragged(MouseEvent mouseEvent);
MouseEvent mouseMoved(MouseEvent mouseEvent);
}

View File

@@ -1,146 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.input;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.inject.Singleton;
@Singleton
public class MouseManager
{
private final List<MouseListener> mouseListeners = new CopyOnWriteArrayList<>();
private final List<MouseWheelListener> mouseWheelListeners = new CopyOnWriteArrayList<>();
public void registerMouseListener(MouseListener mouseListener)
{
if (!mouseListeners.contains(mouseListener))
{
mouseListeners.add(mouseListener);
}
}
public void registerMouseListener(int position, MouseListener mouseListener)
{
mouseListeners.add(position, mouseListener);
}
public void unregisterMouseListener(MouseListener mouseListener)
{
mouseListeners.remove(mouseListener);
}
public void registerMouseWheelListener(MouseWheelListener mouseWheelListener)
{
if (!mouseWheelListeners.contains(mouseWheelListener))
{
mouseWheelListeners.add(mouseWheelListener);
}
}
public void registerMouseWheelListener(int position, MouseWheelListener mouseWheelListener)
{
mouseWheelListeners.add(position, mouseWheelListener);
}
public void unregisterMouseWheelListener(MouseWheelListener mouseWheelListener)
{
mouseWheelListeners.remove(mouseWheelListener);
}
public MouseEvent processMousePressed(MouseEvent mouseEvent)
{
for (MouseListener mouseListener : mouseListeners)
{
mouseEvent = mouseListener.mousePressed(mouseEvent);
}
return mouseEvent;
}
public MouseEvent processMouseReleased(MouseEvent mouseEvent)
{
for (MouseListener mouseListener : mouseListeners)
{
mouseEvent = mouseListener.mouseReleased(mouseEvent);
}
return mouseEvent;
}
public MouseEvent processMouseClicked(MouseEvent mouseEvent)
{
for (MouseListener mouseListener : mouseListeners)
{
mouseEvent = mouseListener.mouseClicked(mouseEvent);
}
return mouseEvent;
}
public MouseEvent processMouseEntered(MouseEvent mouseEvent)
{
for (MouseListener mouseListener : mouseListeners)
{
mouseEvent = mouseListener.mouseEntered(mouseEvent);
}
return mouseEvent;
}
public MouseEvent processMouseExited(MouseEvent mouseEvent)
{
for (MouseListener mouseListener : mouseListeners)
{
mouseEvent = mouseListener.mouseExited(mouseEvent);
}
return mouseEvent;
}
public MouseEvent processMouseDragged(MouseEvent mouseEvent)
{
for (MouseListener mouseListener : mouseListeners)
{
mouseEvent = mouseListener.mouseDragged(mouseEvent);
}
return mouseEvent;
}
public MouseEvent processMouseMoved(MouseEvent mouseEvent)
{
for (MouseListener mouseListener : mouseListeners)
{
mouseEvent = mouseListener.mouseMoved(mouseEvent);
}
return mouseEvent;
}
public MouseWheelEvent processMouseWheelMoved(MouseWheelEvent mouseWheelEvent)
{
for (MouseWheelListener mouseWheelListener : mouseWheelListeners)
{
mouseWheelEvent = mouseWheelListener.mouseWheelMoved(mouseWheelEvent);
}
return mouseWheelEvent;
}
}

View File

@@ -1,32 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.input;
import java.awt.event.MouseWheelEvent;
public interface MouseWheelListener
{
MouseWheelEvent mouseWheelMoved(MouseWheelEvent event);
}

View File

@@ -1,116 +0,0 @@
/*
* Copyright (c) 2019, Lucas <https://github.com/Lucwousin>
* 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 joptsimple.internal.Strings;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import net.runelite.api.MenuEntry;
import static net.runelite.client.menus.MenuManager.LEVEL_PATTERN;
import net.runelite.client.util.Text;
@EqualsAndHashCode
public class ComparableEntry
{
@Getter
private String option;
@Getter
private String target;
@Getter
private int id;
@Getter
private int type;
@Getter
private boolean strictOption;
@Getter
private boolean strictTarget;
public ComparableEntry(String option, String target)
{
this(option, target, -1, -1, true, true);
}
public ComparableEntry(String option, String target, boolean strictTarget)
{
this(option, target, -1, -1, true, strictTarget);
}
public ComparableEntry(String option, String target, int id, int type, boolean strictOption, boolean strictTarget)
{
this.option = option;
this.target = target;
this.id = id;
this.type = type;
this.strictOption = strictOption;
this.strictTarget = strictTarget;
}
boolean matches(MenuEntry entry)
{
String opt = Text.standardize(entry.getOption());
if (strictOption && !opt.equals(option) || !strictOption && !opt.contains(option))
{
return false;
}
if (strictTarget || !Strings.isNullOrEmpty(target))
{
String tgt = Text.standardize(LEVEL_PATTERN.matcher(entry.getTarget()).replaceAll(""));
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.getType();
if (this.type != type)
{
return false;
}
}
return true;
}
}

View File

@@ -1,814 +0,0 @@
/*
* Copyright (c) 2017, Robin <robin.weymans@gmail.com>
* Copyright (c) 2019, Lucas <https://github.com/Lucwousin>
* 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 com.google.common.base.Preconditions;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import javax.inject.Inject;
import javax.inject.Singleton;
import joptsimple.internal.Strings;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.MenuAction;
import net.runelite.api.MenuEntry;
import net.runelite.api.NPCComposition;
import net.runelite.api.ObjectComposition;
import net.runelite.api.events.MenuEntryAdded;
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.widgets.WidgetInfo;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.util.Text;
@Singleton
@Slf4j
public class MenuManager
{
/*
* The index needs to be between 4 and 7,
*/
private static final int IDX_LOWER = 4;
private static final int IDX_UPPER = 8;
static final Pattern LEVEL_PATTERN = Pattern.compile("\\(level-[0-9]*\\)");
private static MenuEntry CANCEL()
{
MenuEntry cancel = new MenuEntry();
cancel.setOption("Cancel");
cancel.setTarget("");
cancel.setIdentifier(0);
cancel.setType(MenuAction.CANCEL.getId());
cancel.setParam0(0);
cancel.setParam1(0);
return cancel;
}
private final Client client;
private final EventBus eventBus;
//Maps the indexes that are being used to the menu option.
private final Map<Integer, String> playerMenuIndexMap = new HashMap<>();
//Used to manage custom non-player menu options
private final Multimap<Integer, WidgetMenuOption> managedMenuOptions = HashMultimap.create();
private final Set<String> npcMenuOptions = new HashSet<>();
private final Set<ComparableEntry> priorityEntries = new HashSet<>();
private final Set<MenuEntry> currentPriorityEntries = new HashSet<>();
private final Map<ComparableEntry, ComparableEntry> swaps = new HashMap<>();
private final Set<MenuEntry> originalTypes = new HashSet<>();
private final Set<Integer> leftClickObjects = new HashSet<>();
@Inject
private MenuManager(Client client, EventBus eventBus)
{
this.client = client;
this.eventBus = eventBus;
}
/**
* Adds a CustomMenuOption to the list of managed menu options.
*
* @param customMenuOption The custom menu to add
*/
public void addManagedCustomMenu(WidgetMenuOption customMenuOption)
{
WidgetInfo widget = customMenuOption.getWidget();
managedMenuOptions.put(widget.getId(), customMenuOption);
}
/**
* Removes a CustomMenuOption from the list of managed menu options.
*
* @param customMenuOption The custom menu to add
*/
public void removeManagedCustomMenu(WidgetMenuOption customMenuOption)
{
WidgetInfo widget = customMenuOption.getWidget();
managedMenuOptions.remove(widget.getId(), customMenuOption);
}
private boolean menuContainsCustomMenu(WidgetMenuOption customMenuOption)
{
for (MenuEntry menuEntry : client.getMenuEntries())
{
String option = menuEntry.getOption();
String target = menuEntry.getTarget();
if (option.equals(customMenuOption.getMenuOption()) && target.equals(customMenuOption.getMenuTarget()))
{
return true;
}
}
return false;
}
@Subscribe
public void onMenuEntryAdded(MenuEntryAdded event)
{
int widgetId = event.getActionParam1();
Collection<WidgetMenuOption> options = managedMenuOptions.get(widgetId);
MenuEntry[] menuEntries = client.getMenuEntries();
if (menuEntries.length == 1)
{
// Menu entries reset, so priority entries should reset as well
currentPriorityEntries.clear();
originalTypes.clear();
}
for (WidgetMenuOption currentMenu : options)
{
if (!menuContainsCustomMenu(currentMenu))//Don't add if we have already added it to this widget
{
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);
}
}
final MenuEntry newestEntry = menuEntries[menuEntries.length - 1];
boolean isPrio = false;
for (ComparableEntry p : priorityEntries)
{
if (p.matches(newestEntry))
{
isPrio = true;
break;
}
}
// If the last entry was a priority entry, keep track of it
if (isPrio)
{
currentPriorityEntries.add(newestEntry);
}
// Make a copy of the menu entries, cause you can't remove from Arrays.asList()
List<MenuEntry> copy = new ArrayList<>(Arrays.asList(menuEntries));
// If there are entries we want to prioritize, we have to remove the rest
if (!currentPriorityEntries.isEmpty())
{
copy.retainAll(currentPriorityEntries);
copy.add(0, CANCEL());
}
// Find the current entry in the swaps map
ComparableEntry swapEntry = null;
for (ComparableEntry e : swaps.keySet())
{
if (e.matches(newestEntry))
{
swapEntry = e;
break;
}
}
if (swapEntry != null)
{
ComparableEntry swapTarget = swaps.get(swapEntry);
// Find the target for the swap in current menu entries
MenuEntry foundSwap = null;
for (MenuEntry entry : Lists.reverse(copy))
{
if (swapTarget.matches(entry))
{
foundSwap = entry;
break;
}
}
if (foundSwap != null)
{
// This is the menu entry added last's type
final int otherType = foundSwap.getType();
// MenuActions with an id of over 1000 get shifted to the back of the menu entry array
// They have different id's in the packet buffer though, so we got to modify them back on click
// I couldn't get this to work with objects, so we're using modified objectcomposition for that
final boolean shouldModifyType = otherType == MenuAction.EXAMINE_ITEM_BANK_EQ.getId();
if (shouldModifyType)
{
foundSwap.setType(MenuAction.WIDGET_DEFAULT.getId());
originalTypes.add(foundSwap);
}
// Swap
int index = copy.indexOf(foundSwap);
int newIndex = copy.indexOf(newestEntry);
copy.set(index, newestEntry);
copy.set(newIndex, foundSwap);
}
}
client.setMenuEntries(copy.toArray(new MenuEntry[0]));
}
public void addPlayerMenuItem(String menuText)
{
Preconditions.checkNotNull(menuText);
int playerMenuIndex = findEmptyPlayerMenuIndex();
if (playerMenuIndex == IDX_UPPER)
{
return; // no more slots
}
addPlayerMenuItem(playerMenuIndex, menuText);
}
public void removePlayerMenuItem(String menuText)
{
Preconditions.checkNotNull(menuText);
for (Map.Entry<Integer, String> entry : playerMenuIndexMap.entrySet())
{
if (entry.getValue().equalsIgnoreCase(menuText))
{
removePlayerMenuItem(entry.getKey());
break;
}
}
}
public boolean toggleLeftClick(String menuText, int objectID, boolean reset)
{
Preconditions.checkNotNull(menuText);
if (client == null)
{
return false;
}
ObjectComposition oc = client.getObjectDefinition(objectID);
if (oc == null)
{
return false;
}
ObjectComposition impostor = oc.getImpostorIds() != null ? oc.getImpostor() : null;
if (impostor != null)
{
if (toggleLeftClick(menuText, impostor.getId(), reset))
{
// Sorry about this
leftClickObjects.remove(impostor.getId());
if (reset)
{
leftClickObjects.remove(objectID);
}
else
{
leftClickObjects.add(objectID);
}
return true;
}
}
String[] options = oc.getActions();
if (options == null)
{
return false;
}
boolean hasOption5 = !Strings.isNullOrEmpty(options[options.length - 1]);
boolean hasOption1 = !Strings.isNullOrEmpty(options[0]);
if (hasOption5 || hasOption1)
{
String option1 = options[0];
String option5 = options[options.length - 1];
if (reset && !hasOption1 // Won't have to reset anything cause
|| reset && !menuText.equalsIgnoreCase(option1) // theres nothing to reset
|| hasOption5 && !menuText.equalsIgnoreCase(option5))
{
return false;
}
options[0] = option5;
options[options.length - 1] = option1;
}
else
{
return false;
}
if (reset)
{
leftClickObjects.remove(objectID);
}
else
{
leftClickObjects.add(objectID);
}
return true;
}
@Subscribe
public void onPlayerMenuOptionsChanged(PlayerMenuOptionsChanged event)
{
int idx = event.getIndex();
String menuText = playerMenuIndexMap.get(idx);
if (menuText == null)
{
return; // not our menu
}
// find new index for this option
int newIdx = findEmptyPlayerMenuIndex();
if (newIdx == IDX_UPPER)
{
log.debug("Client has updated player menu index {} where option {} was, and there are no more free slots available", idx, menuText);
return;
}
log.debug("Client has updated player menu index {} where option {} was, moving to index {}", idx, menuText, newIdx);
playerMenuIndexMap.remove(idx);
addPlayerMenuItem(newIdx, menuText);
}
@Subscribe
public void onNpcActionChanged(NpcActionChanged event)
{
NPCComposition composition = event.getNpcComposition();
for (String npcOption : npcMenuOptions)
{
addNpcOption(composition, npcOption);
}
}
private void addNpcOption(NPCComposition composition, String npcOption)
{
String[] actions = composition.getActions();
int unused = -1;
for (int i = 0; i < actions.length; ++i)
{
if (actions[i] == null && unused == -1)
{
unused = i;
}
else if (actions[i] != null && actions[i].equals(npcOption))
{
return;
}
}
if (unused == -1)
{
return;
}
actions[unused] = npcOption;
}
private void removeNpcOption(NPCComposition composition, String npcOption)
{
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;
}
}
}
@Subscribe
public void onMenuOptionClicked(MenuOptionClicked event)
{
if (!event.getMenuTarget().equals("do not edit") &&
!originalTypes.isEmpty() &&
event.getMenuAction() == MenuAction.WIDGET_DEFAULT)
{
for (MenuEntry e : originalTypes)
{
// Honestly, I was about to write a huge ass rant about
// how I hate whoever wrote the menuoptionclicked class
// but I decided that that'd be un-nice to them, and they
// probably spent over 24 hours writing it. Not because
// it was that difficult to write, of course, but because
// they must have the fucking iq of a retarded, under developed,
// braindead, basically good-for-nothing, idiotic chimp.
//
// Just kidding, of course, that would be too big of an
// insult towards those poor chimps. It's not their fault
// some dumbass is the way they are, right? Why should they
// feel bad for something they can't do anything about?
//
// Whoever wrote that class though, should actually feel
// 100% terrible. If they aren't depressed, I really wish
// they become depressed very, very soon. What the fuck
// were they even thinking.
if (event.getMenuAction().getId() != e.getType()
|| event.getId() != e.getIdentifier()
|| !event.getMenuOption().equals(e.getOption()))
{
continue;
}
//todo once bytecodes work again, re-enable
/* event.consume();
client.invokeMenuAction(
event.getActionParam(),
event.getWidgetId(),
MenuAction.EXAMINE_ITEM_BANK_EQ.getId(),
event.getId(),
event.getMenuOption(),
"do not edit",
client.getMouseCanvasPosition().getX(),
client.getMouseCanvasPosition().getY()
);*/
break;
}
}
if (!event.getMenuTarget().equals("do not edit") &&
!leftClickObjects.isEmpty() &&
event.getMenuAction() == MenuAction.GAME_OBJECT_FIRST_OPTION &&
(
leftClickObjects.contains(event.getId())
||
client.getObjectDefinition(event.getId()) != null &&
client.getObjectDefinition(event.getId()).getImpostorIds() != null &&
client.getObjectDefinition(event.getId()).getImpostor() != null &&
client.getObjectDefinition(event.getId()).getImpostor().getId() == event.getId()))
{
//todo once bytecodes work again, re-enable
/* event.consume();
client.invokeMenuAction(
event.getActionParam(),
event.getWidgetId(),
MenuAction.GAME_OBJECT_FIFTH_OPTION.getId(),
event.getId(),
event.getMenuOption(),
"do not edit",
client.getMouseCanvasPosition().getX(),
client.getMouseCanvasPosition().getY()
);*/
}
if (event.getMenuAction() != MenuAction.RUNELITE)
{
return; // not a player menu
}
int widgetId = event.getWidgetId();
Collection<WidgetMenuOption> options = managedMenuOptions.get(widgetId);
for (WidgetMenuOption curMenuOption : options)
{
if (curMenuOption.getMenuTarget().equals(event.getMenuTarget())
&& curMenuOption.getMenuOption().equals(event.getMenuOption()))
{
WidgetMenuOptionClicked customMenu = new WidgetMenuOptionClicked();
customMenu.setMenuOption(event.getMenuOption());
customMenu.setMenuTarget(event.getMenuTarget());
customMenu.setWidget(curMenuOption.getWidget());
eventBus.post(customMenu);
return; // don't continue because it's not a player option
}
}
String target = event.getMenuTarget();
// removes tags and level from player names for example:
// <col=ffffff>username<col=40ff00> (level-42) or <col=ffffff><img=2>username</col>
String username = Text.removeTags(target).split("[(]")[0].trim();
PlayerMenuOptionClicked playerMenuOptionClicked = new PlayerMenuOptionClicked();
playerMenuOptionClicked.setMenuOption(event.getMenuOption());
playerMenuOptionClicked.setMenuTarget(username);
eventBus.post(playerMenuOptionClicked);
}
private void addPlayerMenuItem(int playerOptionIndex, String menuText)
{
client.getPlayerOptions()[playerOptionIndex] = menuText;
client.getPlayerOptionsPriorities()[playerOptionIndex] = true;
client.getPlayerMenuTypes()[playerOptionIndex] = MenuAction.RUNELITE.getId();
playerMenuIndexMap.put(playerOptionIndex, menuText);
}
private void removePlayerMenuItem(int playerOptionIndex)
{
client.getPlayerOptions()[playerOptionIndex] = null;
playerMenuIndexMap.remove(playerOptionIndex);
}
/**
* Find the next empty player menu slot index
*/
private int findEmptyPlayerMenuIndex()
{
int index = IDX_LOWER;
String[] playerOptions = client.getPlayerOptions();
while (index < IDX_UPPER && playerOptions[index] != null)
{
index++;
}
return index;
}
/**
* Adds to the set of menu entries which when present, will remove all entries except for this one
*/
public void addPriorityEntry(String option, String target)
{
option = Text.standardize(option);
target = Text.standardize(target);
ComparableEntry entry = new ComparableEntry(option, target);
priorityEntries.add(entry);
}
public void removePriorityEntry(String option, String target)
{
option = Text.standardize(option);
target = Text.standardize(target);
ComparableEntry entry = new ComparableEntry(option, target);
Set<ComparableEntry> toRemove = new HashSet<>();
for (ComparableEntry priorityEntry : priorityEntries)
{
if (entry.equals(priorityEntry))
{
toRemove.add(entry);
}
}
for (ComparableEntry e : toRemove)
{
priorityEntries.remove(e);
}
}
/**
* 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 void addPriorityEntry(String option)
{
option = Text.standardize(option);
ComparableEntry entry = new ComparableEntry(option, "", false);
priorityEntries.add(entry);
}
public void removePriorityEntry(String option)
{
option = Text.standardize(option);
ComparableEntry entry = new ComparableEntry(option, "", false);
Set<ComparableEntry> toRemove = new HashSet<>();
for (ComparableEntry priorityEntry : priorityEntries)
{
if (entry.equals(priorityEntry))
{
toRemove.add(entry);
}
}
for (ComparableEntry e : toRemove)
{
priorityEntries.remove(e);
}
}
/**
* 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);
}
/**
* Adds to the map of swaps.
*/
public void addSwap(String option, String target, String option2, String target2, boolean strictOption, boolean strictTarget)
{
option = Text.standardize(option);
target = Text.standardize(target);
option2 = Text.standardize(option2);
target2 = Text.standardize(target2);
ComparableEntry swapFrom = new ComparableEntry(option, target, -1, -1, strictOption, strictTarget);
ComparableEntry swapTo = new ComparableEntry(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);
}
public void removeSwap(String option, String target, String option2, String target2, boolean strictOption, boolean strictTarget)
{
option = Text.standardize(option);
target = Text.standardize(target);
option2 = Text.standardize(option2);
target2 = Text.standardize(target2);
ComparableEntry swapFrom = new ComparableEntry(option, target, -1, -1, strictOption, strictTarget);
ComparableEntry swapTo = new ComparableEntry(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 Abstract entry
*/
public void addSwap(ComparableEntry swapFrom, ComparableEntry 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 type & 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 = Text.standardize(option);
target = Text.standardize(target);
option2 = Text.standardize(option2);
target2 = Text.standardize(target2);
ComparableEntry swapFrom = new ComparableEntry(option, target, id, type, false, false);
ComparableEntry swapTo = new ComparableEntry(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 = Text.standardize(option);
target = Text.standardize(target);
option2 = Text.standardize(option2);
target2 = Text.standardize(target2);
ComparableEntry swapFrom = new ComparableEntry(option, target, id, type, false, false);
ComparableEntry swapTo = new ComparableEntry(option2, target2, id2, type2, false, false);
Set<ComparableEntry> toRemove = new HashSet<>();
for (Map.Entry<ComparableEntry, ComparableEntry> e : swaps.entrySet())
{
if (e.getKey().equals(swapFrom) && e.getValue().equals(swapTo))
{
toRemove.add(e.getKey());
}
}
for (ComparableEntry entry : toRemove)
{
swaps.remove(entry);
}
}
public void removeSwap(ComparableEntry swapFrom, ComparableEntry swapTo)
{
Set<ComparableEntry> toRemove = new HashSet<>();
for (Map.Entry<ComparableEntry, ComparableEntry> e : swaps.entrySet())
{
if (e.getKey().equals(swapFrom) && e.getValue().equals(swapTo))
{
toRemove.add(e.getKey());
}
}
for (ComparableEntry entry : toRemove)
{
swaps.remove(entry);
}
}
/**
* Removes all swaps with target
*/
public void removeSwaps(String withTarget)
{
withTarget = Text.standardize(withTarget);
Set<ComparableEntry> toRemove = new HashSet<>();
for (ComparableEntry e : swaps.keySet())
{
if (e.getTarget().equals(withTarget))
{
toRemove.add(e);
}
}
for (ComparableEntry entry : toRemove)
{
swaps.remove(entry);
}
}
}

View File

@@ -1,105 +0,0 @@
/*
* Copyright (c) 2017, Robin <robin.weymans@gmail.com>
* 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 java.awt.Color;
import net.runelite.api.widgets.WidgetInfo;
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 menuOption of "Drop Bones" is "Drop"
*/
private String menuOption;
/**
* 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 menuTarget should be. Defaults to the brownish color that most menu options have.
*/
private Color color = JagexColors.MENU_TARGET;
/**
* The widget to add the option to
*/
private final WidgetInfo widget;
/**
* Creates a menu to be added to right click menus. The menu will only be added if match is found within the menu options
*
* @param menuOption Option text of this right click option
* @param menuTarget Target text of this right click option
* @param widget The widget to attach this option to
*/
public WidgetMenuOption(String menuOption, String menuTarget, WidgetInfo widget)
{
this.menuOption = menuOption;
setMenuTarget(menuTarget);
this.widget = widget;
}
public void setMenuOption(String option)
{
menuOption = option;
}
/**
* Sets the target of the menu option. Color code will be added on to target
*
* @param target The target text without color code.
*/
public void setMenuTarget(String target)
{
menuTarget = ColorUtil.wrapWithColorTag(target, color);
}
public String getMenuOption()
{
return menuOption;
}
public String getMenuTarget()
{
return menuTarget;
}
public WidgetInfo getWidget()
{
return widget;
}
public Color getColor()
{
return color;
}
public void setColor(Color col)
{
color = col;
}
}

View File

@@ -1,56 +0,0 @@
/*
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
* 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.inject.Binder;
import com.google.inject.Injector;
import com.google.inject.Module;
import java.io.File;
public abstract class Plugin implements Module
{
protected Injector injector;
public File file;
public PluginClassLoader loader;
@Override
public void configure(Binder binder)
{
}
protected void startUp() throws Exception
{
}
protected void shutDown() throws Exception
{
}
public final Injector getInjector()
{
return injector;
}
}

View File

@@ -1,67 +0,0 @@
/*
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
* 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 java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
/**
* A classloader for external plugins
*
* @author Adam
*/
public class PluginClassLoader extends URLClassLoader
{
private final ClassLoader parent;
public PluginClassLoader(File plugin, ClassLoader parent) throws MalformedURLException
{
super(
new URL[]
{
plugin.toURI().toURL()
},
null // null or else class path scanning includes everything from the main class loader
);
this.parent = parent;
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException
{
try
{
return super.loadClass(name);
}
catch (ClassNotFoundException ex)
{
// fall back to main class loader
return parent.loadClass(name);
}
}
}

View File

@@ -1,39 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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 java.lang.annotation.Documented;
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.TYPE)
@Documented
public @interface PluginDependencies
{
PluginDependency[] value();
}

View File

@@ -1,41 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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 java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(PluginDependencies.class)
public @interface PluginDependency
{
Class<? extends Plugin> value();
}

View File

@@ -1,63 +0,0 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* 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 java.lang.annotation.Documented;
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.TYPE)
@Documented
public @interface PluginDescriptor
{
String name();
/**
* A short, one-line summary of the plugin.
*/
String description() default "";
/**
* A list of plugin keywords, used (together with the name) when searching for plugins.
* Each tag should not contain any spaces, and should be fully lowercase.
*/
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.GENERAL_USE;
}

View File

@@ -1,39 +0,0 @@
/*
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
* 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;
public class PluginInstantiationException extends Exception
{
public PluginInstantiationException(String message)
{
super(message);
}
public PluginInstantiationException(Throwable cause)
{
super(cause);
}
}

Some files were not shown because too many files have changed in this diff Show More