Merge branch 'master' into pr/14

This commit is contained in:
xKylee
2019-08-26 00:41:39 +01:00
2644 changed files with 432608 additions and 24206 deletions

View File

@@ -1,113 +1,113 @@
/*
* 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;
}
}
}
/*
* 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

@@ -24,7 +24,6 @@
*/
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;
@@ -101,9 +100,7 @@ public class Notifier
this.notifyIconPath = RuneLite.RUNELITE_DIR.toPath().resolve("icon.png");
// First check if we are running in launcher
this.terminalNotifierAvailable =
!Strings.isNullOrEmpty(RuneLiteProperties.getLauncherVersion())
&& isTerminalNotifierAvailable();
this.terminalNotifierAvailable = true;
storeIcon();
}
@@ -328,7 +325,7 @@ public class Notifier
{
if (OSType.getOSType() == OSType.Linux && !Files.exists(notifyIconPath))
{
try (InputStream stream = Notifier.class.getResourceAsStream("/runelite.png"))
try (InputStream stream = Notifier.class.getResourceAsStream("/runeliteplus.png"))
{
Files.copy(stream, notifyIconPath);
}

View File

@@ -33,11 +33,12 @@ 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 javax.swing.SwingUtilities;
import joptsimple.ArgumentAcceptingOptionSpec;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
@@ -50,27 +51,25 @@ 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.PluginManager;
import net.runelite.client.rs.ClientLoader;
import net.runelite.client.rs.ClientUpdateCheckMode;
import net.runelite.client.task.Scheduler;
import net.runelite.client.ui.ClientUI;
import net.runelite.client.ui.DrawManager;
import net.runelite.client.ui.FatalErrorDialog;
import net.runelite.client.ui.SplashScreen;
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.infobox.InfoBoxManager;
import net.runelite.client.ui.overlay.arrow.ArrowMinimapOverlay;
import net.runelite.client.ui.overlay.arrow.ArrowWorldOverlay;
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
@@ -79,8 +78,11 @@ public class RuneLite
{
public static final File RUNELITE_DIR = new File(System.getProperty("user.home"), ".runelite");
public static final File PROFILES_DIR = new File(RUNELITE_DIR, "profiles");
public static final File PLUGIN_DIR = new File(RUNELITE_DIR, "plugins");
public static final File SCREENSHOT_DIR = new File(RUNELITE_DIR, "screenshots");
public static final File LOGS_DIR = new File(RUNELITE_DIR, "logs");
public static boolean allowPrivateServer = false;
public static final Locale SYSTEM_LOCALE = Locale.getDefault();
@Getter
private static Injector injector;
@@ -88,20 +90,14 @@ public class RuneLite
@Inject
private PluginManager pluginManager;
@Inject
private EventBus eventBus;
@Inject
private ConfigManager configManager;
@Inject
private DrawManager drawManager;
@Inject
private SessionManager sessionManager;
@Inject
private DiscordService discordService;
public DiscordService discordService;
@Inject
private ClientSessionManager clientSessionManager;
@@ -109,15 +105,9 @@ public class RuneLite
@Inject
private ClientUI clientUI;
@Inject
private InfoBoxManager infoBoxManager;
@Inject
private OverlayManager overlayManager;
@Inject
private Provider<PartyService> partyService;
@Inject
private Provider<ItemManager> itemManager;
@@ -145,6 +135,12 @@ public class RuneLite
@Inject
private Provider<WorldMapOverlay> worldMapOverlay;
@Inject
private Provider<ArrowWorldOverlay> arrowWorldOverlay;
@Inject
private Provider<ArrowMinimapOverlay> arrowMinimapOverlay;
@Inject
private Provider<LootManager> lootManager;
@@ -155,6 +151,12 @@ public class RuneLite
@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);
@@ -162,6 +164,10 @@ public class RuneLite
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")
@@ -180,12 +186,63 @@ public class RuneLite
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 = options.has("developer-mode");
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`");
}
}
if (!options.has("no-splash"))
{
RuneLiteSplashScreen.init();
}
RuneLiteSplashScreen.stage(0, "Initializing client");
PROFILES_DIR.mkdirs();
if (options.has("debug"))
{
final Logger logger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
@@ -197,65 +254,24 @@ public class RuneLite
log.error("Uncaught exception:", throwable);
if (throwable instanceof AbstractMethodError)
{
log.error("Classes are out of date; Build with maven again.");
log.error("Classes are out of date; Build with Gradle again.");
}
});
SplashScreen.init();
SplashScreen.stage(0, "Retrieving client", "");
try
{
final ClientLoader clientLoader = new ClientLoader(options.valueOf(updateMode));
RuneLiteSplashScreen.stage(.2, "Starting RuneLitePlus injector");
new Thread(() ->
{
clientLoader.get();
ClassPreloader.preload();
}, "Preloader").start();
final long start = System.currentTimeMillis();
final boolean developerMode = options.has("developer-mode") && RuneLiteProperties.getLauncherVersion() == null;
injector = Guice.createInjector(new RuneLiteModule(
options.valueOf(updateMode),
true));
if (developerMode)
{
boolean assertions = false;
assert assertions = true;
if (!assertions)
{
SwingUtilities.invokeLater(() ->
new FatalErrorDialog("Developers should enable assertions; Add `-ea` to your JVM arguments`")
.addBuildingGuide()
.open());
return;
}
}
PROFILES_DIR.mkdirs();
final long start = System.currentTimeMillis();
injector = Guice.createInjector(new RuneLiteModule(
clientLoader,
developerMode));
injector.getInstance(RuneLite.class).start();
final long end = System.currentTimeMillis();
final RuntimeMXBean rb = ManagementFactory.getRuntimeMXBean();
final long uptime = rb.getUptime();
log.info("Client initialization took {}ms. Uptime: {}ms", end - start, uptime);
}
catch (Exception e)
{
log.warn("Failure during startup", e);
SwingUtilities.invokeLater(() ->
new FatalErrorDialog("RuneLite has encountered an unexpected error during startup.")
.open());
}
finally
{
SplashScreen.stop();
}
injector.getInstance(RuneLite.class).start();
final long end = System.currentTimeMillis();
final RuntimeMXBean rb = ManagementFactory.getRuntimeMXBean();
final long uptime = rb.getUptime();
log.info("Client initialization took {}ms. Uptime: {}ms", end - start, uptime);
}
public void start() throws Exception
@@ -269,13 +285,17 @@ public class RuneLite
injector.injectMembers(client);
}
SplashScreen.stage(.57, null, "Loading configuration");
// Load user configuration
RuneLiteSplashScreen.stage(.57, "Loading user config");
configManager.load();
// Load the session, including saved configuration
sessionManager.loadSession();
RuneLiteSplashScreen.stage(.58, "Loading session data");
// Begin watching for new plugins
pluginManager.watch();
// Tell the plugin manager if client is outdated or not
pluginManager.setOutdated(isOutdated);
@@ -283,57 +303,55 @@ public class RuneLite
// Load the plugins, but does not start them yet.
// This will initialize configuration
pluginManager.loadCorePlugins();
SplashScreen.stage(.70, null, "Finalizing configuration");
RuneLiteSplashScreen.stage(.70, "Finalizing configuration");
// Plugins have provided their config, so set default config
// to main settings
pluginManager.loadDefaultPluginConfiguration();
// Start client session
RuneLiteSplashScreen.stage(.80, "Starting core interface");
clientSessionManager.start();
SplashScreen.stage(.75, null, "Starting core interface");
// Initialize UI
clientUI.init(this);
// Initialize Discord service
discordService.init();
// Register event listeners
eventBus.register(clientUI);
eventBus.register(pluginManager);
eventBus.register(overlayManager);
eventBus.register(drawManager);
eventBus.register(infoBoxManager);
if (!isOutdated)
{
// Initialize chat colors
chatMessageManager.get().loadColors();
eventBus.register(partyService.get());
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());
overlayRenderer.get();
clanManager.get();
itemManager.get();
menuManager.get();
chatMessageManager.get();
commandManager.get();
lootManager.get();
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();
SplashScreen.stop();
discordService.init();
// Register additional schedulers
if (this.client != null)
{
scheduler.registerObject(modelOutlineRenderer.get());
}
// Close the splash screen
RuneLiteSplashScreen.close();
clientUI.show();
}
@@ -350,4 +368,4 @@ public class RuneLite
{
RuneLite.injector = injector;
}
}
}

View File

@@ -30,7 +30,6 @@ import com.google.inject.name.Names;
import java.applet.Applet;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import javax.inject.Singleton;
import net.runelite.api.Client;
@@ -41,10 +40,13 @@ 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.config.RuneLitePlusConfig;
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;
@@ -55,18 +57,19 @@ import org.slf4j.LoggerFactory;
public class RuneLiteModule extends AbstractModule
{
private final Supplier<Applet> clientLoader;
private final ClientUpdateCheckMode updateCheckMode;
private final boolean developerMode;
public RuneLiteModule(Supplier<Applet> clientLoader, boolean developerMode)
public RuneLiteModule(final ClientUpdateCheckMode updateCheckMode, final boolean developerMode)
{
this.clientLoader = clientLoader;
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);
@@ -75,6 +78,7 @@ public class RuneLiteModule extends AbstractModule
bind(ItemManager.class);
bind(Scheduler.class);
bind(PluginManager.class);
bind(RuneLiteProperties.class);
bind(SessionManager.class);
bind(Callbacks.class).to(Hooks.class);
@@ -93,9 +97,9 @@ public class RuneLiteModule extends AbstractModule
@Provides
@Singleton
Applet provideApplet()
Applet provideApplet(ClientLoader clientLoader)
{
return clientLoader.get();
return clientLoader.load();
}
@Provides
@@ -112,6 +116,13 @@ public class RuneLiteModule extends AbstractModule
return configManager.getConfig(RuneLiteConfig.class);
}
@Provides
@Singleton
RuneLitePlusConfig providePlusConfig(ConfigManager configManager)
{
return configManager.getConfig(RuneLitePlusConfig.class);
}
@Provides
@Singleton
ChatColorConfig provideChatColorConfig(ConfigManager configManager)

View File

@@ -24,17 +24,20 @@
*/
package net.runelite.client;
import com.google.inject.Singleton;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import javax.annotation.Nullable;
@Singleton
public class RuneLiteProperties
{
private static final String RUNELITE_TITLE = "runelite.title";
private static final String RUNELITE_TITLE = "runelite.plus.title";
private static final String RUNELITE_VERSION = "runelite.version";
private static final String RUNELITE_PLUS_VERSION = "runelite.plus.version";
private static final String RUNELITE_PLUS_DATE = "runelite.plus.builddate";
private static final String RUNESCAPE_VERSION = "runescape.version";
private static final String DISCORD_APP_ID = "runelite.discord.appid";
private static final String DISCORD_APP_ID = "runelite.plus.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";
@@ -48,7 +51,7 @@ public class RuneLiteProperties
static
{
try (InputStream in = RuneLiteProperties.class.getResourceAsStream("runelite.properties"))
try (InputStream in = RuneLiteProperties.class.getResourceAsStream("/runelite.plus.properties"))
{
properties.load(in);
}
@@ -60,7 +63,13 @@ public class RuneLiteProperties
public static String getTitle()
{
return properties.getProperty(RUNELITE_TITLE);
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 static String getVersion()
@@ -68,6 +77,16 @@ public class RuneLiteProperties
return properties.getProperty(RUNELITE_VERSION);
}
public static String getPlusVersion()
{
return properties.getProperty(RUNELITE_PLUS_VERSION);
}
public static String getPlusDate()
{
return properties.getProperty(RUNELITE_PLUS_DATE);
}
public static String getRunescapeVersion()
{
return properties.getProperty(RUNESCAPE_VERSION);
@@ -98,12 +117,6 @@ public class RuneLiteProperties
return properties.getProperty(PATREON_LINK);
}
@Nullable
public static String getLauncherVersion()
{
return System.getProperty(LAUNCHER_VERSION_PROPERTY);
}
public static String getTroubleshootingLink()
{
return properties.getProperty(TROUBLESHOOTING_LINK);

View File

@@ -1,95 +1,95 @@
/*
* 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.getSessionBase().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.getSessionBase().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.getSessionBase().newBuilder()
.addQueryParameter("session", uuid.toString())
.build();
Request request = new Request.Builder()
.delete()
.url(url)
.build();
RuneLiteAPI.CLIENT.newCall(request).execute().close();
}
}
/*
* 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

@@ -36,12 +36,11 @@ import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.client.events.SessionClose;
import net.runelite.client.events.SessionOpen;
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;
@@ -67,7 +66,8 @@ public class SessionManager
this.configManager = configManager;
this.eventBus = eventBus;
this.wsClient = wsClient;
eventBus.register(this);
this.eventBus.subscribe(LoginResponse.class, this, this::onLoginResponse);
}
public void loadSession()
@@ -147,10 +147,10 @@ public class SessionManager
{
// Initialize config for new session
// If the session isn't logged in yet, don't switch to the new config
configManager.switchSession(session);
configManager.switchSession();
}
eventBus.post(new SessionOpen());
eventBus.post(SessionOpen.class, new SessionOpen());
}
private void closeSession()
@@ -177,9 +177,9 @@ public class SessionManager
accountSession = null; // No more account
// Restore config
configManager.switchSession(null);
configManager.switchSession();
eventBus.post(new SessionClose());
eventBus.post(SessionClose.class, new SessionClose());
}
public void login()
@@ -207,8 +207,7 @@ public class SessionManager
LinkBrowser.browse(login.getOauthUrl());
}
@Subscribe
public void onLoginResponse(LoginResponse loginResponse)
private void onLoginResponse(LoginResponse loginResponse)
{
log.debug("Now logged in as {}", loginResponse.getUsername());

View File

@@ -27,16 +27,18 @@ package net.runelite.client.callback;
import com.google.inject.Inject;
import java.util.Iterator;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
import java.util.function.BooleanSupplier;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import org.jetbrains.annotations.NotNull;
@Singleton
@Slf4j
public class ClientThread
public class ClientThread implements Executor
{
private ConcurrentLinkedQueue<BooleanSupplier> invokes = new ConcurrentLinkedQueue<>();
private final ConcurrentLinkedQueue<BooleanSupplier> invokes = new ConcurrentLinkedQueue<>();
@Inject
private Client client;
@@ -112,4 +114,14 @@ public class ClientThread
}
}
}
@Override
public void execute(@NotNull Runnable r)
{
invoke(() ->
{
r.run();
return true;
});
}
}

View File

@@ -43,13 +43,14 @@ import lombok.extern.slf4j.Slf4j;
import net.runelite.api.BufferProvider;
import net.runelite.api.Client;
import net.runelite.api.Constants;
import net.runelite.api.Entity;
import net.runelite.api.MainBufferProvider;
import net.runelite.api.NullItemID;
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.Event;
import net.runelite.api.events.GameTick;
import net.runelite.api.hooks.Callbacks;
import net.runelite.api.hooks.DrawCallbacks;
@@ -87,9 +88,6 @@ public class Hooks implements Callbacks
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;
@@ -156,15 +154,15 @@ public class Hooks implements Callbacks
}
@Override
public void post(Object event)
public <T> void post(Class<T> eventClass, Event event)
{
eventBus.post(event);
eventBus.post(eventClass, event);
}
@Override
public void postDeferred(Object event)
public <T> void postDeferred(Class<T> eventClass, Event event)
{
deferredEventBus.post(event);
deferredEventBus.post(eventClass, event);
}
@Override
@@ -176,13 +174,13 @@ public class Hooks implements Callbacks
deferredEventBus.replay();
eventBus.post(GAME_TICK);
eventBus.post(GameTick.class, GameTick.INSTANCE);
int tick = client.getTickCount();
client.setTickCount(tick + 1);
}
eventBus.post(BEFORE_RENDER);
eventBus.post(BeforeRender.class, BeforeRender.INSTANCE);
clientThread.invoke();
@@ -397,8 +395,6 @@ public class Hooks implements Callbacks
/**
* Copy an image
* @param src
* @return
*/
private static Image copy(Image src)
{
@@ -477,16 +473,16 @@ public class Hooks implements Callbacks
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)
public static void renderDraw(Entity entity, int orientation, int pitchSin, int pitchCos, int yawSin, int yawCos, int x, int y, int z, long hash)
{
DrawCallbacks drawCallbacks = client.getDrawCallbacks();
if (drawCallbacks != null)
{
drawCallbacks.draw(renderable, orientation, pitchSin, pitchCos, yawSin, yawCos, x, y, z, hash);
drawCallbacks.draw(entity, orientation, pitchSin, pitchCos, yawSin, yawCos, x, y, z, hash);
}
else
{
renderable.draw(orientation, pitchSin, pitchCos, yawSin, yawCos, x, y, z, hash);
entity.draw(orientation, pitchSin, pitchCos, yawSin, yawCos, x, y, z, hash);
}
}
@@ -522,7 +518,7 @@ public class Hooks implements Callbacks
public static boolean drawMenu()
{
BeforeMenuRender event = new BeforeMenuRender();
client.getCallbacks().post(event);
client.getCallbacks().post(BeforeMenuRender.class, event);
return event.isConsumed();
}
}

View File

@@ -35,7 +35,6 @@ 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;
@@ -53,8 +52,10 @@ public class ChatCommandManager implements ChatboxInputListener
{
this.client = client;
this.scheduledExecutorService = scheduledExecutorService;
eventBus.register(this);
// eventBus.register(this);
commandManager.register(this);
eventBus.subscribe(ChatMessage.class, this, this::onChatMessage);
}
public void registerCommand(String command, BiConsumer<ChatMessage, String> execute)
@@ -82,8 +83,7 @@ public class ChatCommandManager implements ChatboxInputListener
commands.remove(command.toLowerCase());
}
@Subscribe
public void onChatMessage(ChatMessage chatMessage)
private void onChatMessage(ChatMessage chatMessage)
{
if (client.getGameState() != GameState.LOGGED_IN)
{

View File

@@ -52,7 +52,7 @@ 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.eventbus.EventBus;
import net.runelite.client.ui.JagexColors;
import net.runelite.client.util.ColorUtil;
@@ -70,17 +70,23 @@ public class ChatMessageManager
@Inject
private ChatMessageManager(
Client client,
ChatColorConfig chatColorConfig,
ClientThread clientThread)
final Client client,
final ChatColorConfig chatColorConfig,
final ClientThread clientThread,
final EventBus eventbus)
{
this.client = client;
this.chatColorConfig = chatColorConfig;
this.clientThread = clientThread;
eventbus.subscribe(VarbitChanged.class, this, this::onVarbitChanged);
eventbus.subscribe(ResizeableChanged.class, this, this::onResizeableChanged);
eventbus.subscribe(ConfigChanged.class, this, this::onConfigChanged);
eventbus.subscribe(ChatMessage.class, this, this::onChatMessage);
eventbus.subscribe(ScriptCallbackEvent.class, this, this::onScriptCallbackEvent);
}
@Subscribe
public void onVarbitChanged(VarbitChanged event)
private void onVarbitChanged(VarbitChanged event)
{
int setting = client.getVar(Varbits.TRANSPARENT_CHATBOX);
@@ -91,14 +97,12 @@ public class ChatMessageManager
}
}
@Subscribe
public void onResizeableChanged(ResizeableChanged event)
private void onResizeableChanged(ResizeableChanged event)
{
refreshAll();
}
@Subscribe
public void onConfigChanged(ConfigChanged event)
private void onConfigChanged(ConfigChanged event)
{
if (event.getGroup().equals("textrecolor"))
{
@@ -107,15 +111,14 @@ public class ChatMessageManager
}
}
@Subscribe
public void onChatMessage(ChatMessage chatMessage)
private 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;
Color senderColor;
switch (chatMessageType)
{
@@ -173,8 +176,7 @@ public class ChatMessageManager
}
}
@Subscribe
public void onScriptCallbackEvent(ScriptCallbackEvent scriptCallbackEvent)
private void onScriptCallbackEvent(ScriptCallbackEvent scriptCallbackEvent)
{
final String eventName = scriptCallbackEvent.getEventName();
@@ -568,6 +570,12 @@ public class ChatMessageManager
return;
}
//guard case for google MoreObjects#firstNonNull
if (message.getValue() == null && message.getRuneLiteFormattedMessage() == null)
{
return;
}
// this updates chat cycle
client.addChatMessage(
message.getType(),

View File

@@ -1,35 +1,35 @@
/*
* 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);
}
/*
* 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

@@ -38,7 +38,6 @@ 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;
@@ -58,11 +57,17 @@ public class CommandManager
private final List<ChatboxInputListener> chatboxInputListenerList = new ArrayList<>();
@Inject
private CommandManager(Client client, EventBus eventBus, ClientThread clientThread)
private CommandManager(
final Client client,
final EventBus eventBus,
final ClientThread clientThread
)
{
this.client = client;
this.eventBus = eventBus;
this.clientThread = clientThread;
eventBus.subscribe(ScriptCallbackEvent.class, this, this::onScriptCallbackEvent);
}
public void register(ChatboxInputListener chatboxInputListener)
@@ -75,7 +80,6 @@ public class CommandManager
chatboxInputListenerList.remove(chatboxInputListener);
}
@Subscribe
private void onScriptCallbackEvent(ScriptCallbackEvent event)
{
if (sending)
@@ -115,7 +119,7 @@ public class CommandManager
String[] args = Arrays.copyOfRange(split, 1, split.length);
CommandExecuted commandExecuted = new CommandExecuted(command, args);
eventBus.post(commandExecuted);
eventBus.post(CommandExecuted.class, commandExecuted);
}
private void handleInput(ScriptCallbackEvent event)

View File

@@ -24,17 +24,18 @@
*/
package net.runelite.client.config;
import java.util.ArrayList;
import java.util.Collection;
public class ConfigDescriptor
{
private final ConfigGroup group;
private final Collection<ConfigItemDescriptor> items;
private final Collection<ConfigItemsGroup> itemGroups;
public ConfigDescriptor(ConfigGroup group, Collection<ConfigItemDescriptor> items)
public ConfigDescriptor(ConfigGroup group, Collection<ConfigItemsGroup> itemGroups)
{
this.group = group;
this.items = items;
this.itemGroups = itemGroups;
}
public ConfigGroup getGroup()
@@ -42,8 +43,19 @@ public class ConfigDescriptor
return group;
}
public Collection<ConfigItemsGroup> getItemGroups()
{
return itemGroups;
}
public Collection<ConfigItemDescriptor> getItems()
{
return items;
Collection<ConfigItemDescriptor> allItems = new ArrayList<>();
for (ConfigItemsGroup g : itemGroups)
{
allItems.addAll(g.getItems());
}
return allItems;
}
}
}

View File

@@ -24,56 +24,63 @@
*/
package net.runelite.client.config;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
@Slf4j
class ConfigInvocationHandler implements InvocationHandler
{
// Special object to represent null values in the cache
private static final Object NULL = new Object();
private final ConfigManager manager;
private final Cache<Method, Object> cache = CacheBuilder.newBuilder()
.maximumSize(128)
.build();
ConfigInvocationHandler(ConfigManager manager)
// Caches for annotation values
private static final Map<Class<?>, String> groupValueCache = new HashMap<>();
private static final Map<Method, String> methodKeyNameCache = new HashMap<>();
public ConfigInvocationHandler(ConfigManager manager)
{
this.manager = manager;
}
private static String groupValueFromProxy(Class<?> proxyClass)
{
Class<?> iface = proxyClass.getInterfaces()[0];
ConfigGroup group = iface.getAnnotation(ConfigGroup.class);
return group == null ? null : group.value();
}
private static String keyNameFromMethod(Method method)
{
ConfigItem item = method.getAnnotation(ConfigItem.class);
return item == null ? null : item.keyName();
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
// Use cached configuration value if available
if (args == null)
String itemKeyName, groupValue;
try
{
Object cachedValue = cache.getIfPresent(method);
if (cachedValue != null)
{
return cachedValue == NULL ? null : cachedValue;
}
groupValue = groupValueCache.computeIfAbsent(proxy.getClass(), ConfigInvocationHandler::groupValueFromProxy);
}
Class<?> iface = proxy.getClass().getInterfaces()[0];
ConfigGroup group = iface.getAnnotation(ConfigGroup.class);
ConfigItem item = method.getAnnotation(ConfigItem.class);
if (group == null)
catch (NullPointerException e)
{
log.warn("Configuration proxy class {} has no @ConfigGroup!", proxy.getClass());
return null;
}
if (item == null)
try
{
itemKeyName = methodKeyNameCache.computeIfAbsent(method, ConfigInvocationHandler::keyNameFromMethod);
}
catch (NullPointerException e)
{
log.warn("Configuration method {} has no @ConfigItem!", method);
return null;
@@ -81,42 +88,44 @@ class ConfigInvocationHandler implements InvocationHandler
if (args == null)
{
log.trace("cache miss (size: {}, group: {}, key: {})", cache.size(), group.value(), item.keyName());
// Getting configuration item
String value = manager.getConfiguration(group.value(), item.keyName());
if (value == null)
return manager.getConfigObjectFromCacheOrElse(groupValue, itemKeyName, (value) ->
{
if (method.isDefault())
try
{
Object defaultValue = callDefaultMethod(proxy, method, null);
cache.put(method, defaultValue == null ? NULL : defaultValue);
return defaultValue;
value = manager.getConfiguration(value);
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 {}.{} ", groupValue, itemKeyName, e);
if (method.isDefault())
{
return callDefaultMethod(proxy, method, null);
}
return null;
}
}
cache.put(method, NULL);
return null;
}
// Convert value to return type
Class<?> returnType = method.getReturnType();
try
{
Object objectValue = ConfigManager.stringToObject(value, returnType);
cache.put(method, objectValue == null ? NULL : objectValue);
return objectValue;
}
catch (Exception e)
{
log.warn("Unable to unmarshal {}.{} ", group.value(), item.keyName(), e);
if (method.isDefault())
catch (Throwable throwable)
{
return callDefaultMethod(proxy, method, null);
log.error("Unable to resolve configuration value {}.{}", groupValue, itemKeyName, throwable);
return null;
}
return null;
}
});
}
else
{
@@ -130,7 +139,7 @@ class ConfigInvocationHandler implements InvocationHandler
Object newValue = args[0];
Class<?> type = method.getParameterTypes()[0];
Object oldValue = manager.getConfiguration(group.value(), item.keyName(), type);
Object oldValue = manager.getConfiguration(groupValue, itemKeyName, type);
if (Objects.equals(oldValue, newValue))
{
@@ -145,19 +154,19 @@ class ConfigInvocationHandler implements InvocationHandler
if (Objects.equals(newValue, defaultValue))
{
// Just unset if it goes back to the default
manager.unsetConfiguration(group.value(), item.keyName());
manager.unsetConfiguration(groupValue, itemKeyName);
return null;
}
}
if (newValue == null)
{
manager.unsetConfiguration(group.value(), item.keyName());
manager.unsetConfiguration(groupValue, itemKeyName);
}
else
{
String newValueStr = ConfigManager.objectToString(newValue);
manager.setConfiguration(group.value(), item.keyName(), newValueStr);
manager.setConfiguration(groupValue, itemKeyName, newValueStr);
}
return null;
}
@@ -175,10 +184,4 @@ class ConfigInvocationHandler implements InvocationHandler
.bindTo(proxy)
.invokeWithArguments(args);
}
void invalidate()
{
log.trace("cache invalidate");
cache.invalidateAll();
}
}

View File

@@ -46,4 +46,29 @@ public @interface ConfigItem
String warning() default "";
boolean secret() default false;
}
String group() default "";
String unhide() default "";
String unhideValue() default "";
String hide() default "";
String hideValue() default "";
String parent() default "";
String enabledBy() default "";
String enabledByValue() default "";
String disabledBy() default "";
String disabledByValue() default "";
boolean parse() default false;
Class<?> clazz() default void.class;
String method() default "";
}

View File

@@ -0,0 +1,52 @@
/*
* 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

@@ -42,13 +42,12 @@ 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.text.DateFormat;
import java.text.SimpleDateFormat;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -56,6 +55,7 @@ import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -63,146 +63,42 @@ import lombok.extern.slf4j.Slf4j;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.ConfigChanged;
import net.runelite.client.RuneLite;
import net.runelite.client.account.AccountSession;
import static net.runelite.client.RuneLite.PROFILES_DIR;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.util.ColorUtil;
import net.runelite.http.api.config.ConfigClient;
import net.runelite.http.api.config.ConfigEntry;
import net.runelite.http.api.config.Configuration;
@Singleton
@Slf4j
public class ConfigManager
{
private static final String SETTINGS_FILE_NAME = "settings.properties";
private static final DateFormat TIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
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 AccountSession session;
private ConfigClient client;
private File propertiesFile;
private final ConfigInvocationHandler handler = new ConfigInvocationHandler(this);
private final Properties properties = new Properties();
private final Map<String, Object> configObjectCache = new HashMap<>();
private final Map<String, String> pendingChanges = new HashMap<>();
@Inject
public ConfigManager(ScheduledExecutorService scheduledExecutorService)
{
this.executor = scheduledExecutorService;
this.propertiesFile = getPropertiesFile();
executor.scheduleWithFixedDelay(this::sendConfig, 30, 30, TimeUnit.SECONDS);
scheduledExecutorService.scheduleWithFixedDelay(this::sendConfig, 30, 30, TimeUnit.SECONDS);
}
public final void switchSession(AccountSession session)
public final void switchSession()
{
// Ensure existing config is saved
sendConfig();
if (session == null)
{
this.session = null;
this.client = null;
}
else
{
this.session = session;
this.client = new ConfigClient(session.getUuid());
}
this.propertiesFile = getPropertiesFile();
load(); // load profile specific config
}
private File getLocalPropertiesFile()
{
return new File(RuneLite.RUNELITE_DIR, SETTINGS_FILE_NAME);
}
private File getPropertiesFile()
{
// Sessions that aren't logged in have no username
if (session == null || session.getUsername() == null)
{
return getLocalPropertiesFile();
}
else
{
File profileDir = new File(RuneLite.PROFILES_DIR, session.getUsername().toLowerCase());
return new File(profileDir, SETTINGS_FILE_NAME);
}
load();
}
public void load()
{
if (client == null)
{
loadFromFile();
return;
}
Configuration configuration;
try
{
configuration = client.get();
}
catch (IOException ex)
{
log.debug("Unable to load configuration from client, using saved configuration from disk", ex);
loadFromFile();
return;
}
if (configuration.getConfig() == null || configuration.getConfig().isEmpty())
{
log.debug("No configuration from client, using saved configuration on disk");
loadFromFile();
return;
}
handler.invalidate();
properties.clear();
for (ConfigEntry entry : configuration.getConfig())
{
log.debug("Loading configuration value from client {}: {}", entry.getKey(), entry.getValue());
final String[] split = entry.getKey().split("\\.", 2);
if (split.length != 2)
{
continue;
}
final String groupName = split[0];
final String key = split[1];
final String value = entry.getValue();
final String oldValue = (String) properties.setProperty(entry.getKey(), value);
ConfigChanged configChanged = new ConfigChanged();
configChanged.setGroup(groupName);
configChanged.setKey(key);
configChanged.setOldValue(oldValue);
configChanged.setNewValue(value);
eventBus.post(configChanged);
}
try
{
saveToFile(propertiesFile);
log.debug("Updated configuration on disk with the latest version");
}
catch (IOException ex)
{
log.warn("Unable to update configuration on disk", ex);
}
loadFromFile();
}
private synchronized void syncPropertiesFromFile(File propertiesFile)
@@ -210,7 +106,7 @@ public class ConfigManager
final Properties properties = new Properties();
try (FileInputStream in = new FileInputStream(propertiesFile))
{
properties.load(new InputStreamReader(in, Charset.forName("UTF-8")));
properties.load(new InputStreamReader(in, StandardCharsets.UTF_8));
}
catch (Exception e)
{
@@ -218,7 +114,7 @@ public class ConfigManager
return;
}
final Map<String, String> copy = (Map) ImmutableMap.copyOf(this.properties);
@SuppressWarnings("unchecked") final Map<String, String> copy = (Map) ImmutableMap.copyOf(this.properties);
copy.forEach((groupAndKey, value) ->
{
if (!properties.containsKey(groupAndKey))
@@ -253,39 +149,21 @@ public class ConfigManager
public void importLocal()
{
if (session == null)
{
// No session, no import
return;
}
final File file = new File(propertiesFile.getParent(), propertiesFile.getName() + "." + TIME_FORMAT.format(new Date()));
try
{
saveToFile(file);
}
catch (IOException e)
{
log.warn("Backup failed, skipping import", e);
return;
}
syncPropertiesFromFile(getLocalPropertiesFile());
log.info("Nothing changed, don't worry!");
}
private synchronized void loadFromFile()
{
handler.invalidate();
properties.clear();
try (FileInputStream in = new FileInputStream(propertiesFile))
try (FileInputStream in = new FileInputStream(SETTINGS_FILE))
{
properties.load(new InputStreamReader(in, Charset.forName("UTF-8")));
properties.load(new InputStreamReader(in, StandardCharsets.UTF_8));
}
catch (FileNotFoundException ex)
{
log.debug("Unable to load settings - no such file");
log.debug("Unable to load settings - no such file, syncing from standard settings");
syncLastModified();
}
catch (IllegalArgumentException | IOException ex)
{
@@ -294,7 +172,7 @@ public class ConfigManager
try
{
Map<String, String> copy = (Map) ImmutableMap.copyOf(properties);
@SuppressWarnings("unchecked") Map<String, String> copy = (Map) ImmutableMap.copyOf(properties);
copy.forEach((groupAndKey, value) ->
{
final String[] split = groupAndKey.split("\\.", 2);
@@ -313,7 +191,7 @@ public class ConfigManager
configChanged.setKey(key);
configChanged.setOldValue(null);
configChanged.setNewValue(value);
eventBus.post(configChanged);
eventBus.post(ConfigChanged.class, configChanged);
});
}
catch (Exception ex)
@@ -322,17 +200,17 @@ public class ConfigManager
}
}
private void saveToFile(final File propertiesFile) throws IOException
private void saveToFile() throws IOException
{
propertiesFile.getParentFile().mkdirs();
ConfigManager.SETTINGS_FILE.getParentFile().mkdirs();
try (FileOutputStream out = new FileOutputStream(propertiesFile))
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");
properties.store(new OutputStreamWriter(out, StandardCharsets.UTF_8), "RuneLite configuration");
}
finally
{
@@ -341,6 +219,21 @@ public class ConfigManager
}
}
// Attempts to fetch the config value from the cache if present. Otherwise it calls the get value function and caches the result
Object getConfigObjectFromCacheOrElse(String groupName, String key, Function<String, Object> getValue)
{
String configItemKey = groupName + "." + key;
return configObjectCache.computeIfAbsent(configItemKey, getValue);
}
// Posts the configchanged event to the event bus and remove the changed key from the cache
private void postConfigChanged(ConfigChanged configChanged)
{
configObjectCache.remove(configChanged.getGroup() + "." + configChanged.getKey());
eventBus.post(ConfigChanged.class, configChanged);
}
@SuppressWarnings("unchecked")
public <T> T getConfig(Class<T> clazz)
{
if (!Modifier.isPublic(clazz.getModifiers()))
@@ -348,12 +241,10 @@ public class ConfigManager
throw new RuntimeException("Non-public configuration classes can't have default methods invoked");
}
T t = (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[]
return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[]
{
clazz
}, handler);
return t;
}
public List<String> getConfigurationKeys(String prefix)
@@ -366,6 +257,12 @@ public class ConfigManager
return properties.getProperty(groupName + "." + key);
}
public String getConfiguration(String propertyKey)
{
return properties.getProperty(propertyKey);
}
@SuppressWarnings("unchecked")
public <T> T getConfiguration(String groupName, String key, Class<T> clazz)
{
String value = getConfiguration(groupName, key);
@@ -393,7 +290,6 @@ public class ConfigManager
}
log.debug("Setting configuration value for {}.{} to {}", groupName, key, value);
handler.invalidate();
synchronized (pendingChanges)
{
@@ -406,7 +302,7 @@ public class ConfigManager
configChanged.setOldValue(oldValue);
configChanged.setNewValue(value);
eventBus.post(configChanged);
postConfigChanged(configChanged);
}
public void setConfiguration(String groupName, String key, Object value)
@@ -424,7 +320,6 @@ public class ConfigManager
}
log.debug("Unsetting configuration value for {}.{}", groupName, key);
handler.invalidate();
synchronized (pendingChanges)
{
@@ -436,7 +331,7 @@ public class ConfigManager
configChanged.setKey(key);
configChanged.setOldValue(oldValue);
eventBus.post(configChanged);
eventBus.post(ConfigChanged.class, configChanged);
}
public ConfigDescriptor getConfigDescriptor(Object configurationProxy)
@@ -463,7 +358,35 @@ public class ConfigManager
.result())
.collect(Collectors.toList());
return new ConfigDescriptor(group, items);
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);
}
/**
@@ -541,6 +464,7 @@ public class ConfigManager
}
}
@SuppressWarnings("unchecked")
static Object stringToObject(String str, Class<?> type)
{
if (type == boolean.class || type == Boolean.class)
@@ -609,6 +533,22 @@ public class ConfigManager
{
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;
}
@@ -663,23 +603,6 @@ public class ConfigManager
boolean changed;
synchronized (pendingChanges)
{
if (client != null)
{
for (Map.Entry<String, String> entry : pendingChanges.entrySet())
{
String key = entry.getKey();
String value = entry.getValue();
if (Strings.isNullOrEmpty(value))
{
client.unset(key);
}
else
{
client.set(key, value);
}
}
}
changed = !pendingChanges.isEmpty();
pendingChanges.clear();
}
@@ -688,7 +611,7 @@ public class ConfigManager
{
try
{
saveToFile(propertiesFile);
saveToFile();
}
catch (IOException ex)
{
@@ -696,4 +619,44 @@ public class ConfigManager
}
}
}
}
private void syncLastModified()
{
File newestFile;
newestFile = STANDARD_SETTINGS_FILE;
File[] profileDirFiles = PROFILES_DIR.listFiles();
if (profileDirFiles != null)
{
for (File profileDir : profileDirFiles)
{
if (!profileDir.isDirectory())
{
continue;
}
File[] settingsFiles = profileDir.listFiles();
if (settingsFiles == null)
{
continue;
}
for (File settings : settingsFiles)
{
if (!settings.getName().equals(STANDARD_SETTINGS_FILE_NAME) ||
settings.lastModified() < newestFile.lastModified())
{
continue;
}
newestFile = settings;
}
}
}
syncPropertiesFromFile(newestFile);
}
}

View File

@@ -0,0 +1,91 @@
/*
* 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

@@ -24,11 +24,10 @@
*/
package net.runelite.client.config;
import lombok.RequiredArgsConstructor;
import lombok.Getter;
import net.runelite.client.ui.FontManager;
import java.awt.Font;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.runelite.client.ui.FontManager;
@Getter
@RequiredArgsConstructor

View File

@@ -60,7 +60,7 @@ public class Keybind
private final int keyCode;
private final int modifiers;
protected Keybind(int keyCode, int modifiers, boolean ignoreModifiers)
Keybind(int keyCode, int modifiers, boolean ignoreModifiers)
{
modifiers &= KEYBOARD_MODIFIER_MASK;
@@ -108,7 +108,7 @@ public class Keybind
return matches(e, false);
}
protected boolean matches(KeyEvent e, boolean ignoreModifiers)
boolean matches(KeyEvent e, boolean ignoreModifiers)
{
if (NOT_SET.equals(this))
{
@@ -177,7 +177,7 @@ public class Keybind
return mod;
}
public static String getModifiersExText(int modifiers)
private static String getModifiersExText(int modifiers)
{
StringBuilder buf = new StringBuilder();
if ((modifiers & InputEvent.META_DOWN_MASK) != 0)

View File

@@ -25,7 +25,9 @@
package net.runelite.client.config;
import java.awt.Dimension;
import java.awt.Font;
import net.runelite.api.Constants;
import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.ContainableFrame;
@ConfigGroup("runelite")
@@ -176,8 +178,8 @@ public interface RuneLiteConfig extends Config
}
@ConfigItem(
keyName = "flashNotification",
name = "Flash notification",
keyName = "notificationFlash",
name = "Enable flash notification",
description = "Flashes the game frame as a notification",
position = 24
)
@@ -197,6 +199,17 @@ public interface RuneLiteConfig extends Config
return false;
}
@ConfigItem(
keyName = "clientFont",
name = "Font",
description = "Configure what font is used for the client and runelite added overlays",
position = 29
)
default Font clientFont()
{
return FontManager.getRunescapeFont();
}
@ConfigItem(
keyName = "fontType",
name = "Dynamic Overlay Font",
@@ -273,4 +286,17 @@ public interface RuneLiteConfig extends Config
{
return 35;
}
}
@Range(max = 100, min = 0)
@ConfigItem(
keyName = "volume",
name = "Runelite Volume",
description = "Sets the volume of custom Runelite sounds (not the client sounds)",
position = 43
)
default int volume()
{
return 100;
}
}

View File

@@ -0,0 +1,90 @@
/*
*
* 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.config;
@ConfigGroup("runeliteplus")
public interface RuneLitePlusConfig extends Config
{
@ConfigItem(
keyName = "enableOpacity",
name = "Enable opacity",
description = "Enables opacity for the whole window.<br>NOTE: This only stays enabled if your pc supports this!",
position = 0
)
default boolean enableOpacity()
{
return false;
}
@Range(
min = 15,
max = 100
)
@ConfigItem(
keyName = "opacityPercentage",
name = "Opacity percentage",
description = "Changes the opacity of the window if opacity is enabled",
position = 1
)
default int opacityPercentage()
{
return 100;
}
@ConfigItem(
keyName = "keyboardPin",
name = "Keyboard bank pin",
description = "Enables you to type your bank pin",
position = 2
)
default boolean keyboardPin()
{
return false;
}
@ConfigItem(
keyName = "enablePlugins",
name = "Enable loading of external plugins",
description = "Enable loading of external plugins",
position = 3
)
default boolean enablePlugins()
{
return false;
}
@ConfigItem(
keyName = "detachHotkey",
name = "Detach Cam",
description = "Detach Camera hotkey, press this and it will activate detatched camera.",
position = 4
)
default Keybind detachHotkey()
{
return Keybind.NOT_SET;
}
}

View File

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

View File

@@ -0,0 +1,87 @@
package net.runelite.client.database;
import java.io.File;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import static net.runelite.client.RuneLite.RUNELITE_DIR;
import org.h2.jdbcx.JdbcDataSource;
import org.jooq.DSLContext;
import org.jooq.SQLDialect;
import org.jooq.impl.DSL;
@Singleton
@Slf4j
public class DatabaseManager
{
private static final String DB_URL = "jdbc:h2:" + RUNELITE_DIR + File.separator + "RunelitePlus;AUTO_SERVER=TRUE";
// Database credentials
private static final String USER = "RLP";
private static final String PASS = "";
private Connection connection;
DatabaseManager()
{
System.getProperties().setProperty("org.jooq.no-logo", "true");
}
private void connect()
{
if (connection != null)
{
return;
}
JdbcDataSource ds = new JdbcDataSource();
ds.setURL(DatabaseManager.DB_URL);
ds.setUser(DatabaseManager.USER);
ds.setPassword(DatabaseManager.PASS);
try
{
connection = ds.getConnection();
}
catch (SQLException e)
{
e.printStackTrace();
}
}
Connection getConnection()
{
connect();
return connection;
}
public DSLContext getDsl()
{
return DSL.using(connection, SQLDialect.H2);
}
public boolean checkTableExists(String table)
{
boolean tableExists = false;
connect();
try
{
ResultSet rset = connection.getMetaData().getTables(null, null, table.toUpperCase(), null);
if (rset.next())
{
tableExists = true;
}
}
catch (SQLException e)
{
e.printStackTrace();
}
return tableExists;
}
}

View File

@@ -0,0 +1,42 @@
package net.runelite.client.database;
import java.sql.Connection;
import org.jooq.codegen.GenerationTool;
import org.jooq.meta.h2.H2Database;
import org.jooq.meta.jaxb.Configuration;
import org.jooq.meta.jaxb.Database;
import org.jooq.meta.jaxb.Generator;
import org.jooq.meta.jaxb.Target;
public class GenerateClasses
{
public static void main(String... args)
{
DatabaseManager databaseManager = new DatabaseManager();
try (Connection c = databaseManager.getConnection())
{
Configuration configuration = new Configuration()
.withGenerator(new Generator()
.withDatabase(new Database()
.withName(H2Database.class.getCanonicalName())
.withIncludes(".*")
.withExcludes("")
.withInputSchema("PUBLIC")
)
.withTarget(new Target()
.withPackageName("net.runelite.client.database.data")
.withDirectory("runelite-client/src/main/java/net/runelite/client/database/data")
)
);
GenerationTool tool = new GenerationTool();
tool.setConnection(c);
tool.run(configuration);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,62 @@
/*
* This file is generated by jOOQ.
*/
package net.runelite.client.database.data;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.annotation.Generated;
import org.jooq.Schema;
import org.jooq.impl.CatalogImpl;
/**
* This class is generated by jOOQ.
*/
@Generated(
value = {
"http://www.jooq.org",
"jOOQ version:3.11.12"
},
comments = "This class is generated by jOOQ"
)
@SuppressWarnings({"all", "unchecked", "rawtypes"})
public class DefaultCatalog extends CatalogImpl
{
private static final long serialVersionUID = 836257769;
/**
* The reference instance of <code></code>
*/
public static final DefaultCatalog DEFAULT_CATALOG = new DefaultCatalog();
/**
* The schema <code>PUBLIC</code>.
*/
public final Public PUBLIC = net.runelite.client.database.data.Public.PUBLIC;
/**
* No further instances allowed
*/
private DefaultCatalog()
{
super("");
}
@Override
public final List<Schema> getSchemas()
{
List result = new ArrayList();
result.addAll(getSchemas0());
return result;
}
private final List<Schema> getSchemas0()
{
return Arrays.<Schema>asList(
Public.PUBLIC);
}
}

View File

@@ -0,0 +1,55 @@
/*
* This file is generated by jOOQ.
*/
package net.runelite.client.database.data;
import javax.annotation.Generated;
import net.runelite.client.database.data.tables.Loottrackerevents;
import net.runelite.client.database.data.tables.Loottrackerlink;
import net.runelite.client.database.data.tables.Loottrackerloot;
import net.runelite.client.database.data.tables.User;
import org.jooq.Index;
import org.jooq.OrderField;
import org.jooq.impl.Internal;
/**
* A class modelling indexes of tables of the <code>PUBLIC</code> schema.
*/
@Generated(
value = {
"http://www.jooq.org",
"jOOQ version:3.11.12"
},
comments = "This class is generated by jOOQ"
)
@SuppressWarnings({"all", "unchecked", "rawtypes"})
public class Indexes
{
// -------------------------------------------------------------------------
// INDEX definitions
// -------------------------------------------------------------------------
public static final Index PRIMARY_KEY_B = Indexes0.PRIMARY_KEY_B;
public static final Index FK_LOOTTRACKEREVENT_INDEX_6 = Indexes0.FK_LOOTTRACKEREVENT_INDEX_6;
public static final Index FK_LOOTTRACKERLOOT_INDEX_6 = Indexes0.FK_LOOTTRACKERLOOT_INDEX_6;
public static final Index PRIMARY_KEY_6B = Indexes0.PRIMARY_KEY_6B;
public static final Index PRIMARY_KEY_6 = Indexes0.PRIMARY_KEY_6;
public static final Index PRIMARY_KEY_2 = Indexes0.PRIMARY_KEY_2;
// -------------------------------------------------------------------------
// [#1459] distribute members to avoid static initialisers > 64kb
// -------------------------------------------------------------------------
private static class Indexes0
{
public static Index PRIMARY_KEY_B = Internal.createIndex("PRIMARY_KEY_B", Loottrackerevents.LOOTTRACKEREVENTS, new OrderField[]{Loottrackerevents.LOOTTRACKEREVENTS.UNIQUEID}, true);
public static Index FK_LOOTTRACKEREVENT_INDEX_6 = Internal.createIndex("FK_LOOTTRACKEREVENT_INDEX_6", Loottrackerlink.LOOTTRACKERLINK, new OrderField[]{Loottrackerlink.LOOTTRACKERLINK.EVENTUNIQUEID}, false);
public static Index FK_LOOTTRACKERLOOT_INDEX_6 = Internal.createIndex("FK_LOOTTRACKERLOOT_INDEX_6", Loottrackerlink.LOOTTRACKERLINK, new OrderField[]{Loottrackerlink.LOOTTRACKERLINK.DROPUNIQUEID}, false);
public static Index PRIMARY_KEY_6B = Internal.createIndex("PRIMARY_KEY_6B", Loottrackerlink.LOOTTRACKERLINK, new OrderField[]{Loottrackerlink.LOOTTRACKERLINK.LINKUNIQUEID}, true);
public static Index PRIMARY_KEY_6 = Internal.createIndex("PRIMARY_KEY_6", Loottrackerloot.LOOTTRACKERLOOT, new OrderField[]{Loottrackerloot.LOOTTRACKERLOOT.UNIQUEID}, true);
public static Index PRIMARY_KEY_2 = Internal.createIndex("PRIMARY_KEY_2", User.USER, new OrderField[]{User.USER.UNIQUEID}, true);
}
}

View File

@@ -0,0 +1,76 @@
/*
* This file is generated by jOOQ.
*/
package net.runelite.client.database.data;
import javax.annotation.Generated;
import net.runelite.client.database.data.tables.Loottrackerevents;
import net.runelite.client.database.data.tables.Loottrackerlink;
import net.runelite.client.database.data.tables.Loottrackerloot;
import net.runelite.client.database.data.tables.User;
import net.runelite.client.database.data.tables.records.LoottrackereventsRecord;
import net.runelite.client.database.data.tables.records.LoottrackerlinkRecord;
import net.runelite.client.database.data.tables.records.LoottrackerlootRecord;
import net.runelite.client.database.data.tables.records.UserRecord;
import org.jooq.ForeignKey;
import org.jooq.UniqueKey;
import org.jooq.impl.Internal;
/**
* A class modelling foreign key relationships and constraints of tables of
* the <code>PUBLIC</code> schema.
*/
@Generated(
value = {
"http://www.jooq.org",
"jOOQ version:3.11.12"
},
comments = "This class is generated by jOOQ"
)
@SuppressWarnings({"all", "unchecked", "rawtypes"})
public class Keys
{
// -------------------------------------------------------------------------
// IDENTITY definitions
// -------------------------------------------------------------------------
// -------------------------------------------------------------------------
// UNIQUE and PRIMARY KEY definitions
// -------------------------------------------------------------------------
public static final UniqueKey<LoottrackereventsRecord> PK_EVENTUNIQUEID = UniqueKeys0.PK_EVENTUNIQUEID;
public static final UniqueKey<LoottrackerlinkRecord> PK_LOOTTRACKERLINK = UniqueKeys0.PK_LOOTTRACKERLINK;
public static final UniqueKey<LoottrackerlootRecord> PK_LOOTUNIQUEID = UniqueKeys0.PK_LOOTUNIQUEID;
public static final UniqueKey<UserRecord> PK_USER = UniqueKeys0.PK_USER;
// -------------------------------------------------------------------------
// FOREIGN KEY definitions
// -------------------------------------------------------------------------
public static final ForeignKey<LoottrackerlinkRecord, LoottrackereventsRecord> FK_LOOTTRACKEREVENT = ForeignKeys0.FK_LOOTTRACKEREVENT;
public static final ForeignKey<LoottrackerlinkRecord, LoottrackerlootRecord> FK_LOOTTRACKERLOOT = ForeignKeys0.FK_LOOTTRACKERLOOT;
public static final ForeignKey<LoottrackerlinkRecord, UserRecord> FK_USER = ForeignKeys0.FK_USER;
// -------------------------------------------------------------------------
// [#1459] distribute members to avoid static initialisers > 64kb
// -------------------------------------------------------------------------
private static class UniqueKeys0
{
public static final UniqueKey<LoottrackereventsRecord> PK_EVENTUNIQUEID = Internal.createUniqueKey(Loottrackerevents.LOOTTRACKEREVENTS, "PK_EVENTUNIQUEID", Loottrackerevents.LOOTTRACKEREVENTS.UNIQUEID);
public static final UniqueKey<LoottrackerlinkRecord> PK_LOOTTRACKERLINK = Internal.createUniqueKey(Loottrackerlink.LOOTTRACKERLINK, "PK_LOOTTRACKERLINK", Loottrackerlink.LOOTTRACKERLINK.LINKUNIQUEID);
public static final UniqueKey<LoottrackerlootRecord> PK_LOOTUNIQUEID = Internal.createUniqueKey(Loottrackerloot.LOOTTRACKERLOOT, "PK_LOOTUNIQUEID", Loottrackerloot.LOOTTRACKERLOOT.UNIQUEID);
public static final UniqueKey<UserRecord> PK_USER = Internal.createUniqueKey(User.USER, "PK_USER", User.USER.UNIQUEID);
}
private static class ForeignKeys0
{
public static final ForeignKey<LoottrackerlinkRecord, LoottrackereventsRecord> FK_LOOTTRACKEREVENT = Internal.createForeignKey(net.runelite.client.database.data.Keys.PK_EVENTUNIQUEID, Loottrackerlink.LOOTTRACKERLINK, "FK_LOOTTRACKEREVENT", Loottrackerlink.LOOTTRACKERLINK.EVENTUNIQUEID);
public static final ForeignKey<LoottrackerlinkRecord, LoottrackerlootRecord> FK_LOOTTRACKERLOOT = Internal.createForeignKey(net.runelite.client.database.data.Keys.PK_LOOTUNIQUEID, Loottrackerlink.LOOTTRACKERLINK, "FK_LOOTTRACKERLOOT", Loottrackerlink.LOOTTRACKERLINK.DROPUNIQUEID);
public static final ForeignKey<LoottrackerlinkRecord, UserRecord> FK_USER = Internal.createForeignKey(net.runelite.client.database.data.Keys.PK_USER, Loottrackerlink.LOOTTRACKERLINK, "FK_USER", Loottrackerlink.LOOTTRACKERLINK.DROPUNIQUEID);
}
}

View File

@@ -0,0 +1,95 @@
/*
* This file is generated by jOOQ.
*/
package net.runelite.client.database.data;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.annotation.Generated;
import net.runelite.client.database.data.tables.Loottrackerevents;
import net.runelite.client.database.data.tables.Loottrackerlink;
import net.runelite.client.database.data.tables.Loottrackerloot;
import net.runelite.client.database.data.tables.User;
import org.jooq.Catalog;
import org.jooq.Table;
import org.jooq.impl.SchemaImpl;
/**
* This class is generated by jOOQ.
*/
@Generated(
value = {
"http://www.jooq.org",
"jOOQ version:3.11.12"
},
comments = "This class is generated by jOOQ"
)
@SuppressWarnings({"all", "unchecked", "rawtypes"})
public class Public extends SchemaImpl
{
private static final long serialVersionUID = 1499404561;
/**
* The reference instance of <code>PUBLIC</code>
*/
public static final Public PUBLIC = new Public();
/**
* The table <code>PUBLIC.LOOTTRACKEREVENTS</code>.
*/
public final Loottrackerevents LOOTTRACKEREVENTS = net.runelite.client.database.data.tables.Loottrackerevents.LOOTTRACKEREVENTS;
/**
* The table <code>PUBLIC.LOOTTRACKERLINK</code>.
*/
public final Loottrackerlink LOOTTRACKERLINK = net.runelite.client.database.data.tables.Loottrackerlink.LOOTTRACKERLINK;
/**
* The table <code>PUBLIC.LOOTTRACKERLOOT</code>.
*/
public final Loottrackerloot LOOTTRACKERLOOT = net.runelite.client.database.data.tables.Loottrackerloot.LOOTTRACKERLOOT;
/**
* The table <code>PUBLIC.USER</code>.
*/
public final User USER = net.runelite.client.database.data.tables.User.USER;
/**
* No further instances allowed
*/
private Public()
{
super("PUBLIC", null);
}
/**
* {@inheritDoc}
*/
@Override
public Catalog getCatalog()
{
return DefaultCatalog.DEFAULT_CATALOG;
}
@Override
public final List<Table<?>> getTables()
{
List result = new ArrayList();
result.addAll(getTables0());
return result;
}
private final List<Table<?>> getTables0()
{
return Arrays.<Table<?>>asList(
Loottrackerevents.LOOTTRACKEREVENTS,
Loottrackerlink.LOOTTRACKERLINK,
Loottrackerloot.LOOTTRACKERLOOT,
User.USER);
}
}

View File

@@ -0,0 +1,47 @@
/*
* This file is generated by jOOQ.
*/
package net.runelite.client.database.data;
import javax.annotation.Generated;
import net.runelite.client.database.data.tables.Loottrackerevents;
import net.runelite.client.database.data.tables.Loottrackerlink;
import net.runelite.client.database.data.tables.Loottrackerloot;
import net.runelite.client.database.data.tables.User;
/**
* Convenience access to all tables in PUBLIC
*/
@Generated(
value = {
"http://www.jooq.org",
"jOOQ version:3.11.12"
},
comments = "This class is generated by jOOQ"
)
@SuppressWarnings({"all", "unchecked", "rawtypes"})
public class Tables
{
/**
* The table <code>PUBLIC.LOOTTRACKEREVENTS</code>.
*/
public static final Loottrackerevents LOOTTRACKEREVENTS = net.runelite.client.database.data.tables.Loottrackerevents.LOOTTRACKEREVENTS;
/**
* The table <code>PUBLIC.LOOTTRACKERLINK</code>.
*/
public static final Loottrackerlink LOOTTRACKERLINK = net.runelite.client.database.data.tables.Loottrackerlink.LOOTTRACKERLINK;
/**
* The table <code>PUBLIC.LOOTTRACKERLOOT</code>.
*/
public static final Loottrackerloot LOOTTRACKERLOOT = net.runelite.client.database.data.tables.Loottrackerloot.LOOTTRACKERLOOT;
/**
* The table <code>PUBLIC.USER</code>.
*/
public static final User USER = net.runelite.client.database.data.tables.User.USER;
}

View File

@@ -0,0 +1,189 @@
/*
* This file is generated by jOOQ.
*/
package net.runelite.client.database.data.tables;
import java.sql.Timestamp;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import javax.annotation.Generated;
import net.runelite.client.database.data.Indexes;
import net.runelite.client.database.data.Keys;
import net.runelite.client.database.data.Public;
import net.runelite.client.database.data.tables.records.LoottrackereventsRecord;
import org.jooq.Field;
import org.jooq.ForeignKey;
import org.jooq.Index;
import org.jooq.Name;
import org.jooq.Record;
import org.jooq.Schema;
import org.jooq.Table;
import org.jooq.TableField;
import org.jooq.UniqueKey;
import org.jooq.impl.DSL;
import org.jooq.impl.TableImpl;
/**
* This class is generated by jOOQ.
*/
@Generated(
value = {
"http://www.jooq.org",
"jOOQ version:3.11.12"
},
comments = "This class is generated by jOOQ"
)
@SuppressWarnings({"all", "unchecked", "rawtypes"})
public class Loottrackerevents extends TableImpl<LoottrackereventsRecord>
{
private static final long serialVersionUID = -824670812;
/**
* The reference instance of <code>PUBLIC.LOOTTRACKEREVENTS</code>
*/
public static final Loottrackerevents LOOTTRACKEREVENTS = new Loottrackerevents();
/**
* The class holding records for this type
*/
@Override
public Class<LoottrackereventsRecord> getRecordType()
{
return LoottrackereventsRecord.class;
}
/**
* The column <code>PUBLIC.LOOTTRACKEREVENTS.UNIQUEID</code>.
*/
public final TableField<LoottrackereventsRecord, UUID> UNIQUEID = createField("UNIQUEID", org.jooq.impl.SQLDataType.UUID.nullable(false), this, "");
/**
* The column <code>PUBLIC.LOOTTRACKEREVENTS.EVENTID</code>.
*/
public final TableField<LoottrackereventsRecord, String> EVENTID = createField("EVENTID", org.jooq.impl.SQLDataType.VARCHAR(255).nullable(false), this, "");
/**
* The column <code>PUBLIC.LOOTTRACKEREVENTS.TYPE</code>.
*/
public final TableField<LoottrackereventsRecord, String> TYPE = createField("TYPE", org.jooq.impl.SQLDataType.VARCHAR(255).nullable(false), this, "");
/**
* The column <code>PUBLIC.LOOTTRACKEREVENTS.TIME</code>.
*/
public final TableField<LoottrackereventsRecord, Timestamp> TIME = createField("TIME", org.jooq.impl.SQLDataType.TIMESTAMP.precision(6).nullable(false), this, "");
/**
* Create a <code>PUBLIC.LOOTTRACKEREVENTS</code> table reference
*/
public Loottrackerevents()
{
this(DSL.name("LOOTTRACKEREVENTS"), null);
}
/**
* Create an aliased <code>PUBLIC.LOOTTRACKEREVENTS</code> table reference
*/
public Loottrackerevents(String alias)
{
this(DSL.name(alias), LOOTTRACKEREVENTS);
}
/**
* Create an aliased <code>PUBLIC.LOOTTRACKEREVENTS</code> table reference
*/
public Loottrackerevents(Name alias)
{
this(alias, LOOTTRACKEREVENTS);
}
private Loottrackerevents(Name alias, Table<LoottrackereventsRecord> aliased)
{
this(alias, aliased, null);
}
private Loottrackerevents(Name alias, Table<LoottrackereventsRecord> aliased, Field<?>[] parameters)
{
super(alias, null, aliased, parameters, DSL.comment(""));
}
public <O extends Record> Loottrackerevents(Table<O> child, ForeignKey<O, LoottrackereventsRecord> key)
{
super(child, key, LOOTTRACKEREVENTS);
}
/**
* {@inheritDoc}
*/
@Override
public Schema getSchema()
{
return Public.PUBLIC;
}
/**
* {@inheritDoc}
*/
@Override
public List<Index> getIndexes()
{
return Arrays.<Index>asList(Indexes.PRIMARY_KEY_B);
}
/**
* {@inheritDoc}
*/
@Override
public UniqueKey<LoottrackereventsRecord> getPrimaryKey()
{
return Keys.PK_EVENTUNIQUEID;
}
/**
* {@inheritDoc}
*/
@Override
public List<UniqueKey<LoottrackereventsRecord>> getKeys()
{
return Arrays.<UniqueKey<LoottrackereventsRecord>>asList(Keys.PK_EVENTUNIQUEID);
}
/**
* {@inheritDoc}
*/
@Override
public Loottrackerevents as(String alias)
{
return new Loottrackerevents(DSL.name(alias), this);
}
/**
* {@inheritDoc}
*/
@Override
public Loottrackerevents as(Name alias)
{
return new Loottrackerevents(alias, this);
}
/**
* Rename this table
*/
@Override
public Loottrackerevents rename(String name)
{
return new Loottrackerevents(DSL.name(name), null);
}
/**
* Rename this table
*/
@Override
public Loottrackerevents rename(Name name)
{
return new Loottrackerevents(name, null);
}
}

View File

@@ -0,0 +1,212 @@
/*
* This file is generated by jOOQ.
*/
package net.runelite.client.database.data.tables;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import javax.annotation.Generated;
import net.runelite.client.database.data.Indexes;
import net.runelite.client.database.data.Keys;
import net.runelite.client.database.data.Public;
import net.runelite.client.database.data.tables.records.LoottrackerlinkRecord;
import org.jooq.Field;
import org.jooq.ForeignKey;
import org.jooq.Index;
import org.jooq.Name;
import org.jooq.Record;
import org.jooq.Schema;
import org.jooq.Table;
import org.jooq.TableField;
import org.jooq.UniqueKey;
import org.jooq.impl.DSL;
import org.jooq.impl.TableImpl;
/**
* This class is generated by jOOQ.
*/
@Generated(
value = {
"http://www.jooq.org",
"jOOQ version:3.11.12"
},
comments = "This class is generated by jOOQ"
)
@SuppressWarnings({"all", "unchecked", "rawtypes"})
public class Loottrackerlink extends TableImpl<LoottrackerlinkRecord>
{
private static final long serialVersionUID = 1145289106;
/**
* The reference instance of <code>PUBLIC.LOOTTRACKERLINK</code>
*/
public static final Loottrackerlink LOOTTRACKERLINK = new Loottrackerlink();
/**
* The class holding records for this type
*/
@Override
public Class<LoottrackerlinkRecord> getRecordType()
{
return LoottrackerlinkRecord.class;
}
/**
* The column <code>PUBLIC.LOOTTRACKERLINK.LINKUNIQUEID</code>.
*/
public final TableField<LoottrackerlinkRecord, UUID> LINKUNIQUEID = createField("LINKUNIQUEID", org.jooq.impl.SQLDataType.UUID.nullable(false), this, "");
/**
* The column <code>PUBLIC.LOOTTRACKERLINK.EVENTUNIQUEID</code>.
*/
public final TableField<LoottrackerlinkRecord, UUID> EVENTUNIQUEID = createField("EVENTUNIQUEID", org.jooq.impl.SQLDataType.UUID.nullable(false), this, "");
/**
* The column <code>PUBLIC.LOOTTRACKERLINK.DROPUNIQUEID</code>.
*/
public final TableField<LoottrackerlinkRecord, UUID> DROPUNIQUEID = createField("DROPUNIQUEID", org.jooq.impl.SQLDataType.UUID.nullable(false), this, "");
/**
* The column <code>PUBLIC.LOOTTRACKERLINK.USERUNIQUEID</code>.
*/
public final TableField<LoottrackerlinkRecord, UUID> USERUNIQUEID = createField("USERUNIQUEID", org.jooq.impl.SQLDataType.UUID.nullable(false), this, "");
/**
* Create a <code>PUBLIC.LOOTTRACKERLINK</code> table reference
*/
public Loottrackerlink()
{
this(DSL.name("LOOTTRACKERLINK"), null);
}
/**
* Create an aliased <code>PUBLIC.LOOTTRACKERLINK</code> table reference
*/
public Loottrackerlink(String alias)
{
this(DSL.name(alias), LOOTTRACKERLINK);
}
/**
* Create an aliased <code>PUBLIC.LOOTTRACKERLINK</code> table reference
*/
public Loottrackerlink(Name alias)
{
this(alias, LOOTTRACKERLINK);
}
private Loottrackerlink(Name alias, Table<LoottrackerlinkRecord> aliased)
{
this(alias, aliased, null);
}
private Loottrackerlink(Name alias, Table<LoottrackerlinkRecord> aliased, Field<?>[] parameters)
{
super(alias, null, aliased, parameters, DSL.comment(""));
}
public <O extends Record> Loottrackerlink(Table<O> child, ForeignKey<O, LoottrackerlinkRecord> key)
{
super(child, key, LOOTTRACKERLINK);
}
/**
* {@inheritDoc}
*/
@Override
public Schema getSchema()
{
return Public.PUBLIC;
}
/**
* {@inheritDoc}
*/
@Override
public List<Index> getIndexes()
{
return Arrays.<Index>asList(Indexes.FK_LOOTTRACKEREVENT_INDEX_6, Indexes.FK_LOOTTRACKERLOOT_INDEX_6, Indexes.PRIMARY_KEY_6B);
}
/**
* {@inheritDoc}
*/
@Override
public UniqueKey<LoottrackerlinkRecord> getPrimaryKey()
{
return Keys.PK_LOOTTRACKERLINK;
}
/**
* {@inheritDoc}
*/
@Override
public List<UniqueKey<LoottrackerlinkRecord>> getKeys()
{
return Arrays.<UniqueKey<LoottrackerlinkRecord>>asList(Keys.PK_LOOTTRACKERLINK);
}
/**
* {@inheritDoc}
*/
@Override
public List<ForeignKey<LoottrackerlinkRecord, ?>> getReferences()
{
return Arrays.<ForeignKey<LoottrackerlinkRecord, ?>>asList(Keys.FK_LOOTTRACKEREVENT, Keys.FK_LOOTTRACKERLOOT, Keys.FK_USER);
}
public Loottrackerevents loottrackerevents()
{
return new Loottrackerevents(this, Keys.FK_LOOTTRACKEREVENT);
}
public Loottrackerloot loottrackerloot()
{
return new Loottrackerloot(this, Keys.FK_LOOTTRACKERLOOT);
}
public User user()
{
return new User(this, Keys.FK_USER);
}
/**
* {@inheritDoc}
*/
@Override
public Loottrackerlink as(String alias)
{
return new Loottrackerlink(DSL.name(alias), this);
}
/**
* {@inheritDoc}
*/
@Override
public Loottrackerlink as(Name alias)
{
return new Loottrackerlink(alias, this);
}
/**
* Rename this table
*/
@Override
public Loottrackerlink rename(String name)
{
return new Loottrackerlink(DSL.name(name), null);
}
/**
* Rename this table
*/
@Override
public Loottrackerlink rename(Name name)
{
return new Loottrackerlink(name, null);
}
}

View File

@@ -0,0 +1,183 @@
/*
* This file is generated by jOOQ.
*/
package net.runelite.client.database.data.tables;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import javax.annotation.Generated;
import net.runelite.client.database.data.Indexes;
import net.runelite.client.database.data.Keys;
import net.runelite.client.database.data.Public;
import net.runelite.client.database.data.tables.records.LoottrackerlootRecord;
import org.jooq.Field;
import org.jooq.ForeignKey;
import org.jooq.Index;
import org.jooq.Name;
import org.jooq.Record;
import org.jooq.Schema;
import org.jooq.Table;
import org.jooq.TableField;
import org.jooq.UniqueKey;
import org.jooq.impl.DSL;
import org.jooq.impl.TableImpl;
/**
* This class is generated by jOOQ.
*/
@Generated(
value = {
"http://www.jooq.org",
"jOOQ version:3.11.12"
},
comments = "This class is generated by jOOQ"
)
@SuppressWarnings({"all", "unchecked", "rawtypes"})
public class Loottrackerloot extends TableImpl<LoottrackerlootRecord>
{
private static final long serialVersionUID = 1952959378;
/**
* The reference instance of <code>PUBLIC.LOOTTRACKERLOOT</code>
*/
public static final Loottrackerloot LOOTTRACKERLOOT = new Loottrackerloot();
/**
* The class holding records for this type
*/
@Override
public Class<LoottrackerlootRecord> getRecordType()
{
return LoottrackerlootRecord.class;
}
/**
* The column <code>PUBLIC.LOOTTRACKERLOOT.UNIQUEID</code>.
*/
public final TableField<LoottrackerlootRecord, UUID> UNIQUEID = createField("UNIQUEID", org.jooq.impl.SQLDataType.UUID.nullable(false), this, "");
/**
* The column <code>PUBLIC.LOOTTRACKERLOOT.ITEMID</code>.
*/
public final TableField<LoottrackerlootRecord, Integer> ITEMID = createField("ITEMID", org.jooq.impl.SQLDataType.INTEGER.nullable(false), this, "");
/**
* The column <code>PUBLIC.LOOTTRACKERLOOT.QUANTITY</code>.
*/
public final TableField<LoottrackerlootRecord, Integer> QUANTITY = createField("QUANTITY", org.jooq.impl.SQLDataType.INTEGER.nullable(false), this, "");
/**
* Create a <code>PUBLIC.LOOTTRACKERLOOT</code> table reference
*/
public Loottrackerloot()
{
this(DSL.name("LOOTTRACKERLOOT"), null);
}
/**
* Create an aliased <code>PUBLIC.LOOTTRACKERLOOT</code> table reference
*/
public Loottrackerloot(String alias)
{
this(DSL.name(alias), LOOTTRACKERLOOT);
}
/**
* Create an aliased <code>PUBLIC.LOOTTRACKERLOOT</code> table reference
*/
public Loottrackerloot(Name alias)
{
this(alias, LOOTTRACKERLOOT);
}
private Loottrackerloot(Name alias, Table<LoottrackerlootRecord> aliased)
{
this(alias, aliased, null);
}
private Loottrackerloot(Name alias, Table<LoottrackerlootRecord> aliased, Field<?>[] parameters)
{
super(alias, null, aliased, parameters, DSL.comment(""));
}
public <O extends Record> Loottrackerloot(Table<O> child, ForeignKey<O, LoottrackerlootRecord> key)
{
super(child, key, LOOTTRACKERLOOT);
}
/**
* {@inheritDoc}
*/
@Override
public Schema getSchema()
{
return Public.PUBLIC;
}
/**
* {@inheritDoc}
*/
@Override
public List<Index> getIndexes()
{
return Arrays.<Index>asList(Indexes.PRIMARY_KEY_6);
}
/**
* {@inheritDoc}
*/
@Override
public UniqueKey<LoottrackerlootRecord> getPrimaryKey()
{
return Keys.PK_LOOTUNIQUEID;
}
/**
* {@inheritDoc}
*/
@Override
public List<UniqueKey<LoottrackerlootRecord>> getKeys()
{
return Arrays.<UniqueKey<LoottrackerlootRecord>>asList(Keys.PK_LOOTUNIQUEID);
}
/**
* {@inheritDoc}
*/
@Override
public Loottrackerloot as(String alias)
{
return new Loottrackerloot(DSL.name(alias), this);
}
/**
* {@inheritDoc}
*/
@Override
public Loottrackerloot as(Name alias)
{
return new Loottrackerloot(alias, this);
}
/**
* Rename this table
*/
@Override
public Loottrackerloot rename(String name)
{
return new Loottrackerloot(DSL.name(name), null);
}
/**
* Rename this table
*/
@Override
public Loottrackerloot rename(Name name)
{
return new Loottrackerloot(name, null);
}
}

View File

@@ -0,0 +1,178 @@
/*
* This file is generated by jOOQ.
*/
package net.runelite.client.database.data.tables;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import javax.annotation.Generated;
import net.runelite.client.database.data.Indexes;
import net.runelite.client.database.data.Keys;
import net.runelite.client.database.data.Public;
import net.runelite.client.database.data.tables.records.UserRecord;
import org.jooq.Field;
import org.jooq.ForeignKey;
import org.jooq.Index;
import org.jooq.Name;
import org.jooq.Record;
import org.jooq.Schema;
import org.jooq.Table;
import org.jooq.TableField;
import org.jooq.UniqueKey;
import org.jooq.impl.DSL;
import org.jooq.impl.TableImpl;
/**
* This class is generated by jOOQ.
*/
@Generated(
value = {
"http://www.jooq.org",
"jOOQ version:3.11.12"
},
comments = "This class is generated by jOOQ"
)
@SuppressWarnings({"all", "unchecked", "rawtypes"})
public class User extends TableImpl<UserRecord>
{
private static final long serialVersionUID = 270848699;
/**
* The reference instance of <code>PUBLIC.USER</code>
*/
public static final User USER = new User();
/**
* The class holding records for this type
*/
@Override
public Class<UserRecord> getRecordType()
{
return UserRecord.class;
}
/**
* The column <code>PUBLIC.USER.UNIQUEID</code>.
*/
public final TableField<UserRecord, UUID> UNIQUEID = createField("UNIQUEID", org.jooq.impl.SQLDataType.UUID.nullable(false), this, "");
/**
* The column <code>PUBLIC.USER.USERNAME</code>.
*/
public final TableField<UserRecord, String> USERNAME = createField("USERNAME", org.jooq.impl.SQLDataType.VARCHAR(12).nullable(false), this, "");
/**
* Create a <code>PUBLIC.USER</code> table reference
*/
public User()
{
this(DSL.name("USER"), null);
}
/**
* Create an aliased <code>PUBLIC.USER</code> table reference
*/
public User(String alias)
{
this(DSL.name(alias), USER);
}
/**
* Create an aliased <code>PUBLIC.USER</code> table reference
*/
public User(Name alias)
{
this(alias, USER);
}
private User(Name alias, Table<UserRecord> aliased)
{
this(alias, aliased, null);
}
private User(Name alias, Table<UserRecord> aliased, Field<?>[] parameters)
{
super(alias, null, aliased, parameters, DSL.comment(""));
}
public <O extends Record> User(Table<O> child, ForeignKey<O, UserRecord> key)
{
super(child, key, USER);
}
/**
* {@inheritDoc}
*/
@Override
public Schema getSchema()
{
return Public.PUBLIC;
}
/**
* {@inheritDoc}
*/
@Override
public List<Index> getIndexes()
{
return Arrays.<Index>asList(Indexes.PRIMARY_KEY_2);
}
/**
* {@inheritDoc}
*/
@Override
public UniqueKey<UserRecord> getPrimaryKey()
{
return Keys.PK_USER;
}
/**
* {@inheritDoc}
*/
@Override
public List<UniqueKey<UserRecord>> getKeys()
{
return Arrays.<UniqueKey<UserRecord>>asList(Keys.PK_USER);
}
/**
* {@inheritDoc}
*/
@Override
public User as(String alias)
{
return new User(DSL.name(alias), this);
}
/**
* {@inheritDoc}
*/
@Override
public User as(Name alias)
{
return new User(alias, this);
}
/**
* Rename this table
*/
@Override
public User rename(String name)
{
return new User(DSL.name(name), null);
}
/**
* Rename this table
*/
@Override
public User rename(Name name)
{
return new User(name, null);
}
}

View File

@@ -0,0 +1,318 @@
/*
* This file is generated by jOOQ.
*/
package net.runelite.client.database.data.tables.records;
import java.sql.Timestamp;
import java.util.UUID;
import javax.annotation.Generated;
import net.runelite.client.database.data.tables.Loottrackerevents;
import org.jooq.Field;
import org.jooq.Record1;
import org.jooq.Record4;
import org.jooq.Row4;
import org.jooq.impl.UpdatableRecordImpl;
/**
* This class is generated by jOOQ.
*/
@Generated(
value = {
"http://www.jooq.org",
"jOOQ version:3.11.12"
},
comments = "This class is generated by jOOQ"
)
@SuppressWarnings({"all", "unchecked", "rawtypes"})
public class LoottrackereventsRecord extends UpdatableRecordImpl<LoottrackereventsRecord> implements Record4<UUID, String, String, Timestamp>
{
private static final long serialVersionUID = -1505143967;
/**
* Setter for <code>PUBLIC.LOOTTRACKEREVENTS.UNIQUEID</code>.
*/
public void setUniqueid(UUID value)
{
set(0, value);
}
/**
* Getter for <code>PUBLIC.LOOTTRACKEREVENTS.UNIQUEID</code>.
*/
public UUID getUniqueid()
{
return (UUID) get(0);
}
/**
* Setter for <code>PUBLIC.LOOTTRACKEREVENTS.EVENTID</code>.
*/
public void setEventid(String value)
{
set(1, value);
}
/**
* Getter for <code>PUBLIC.LOOTTRACKEREVENTS.EVENTID</code>.
*/
public String getEventid()
{
return (String) get(1);
}
/**
* Setter for <code>PUBLIC.LOOTTRACKEREVENTS.TYPE</code>.
*/
public void setType(String value)
{
set(2, value);
}
/**
* Getter for <code>PUBLIC.LOOTTRACKEREVENTS.TYPE</code>.
*/
public String getType()
{
return (String) get(2);
}
/**
* Setter for <code>PUBLIC.LOOTTRACKEREVENTS.TIME</code>.
*/
public void setTime(Timestamp value)
{
set(3, value);
}
/**
* Getter for <code>PUBLIC.LOOTTRACKEREVENTS.TIME</code>.
*/
public Timestamp getTime()
{
return (Timestamp) get(3);
}
// -------------------------------------------------------------------------
// Primary key information
// -------------------------------------------------------------------------
/**
* {@inheritDoc}
*/
@Override
public Record1<UUID> key()
{
return (Record1) super.key();
}
// -------------------------------------------------------------------------
// Record4 type implementation
// -------------------------------------------------------------------------
/**
* {@inheritDoc}
*/
@Override
public Row4<UUID, String, String, Timestamp> fieldsRow()
{
return (Row4) super.fieldsRow();
}
/**
* {@inheritDoc}
*/
@Override
public Row4<UUID, String, String, Timestamp> valuesRow()
{
return (Row4) super.valuesRow();
}
/**
* {@inheritDoc}
*/
@Override
public Field<UUID> field1()
{
return Loottrackerevents.LOOTTRACKEREVENTS.UNIQUEID;
}
/**
* {@inheritDoc}
*/
@Override
public Field<String> field2()
{
return Loottrackerevents.LOOTTRACKEREVENTS.EVENTID;
}
/**
* {@inheritDoc}
*/
@Override
public Field<String> field3()
{
return Loottrackerevents.LOOTTRACKEREVENTS.TYPE;
}
/**
* {@inheritDoc}
*/
@Override
public Field<Timestamp> field4()
{
return Loottrackerevents.LOOTTRACKEREVENTS.TIME;
}
/**
* {@inheritDoc}
*/
@Override
public UUID component1()
{
return getUniqueid();
}
/**
* {@inheritDoc}
*/
@Override
public String component2()
{
return getEventid();
}
/**
* {@inheritDoc}
*/
@Override
public String component3()
{
return getType();
}
/**
* {@inheritDoc}
*/
@Override
public Timestamp component4()
{
return getTime();
}
/**
* {@inheritDoc}
*/
@Override
public UUID value1()
{
return getUniqueid();
}
/**
* {@inheritDoc}
*/
@Override
public String value2()
{
return getEventid();
}
/**
* {@inheritDoc}
*/
@Override
public String value3()
{
return getType();
}
/**
* {@inheritDoc}
*/
@Override
public Timestamp value4()
{
return getTime();
}
/**
* {@inheritDoc}
*/
@Override
public LoottrackereventsRecord value1(UUID value)
{
setUniqueid(value);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public LoottrackereventsRecord value2(String value)
{
setEventid(value);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public LoottrackereventsRecord value3(String value)
{
setType(value);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public LoottrackereventsRecord value4(Timestamp value)
{
setTime(value);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public LoottrackereventsRecord values(UUID value1, String value2, String value3, Timestamp value4)
{
value1(value1);
value2(value2);
value3(value3);
value4(value4);
return this;
}
// -------------------------------------------------------------------------
// Constructors
// -------------------------------------------------------------------------
/**
* Create a detached LoottrackereventsRecord
*/
public LoottrackereventsRecord()
{
super(Loottrackerevents.LOOTTRACKEREVENTS);
}
/**
* Create a detached, initialised LoottrackereventsRecord
*/
public LoottrackereventsRecord(UUID uniqueid, String eventid, String type, Timestamp time)
{
super(Loottrackerevents.LOOTTRACKEREVENTS);
set(0, uniqueid);
set(1, eventid);
set(2, type);
set(3, time);
}
}

View File

@@ -0,0 +1,317 @@
/*
* This file is generated by jOOQ.
*/
package net.runelite.client.database.data.tables.records;
import java.util.UUID;
import javax.annotation.Generated;
import net.runelite.client.database.data.tables.Loottrackerlink;
import org.jooq.Field;
import org.jooq.Record1;
import org.jooq.Record4;
import org.jooq.Row4;
import org.jooq.impl.UpdatableRecordImpl;
/**
* This class is generated by jOOQ.
*/
@Generated(
value = {
"http://www.jooq.org",
"jOOQ version:3.11.12"
},
comments = "This class is generated by jOOQ"
)
@SuppressWarnings({"all", "unchecked", "rawtypes"})
public class LoottrackerlinkRecord extends UpdatableRecordImpl<LoottrackerlinkRecord> implements Record4<UUID, UUID, UUID, UUID>
{
private static final long serialVersionUID = 1985117517;
/**
* Setter for <code>PUBLIC.LOOTTRACKERLINK.LINKUNIQUEID</code>.
*/
public void setLinkuniqueid(UUID value)
{
set(0, value);
}
/**
* Getter for <code>PUBLIC.LOOTTRACKERLINK.LINKUNIQUEID</code>.
*/
public UUID getLinkuniqueid()
{
return (UUID) get(0);
}
/**
* Setter for <code>PUBLIC.LOOTTRACKERLINK.EVENTUNIQUEID</code>.
*/
public void setEventuniqueid(UUID value)
{
set(1, value);
}
/**
* Getter for <code>PUBLIC.LOOTTRACKERLINK.EVENTUNIQUEID</code>.
*/
public UUID getEventuniqueid()
{
return (UUID) get(1);
}
/**
* Setter for <code>PUBLIC.LOOTTRACKERLINK.DROPUNIQUEID</code>.
*/
public void setDropuniqueid(UUID value)
{
set(2, value);
}
/**
* Getter for <code>PUBLIC.LOOTTRACKERLINK.DROPUNIQUEID</code>.
*/
public UUID getDropuniqueid()
{
return (UUID) get(2);
}
/**
* Setter for <code>PUBLIC.LOOTTRACKERLINK.USERUNIQUEID</code>.
*/
public void setUseruniqueid(UUID value)
{
set(3, value);
}
/**
* Getter for <code>PUBLIC.LOOTTRACKERLINK.USERUNIQUEID</code>.
*/
public UUID getUseruniqueid()
{
return (UUID) get(3);
}
// -------------------------------------------------------------------------
// Primary key information
// -------------------------------------------------------------------------
/**
* {@inheritDoc}
*/
@Override
public Record1<UUID> key()
{
return (Record1) super.key();
}
// -------------------------------------------------------------------------
// Record4 type implementation
// -------------------------------------------------------------------------
/**
* {@inheritDoc}
*/
@Override
public Row4<UUID, UUID, UUID, UUID> fieldsRow()
{
return (Row4) super.fieldsRow();
}
/**
* {@inheritDoc}
*/
@Override
public Row4<UUID, UUID, UUID, UUID> valuesRow()
{
return (Row4) super.valuesRow();
}
/**
* {@inheritDoc}
*/
@Override
public Field<UUID> field1()
{
return Loottrackerlink.LOOTTRACKERLINK.LINKUNIQUEID;
}
/**
* {@inheritDoc}
*/
@Override
public Field<UUID> field2()
{
return Loottrackerlink.LOOTTRACKERLINK.EVENTUNIQUEID;
}
/**
* {@inheritDoc}
*/
@Override
public Field<UUID> field3()
{
return Loottrackerlink.LOOTTRACKERLINK.DROPUNIQUEID;
}
/**
* {@inheritDoc}
*/
@Override
public Field<UUID> field4()
{
return Loottrackerlink.LOOTTRACKERLINK.USERUNIQUEID;
}
/**
* {@inheritDoc}
*/
@Override
public UUID component1()
{
return getLinkuniqueid();
}
/**
* {@inheritDoc}
*/
@Override
public UUID component2()
{
return getEventuniqueid();
}
/**
* {@inheritDoc}
*/
@Override
public UUID component3()
{
return getDropuniqueid();
}
/**
* {@inheritDoc}
*/
@Override
public UUID component4()
{
return getUseruniqueid();
}
/**
* {@inheritDoc}
*/
@Override
public UUID value1()
{
return getLinkuniqueid();
}
/**
* {@inheritDoc}
*/
@Override
public UUID value2()
{
return getEventuniqueid();
}
/**
* {@inheritDoc}
*/
@Override
public UUID value3()
{
return getDropuniqueid();
}
/**
* {@inheritDoc}
*/
@Override
public UUID value4()
{
return getUseruniqueid();
}
/**
* {@inheritDoc}
*/
@Override
public LoottrackerlinkRecord value1(UUID value)
{
setLinkuniqueid(value);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public LoottrackerlinkRecord value2(UUID value)
{
setEventuniqueid(value);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public LoottrackerlinkRecord value3(UUID value)
{
setDropuniqueid(value);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public LoottrackerlinkRecord value4(UUID value)
{
setUseruniqueid(value);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public LoottrackerlinkRecord values(UUID value1, UUID value2, UUID value3, UUID value4)
{
value1(value1);
value2(value2);
value3(value3);
value4(value4);
return this;
}
// -------------------------------------------------------------------------
// Constructors
// -------------------------------------------------------------------------
/**
* Create a detached LoottrackerlinkRecord
*/
public LoottrackerlinkRecord()
{
super(Loottrackerlink.LOOTTRACKERLINK);
}
/**
* Create a detached, initialised LoottrackerlinkRecord
*/
public LoottrackerlinkRecord(UUID linkuniqueid, UUID eventuniqueid, UUID dropuniqueid, UUID useruniqueid)
{
super(Loottrackerlink.LOOTTRACKERLINK);
set(0, linkuniqueid);
set(1, eventuniqueid);
set(2, dropuniqueid);
set(3, useruniqueid);
}
}

View File

@@ -0,0 +1,262 @@
/*
* This file is generated by jOOQ.
*/
package net.runelite.client.database.data.tables.records;
import java.util.UUID;
import javax.annotation.Generated;
import net.runelite.client.database.data.tables.Loottrackerloot;
import org.jooq.Field;
import org.jooq.Record1;
import org.jooq.Record3;
import org.jooq.Row3;
import org.jooq.impl.UpdatableRecordImpl;
/**
* This class is generated by jOOQ.
*/
@Generated(
value = {
"http://www.jooq.org",
"jOOQ version:3.11.12"
},
comments = "This class is generated by jOOQ"
)
@SuppressWarnings({"all", "unchecked", "rawtypes"})
public class LoottrackerlootRecord extends UpdatableRecordImpl<LoottrackerlootRecord> implements Record3<UUID, Integer, Integer>
{
private static final long serialVersionUID = -1894768090;
/**
* Setter for <code>PUBLIC.LOOTTRACKERLOOT.UNIQUEID</code>.
*/
public void setUniqueid(UUID value)
{
set(0, value);
}
/**
* Getter for <code>PUBLIC.LOOTTRACKERLOOT.UNIQUEID</code>.
*/
public UUID getUniqueid()
{
return (UUID) get(0);
}
/**
* Setter for <code>PUBLIC.LOOTTRACKERLOOT.ITEMID</code>.
*/
public void setItemid(Integer value)
{
set(1, value);
}
/**
* Getter for <code>PUBLIC.LOOTTRACKERLOOT.ITEMID</code>.
*/
public Integer getItemid()
{
return (Integer) get(1);
}
/**
* Setter for <code>PUBLIC.LOOTTRACKERLOOT.QUANTITY</code>.
*/
public void setQuantity(Integer value)
{
set(2, value);
}
/**
* Getter for <code>PUBLIC.LOOTTRACKERLOOT.QUANTITY</code>.
*/
public Integer getQuantity()
{
return (Integer) get(2);
}
// -------------------------------------------------------------------------
// Primary key information
// -------------------------------------------------------------------------
/**
* {@inheritDoc}
*/
@Override
public Record1<UUID> key()
{
return (Record1) super.key();
}
// -------------------------------------------------------------------------
// Record3 type implementation
// -------------------------------------------------------------------------
/**
* {@inheritDoc}
*/
@Override
public Row3<UUID, Integer, Integer> fieldsRow()
{
return (Row3) super.fieldsRow();
}
/**
* {@inheritDoc}
*/
@Override
public Row3<UUID, Integer, Integer> valuesRow()
{
return (Row3) super.valuesRow();
}
/**
* {@inheritDoc}
*/
@Override
public Field<UUID> field1()
{
return Loottrackerloot.LOOTTRACKERLOOT.UNIQUEID;
}
/**
* {@inheritDoc}
*/
@Override
public Field<Integer> field2()
{
return Loottrackerloot.LOOTTRACKERLOOT.ITEMID;
}
/**
* {@inheritDoc}
*/
@Override
public Field<Integer> field3()
{
return Loottrackerloot.LOOTTRACKERLOOT.QUANTITY;
}
/**
* {@inheritDoc}
*/
@Override
public UUID component1()
{
return getUniqueid();
}
/**
* {@inheritDoc}
*/
@Override
public Integer component2()
{
return getItemid();
}
/**
* {@inheritDoc}
*/
@Override
public Integer component3()
{
return getQuantity();
}
/**
* {@inheritDoc}
*/
@Override
public UUID value1()
{
return getUniqueid();
}
/**
* {@inheritDoc}
*/
@Override
public Integer value2()
{
return getItemid();
}
/**
* {@inheritDoc}
*/
@Override
public Integer value3()
{
return getQuantity();
}
/**
* {@inheritDoc}
*/
@Override
public LoottrackerlootRecord value1(UUID value)
{
setUniqueid(value);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public LoottrackerlootRecord value2(Integer value)
{
setItemid(value);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public LoottrackerlootRecord value3(Integer value)
{
setQuantity(value);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public LoottrackerlootRecord values(UUID value1, Integer value2, Integer value3)
{
value1(value1);
value2(value2);
value3(value3);
return this;
}
// -------------------------------------------------------------------------
// Constructors
// -------------------------------------------------------------------------
/**
* Create a detached LoottrackerlootRecord
*/
public LoottrackerlootRecord()
{
super(Loottrackerloot.LOOTTRACKERLOOT);
}
/**
* Create a detached, initialised LoottrackerlootRecord
*/
public LoottrackerlootRecord(UUID uniqueid, Integer itemid, Integer quantity)
{
super(Loottrackerloot.LOOTTRACKERLOOT);
set(0, uniqueid);
set(1, itemid);
set(2, quantity);
}
}

View File

@@ -0,0 +1,207 @@
/*
* This file is generated by jOOQ.
*/
package net.runelite.client.database.data.tables.records;
import java.util.UUID;
import javax.annotation.Generated;
import net.runelite.client.database.data.tables.User;
import org.jooq.Field;
import org.jooq.Record1;
import org.jooq.Record2;
import org.jooq.Row2;
import org.jooq.impl.UpdatableRecordImpl;
/**
* This class is generated by jOOQ.
*/
@Generated(
value = {
"http://www.jooq.org",
"jOOQ version:3.11.12"
},
comments = "This class is generated by jOOQ"
)
@SuppressWarnings({"all", "unchecked", "rawtypes"})
public class UserRecord extends UpdatableRecordImpl<UserRecord> implements Record2<UUID, String>
{
private static final long serialVersionUID = 628808107;
/**
* Setter for <code>PUBLIC.USER.UNIQUEID</code>.
*/
public void setUniqueid(UUID value)
{
set(0, value);
}
/**
* Getter for <code>PUBLIC.USER.UNIQUEID</code>.
*/
public UUID getUniqueid()
{
return (UUID) get(0);
}
/**
* Setter for <code>PUBLIC.USER.USERNAME</code>.
*/
public void setUsername(String value)
{
set(1, value);
}
/**
* Getter for <code>PUBLIC.USER.USERNAME</code>.
*/
public String getUsername()
{
return (String) get(1);
}
// -------------------------------------------------------------------------
// Primary key information
// -------------------------------------------------------------------------
/**
* {@inheritDoc}
*/
@Override
public Record1<UUID> key()
{
return (Record1) super.key();
}
// -------------------------------------------------------------------------
// Record2 type implementation
// -------------------------------------------------------------------------
/**
* {@inheritDoc}
*/
@Override
public Row2<UUID, String> fieldsRow()
{
return (Row2) super.fieldsRow();
}
/**
* {@inheritDoc}
*/
@Override
public Row2<UUID, String> valuesRow()
{
return (Row2) super.valuesRow();
}
/**
* {@inheritDoc}
*/
@Override
public Field<UUID> field1()
{
return User.USER.UNIQUEID;
}
/**
* {@inheritDoc}
*/
@Override
public Field<String> field2()
{
return User.USER.USERNAME;
}
/**
* {@inheritDoc}
*/
@Override
public UUID component1()
{
return getUniqueid();
}
/**
* {@inheritDoc}
*/
@Override
public String component2()
{
return getUsername();
}
/**
* {@inheritDoc}
*/
@Override
public UUID value1()
{
return getUniqueid();
}
/**
* {@inheritDoc}
*/
@Override
public String value2()
{
return getUsername();
}
/**
* {@inheritDoc}
*/
@Override
public UserRecord value1(UUID value)
{
setUniqueid(value);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public UserRecord value2(String value)
{
setUsername(value);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public UserRecord values(UUID value1, String value2)
{
value1(value1);
value2(value2);
return this;
}
// -------------------------------------------------------------------------
// Constructors
// -------------------------------------------------------------------------
/**
* Create a detached UserRecord
*/
public UserRecord()
{
super(User.USER);
}
/**
* Create a detached, initialised UserRecord
*/
public UserRecord(UUID uniqueid, String username)
{
super(User.USER);
set(0, uniqueid);
set(1, username);
}
}

View File

@@ -195,7 +195,7 @@ public class DiscordService implements AutoCloseable
{
log.info("Discord RPC service is ready with user {}.", user.username);
currentUser = user;
eventBus.post(new DiscordReady(
eventBus.post(DiscordReady.class, new DiscordReady(
user.userId,
user.username,
user.discriminator,
@@ -204,28 +204,28 @@ public class DiscordService implements AutoCloseable
private void disconnected(int errorCode, String message)
{
eventBus.post(new DiscordDisconnected(errorCode, message));
eventBus.post(DiscordDisconnected.class, new DiscordDisconnected(errorCode, message));
}
private void errored(int errorCode, String message)
{
log.warn("Discord error: {} - {}", errorCode, message);
eventBus.post(new DiscordErrored(errorCode, message));
eventBus.post(DiscordErrored.class, new DiscordErrored(errorCode, message));
}
private void joinGame(String joinSecret)
{
eventBus.post(new DiscordJoinGame(joinSecret));
eventBus.post(DiscordJoinGame.class, new DiscordJoinGame(joinSecret));
}
private void spectateGame(String spectateSecret)
{
eventBus.post(new DiscordSpectateGame(spectateSecret));
eventBus.post(DiscordSpectateGame.class, new DiscordSpectateGame(spectateSecret));
}
private void joinRequest(DiscordUser user)
{
eventBus.post(new DiscordJoinRequest(
eventBus.post(DiscordJoinRequest.class, new DiscordJoinRequest(
user.userId,
user.username,
user.discriminator,

View File

@@ -25,12 +25,13 @@
package net.runelite.client.discord.events;
import lombok.Value;
import net.runelite.api.events.Event;
/**
* Called when the RPC connection has been severed
*/
@Value
public class DiscordDisconnected
public class DiscordDisconnected implements Event
{
/**
* Discord error code

View File

@@ -25,12 +25,13 @@
package net.runelite.client.discord.events;
import lombok.Value;
import net.runelite.api.events.Event;
/**
* Called when an internal error is caught within the SDK
*/
@Value
public class DiscordErrored
public class DiscordErrored implements Event
{
/**
* Discord error code.

View File

@@ -25,12 +25,13 @@
package net.runelite.client.discord.events;
import lombok.Value;
import net.runelite.api.events.Event;
/**
* Called when the logged in user joined a game
*/
@Value
public class DiscordJoinGame
public class DiscordJoinGame implements Event
{
/**
* Obfuscated data of your choosing used as join secret

View File

@@ -25,12 +25,13 @@
package net.runelite.client.discord.events;
import lombok.Value;
import net.runelite.api.events.Event;
/**
* Called when another discord user wants to join the game of the logged in user
*/
@Value
public class DiscordJoinRequest
public class DiscordJoinRequest implements Event
{
/**
* The userId for the user that requests to join

View File

@@ -25,12 +25,13 @@
package net.runelite.client.discord.events;
import lombok.Value;
import net.runelite.api.events.Event;
/**
* Called when the RPC connection has been established
*/
@Value
public class DiscordReady
public class DiscordReady implements Event
{
/**
* The userId for the active user

View File

@@ -25,12 +25,13 @@
package net.runelite.client.discord.events;
import lombok.Value;
import net.runelite.api.events.Event;
/**
* Called when the logged in user joined to spectate a game
*/
@Value
public class DiscordSpectateGame
public class DiscordSpectateGame implements Event
{
/**
* Obfuscated data of your choosing used as spectate secret

View File

@@ -1,249 +1,83 @@
/*
* 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 com.jakewharton.rxrelay2.PublishRelay;
import com.jakewharton.rxrelay2.Relay;
import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.events.Event;
import org.apache.commons.lang3.exception.ExceptionUtils;
@Slf4j
@RequiredArgsConstructor
@ThreadSafe
public class EventBus
@Singleton
public class EventBus implements EventBusInterface
{
@FunctionalInterface
public interface SubscriberMethod
private Map<Object, Object> subscriptionList = new HashMap<>();
private Map<Class<?>, Relay<Object>> subjectList = new HashMap<>();
private Map<Object, CompositeDisposable> subscriptionsMap = new HashMap<>();
@NonNull
private <T> Relay<Object> getSubject(Class<T> eventClass)
{
void invoke(Object event);
return subjectList.computeIfAbsent(eventClass, k -> PublishRelay.create().toSerialized());
}
@Value
private static class Subscriber
@NonNull
private CompositeDisposable getCompositeDisposable(@NonNull Object object)
{
private final Object object;
private final Method method;
@EqualsAndHashCode.Exclude
private final SubscriberMethod lamda;
void invoke(final Object arg) throws Exception
CompositeDisposable compositeDisposable = subscriptionsMap.get(object);
if (compositeDisposable == null)
{
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);
compositeDisposable = new CompositeDisposable();
subscriptionsMap.put(object, compositeDisposable);
}
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();
return compositeDisposable;
}
/**
* Unregisters all subscribed methods from provided subscriber object.
*
* @param object object to unsubscribe from
*/
public synchronized void unregister(@Nonnull final Object object)
@Override
// Subscribe on lifecycle (for example from plugin startUp -> shutdown)
public <T> void subscribe(Class<T> eventClass, @NonNull Object lifecycle, @NonNull Consumer<T> action)
{
if (subscribers == null)
if (subscriptionList.containsKey(lifecycle) && eventClass.equals(subscriptionList.get(lifecycle)))
{
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())
Disposable disposable = getSubject(eventClass)
.filter(Objects::nonNull) // Filter out null objects, better safe than sorry
.cast(eventClass) // Cast it for easier usage
.subscribe(action, error ->
{
final Subscribe sub = method.getAnnotation(Subscribe.class);
log.error("Error in eventbus: {}", error.getMessage());
log.error(ExceptionUtils.getStackTrace(error));
});
if (sub == null)
{
continue;
}
final Class<?> parameterClazz = method.getParameterTypes()[0];
map.remove(parameterClazz, new Subscriber(object, method, null));
}
}
subscribers = ImmutableMultimap.copyOf(map);
getCompositeDisposable(lifecycle).add(disposable);
subscriptionList.put(lifecycle, eventClass);
}
/**
* 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)
@Override
public void unregister(@NonNull Object lifecycle)
{
for (final Subscriber subscriber : subscribers.get(event.getClass()))
//We have to remove the composition from the map, because once you dispose it can't be used anymore
CompositeDisposable compositeDisposable = subscriptionsMap.remove(lifecycle);
subscriptionList.remove(lifecycle);
if (compositeDisposable != null)
{
try
{
subscriber.invoke(event);
}
catch (Exception e)
{
exceptionHandler.accept(e);
}
compositeDisposable.dispose();
}
}
private static MethodHandles.Lookup privateLookupIn(Class clazz) throws IllegalAccessException, NoSuchFieldException, InvocationTargetException
@Override
public <T> void post(Class<T> eventClass, @NonNull Event event)
{
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;
}
getSubject(eventClass).accept(event);
}
}

View File

@@ -0,0 +1,14 @@
package net.runelite.client.eventbus;
import io.reactivex.annotations.NonNull;
import io.reactivex.functions.Consumer;
import net.runelite.api.events.Event;
public interface EventBusInterface
{
<T> void subscribe(Class<T> eventClass, @NonNull Object lifecycle, @NonNull Consumer<T> action);
void unregister(@NonNull Object lifecycle);
<T> void post(Class<T> eventClass, @NonNull Event event);
}

View File

@@ -25,10 +25,11 @@
package net.runelite.client.events;
import lombok.Value;
import net.runelite.api.events.Event;
import net.runelite.client.ui.NavigationButton;
@Value
public class NavigationButtonAdded
public class NavigationButtonAdded implements Event
{
private NavigationButton button;
}

View File

@@ -25,10 +25,11 @@
package net.runelite.client.events;
import lombok.Value;
import net.runelite.api.events.Event;
import net.runelite.client.ui.NavigationButton;
@Value
public class NavigationButtonRemoved
public class NavigationButtonRemoved implements Event
{
private NavigationButton button;
}

View File

@@ -27,10 +27,11 @@ package net.runelite.client.events;
import java.util.Collection;
import lombok.Value;
import net.runelite.api.NPC;
import net.runelite.api.events.Event;
import net.runelite.client.game.ItemStack;
@Value
public class NpcLootReceived
public class NpcLootReceived implements Event
{
private final NPC npc;
private final Collection<ItemStack> items;

View File

@@ -26,6 +26,7 @@ package net.runelite.client.events;
import lombok.AllArgsConstructor;
import lombok.Data;
import net.runelite.api.events.Event;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayMenuEntry;
@@ -34,7 +35,7 @@ import net.runelite.client.ui.overlay.OverlayMenuEntry;
*/
@Data
@AllArgsConstructor
public class OverlayMenuClicked
public class OverlayMenuClicked implements Event
{
private OverlayMenuEntry entry;
private Overlay overlay;

View File

@@ -26,9 +26,10 @@ package net.runelite.client.events;
import java.util.UUID;
import lombok.Value;
import net.runelite.api.events.Event;
@Value
public class PartyChanged
public class PartyChanged implements Event
{
private final UUID partyId;
}

View File

@@ -27,10 +27,11 @@ package net.runelite.client.events;
import java.util.Collection;
import lombok.Value;
import net.runelite.api.Player;
import net.runelite.api.events.Event;
import net.runelite.client.game.ItemStack;
@Value
public class PlayerLootReceived
public class PlayerLootReceived implements Event
{
private final Player player;
private final Collection<ItemStack> items;

View File

@@ -25,10 +25,11 @@
package net.runelite.client.events;
import lombok.Data;
import net.runelite.api.events.Event;
import net.runelite.client.plugins.Plugin;
@Data
public class PluginChanged
public class PluginChanged implements Event
{
private final Plugin plugin;
private final boolean loaded;

View File

@@ -25,6 +25,7 @@
package net.runelite.client.events;
import lombok.Data;
import net.runelite.api.events.Event;
/**
* An event where a new RuneLite account session has been closed,
@@ -34,7 +35,7 @@ import lombok.Data;
* it has nothing to do with whether an account is being logged out.
*/
@Data
public class SessionClose
public class SessionClose implements Event
{
}

View File

@@ -25,6 +25,7 @@
package net.runelite.client.events;
import lombok.Data;
import net.runelite.api.events.Event;
/**
* An event where a new RuneLite account session has been opened
@@ -34,7 +35,7 @@ import lombok.Data;
* it has nothing to do with whether an account is being logged in.
*/
@Data
public class SessionOpen
public class SessionOpen implements Event
{
}

View File

@@ -0,0 +1,317 @@
/*
*
* 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.event.InputEvent;
import java.awt.event.KeyEvent;
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;
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 Robot peer;
public Flexo() throws AWTException
{
if (GraphicsEnvironment.isHeadless())
{
throw new AWTException("headless environment");
}
init(GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice());
}
private void init(GraphicsDevice screen) throws AWTException
{
try
{
peer = new Robot();
}
catch (Exception e)
{
client.getLogger().error("Flexo not supported on this system configuration.");
}
}
private transient Object anchor = new Object();
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();
}
public Color getPixelColor(int x, int y)
{
return peer.getPixelColor(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

@@ -0,0 +1,225 @@
/*
*
* 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

@@ -26,7 +26,18 @@
package net.runelite.client.game;
import lombok.Getter;
import static net.runelite.api.NullObjectID.*;
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.*;
import net.runelite.api.coords.WorldPoint;
@@ -74,8 +85,8 @@ public enum AgilityShortcut
BRIMHAVEN_DUNGEON_LOG_BALANCE_RETURN(1, "Log Balance", null, LOG_BALANCE_20884),
AGILITY_PYRAMID_ROCKS_WEST(1, "Rocks", null, CLIMBING_ROCKS_11948),
CAIRN_ISLE_CLIMBING_ROCKS(1, "Rocks", null, CLIMBING_ROCKS),
KARAMJA_GLIDER_LOG(1, "Log Balance", new WorldPoint(2906, 3050, 0), A_WOODEN_LOG ),
FALADOR_CRUMBLING_WALL(5, "Crumbling Wall", new WorldPoint(2936, 3357, 0), CRUMBLING_WALL_24222 ),
KARAMJA_GLIDER_LOG(1, "Log Balance", new WorldPoint(2906, 3050, 0), A_WOODEN_LOG),
FALADOR_CRUMBLING_WALL(5, "Crumbling Wall", new WorldPoint(2936, 3357, 0), CRUMBLING_WALL_24222),
RIVER_LUM_GRAPPLE_WEST(8, "Grapple Broken Raft", new WorldPoint(3245, 3179, 0), BROKEN_RAFT),
RIVER_LUM_GRAPPLE_EAST(8, "Grapple Broken Raft", new WorldPoint(3258, 3179, 0), BROKEN_RAFT),
CORSAIR_COVE_ROCKS(10, "Rocks", new WorldPoint(2545, 2871, 0), ROCKS_31757),
@@ -86,7 +97,7 @@ public enum AgilityShortcut
GOBLIN_VILLAGE_WALL(14, "Wall", new WorldPoint(2925, 3523, 0), TIGHTGAP),
CORSAIR_COVE_DUNGEON_PILLAR(15, "Pillar Jump", new WorldPoint(1980, 8996, 0), PILLAR_31809),
EDGEVILLE_DUNGEON_MONKEYBARS(15, "Monkey Bars", null, MONKEYBARS_23566),
TROLLHEIM_ROCKS(15, "Rocks", null, new WorldPoint(2838, 3614, 0), ROCKS_3748), // No fixed world map location, but rocks near death plateau have a requirement of 15
TROLLHEIM_ROCKS(15, "Rocks", null, new WorldPoint(2838, 3614, 0), ROCKS_3748), // No fixed world map location, but rocks near death plateau have a requirement of 15
YANILLE_UNDERWALL_TUNNEL(16, "Underwall Tunnel", new WorldPoint(2574, 3109, 0), HOLE_16520, CASTLE_WALL),
YANILLE_WATCHTOWER_TRELLIS(18, "Trellis", null, TRELLIS_20056),
COAL_TRUCKS_LOG_BALANCE(20, "Log Balance", new WorldPoint(2598, 3475, 0), LOG_BALANCE_23274),
@@ -143,7 +154,7 @@ public enum AgilityShortcut
ISAFDAR_FOREST_OBSTACLES(56, "Trap", null, DENSE_FOREST_3938, DENSE_FOREST_3939, DENSE_FOREST_3998, DENSE_FOREST_3999, DENSE_FOREST, LEAVES, LEAVES_3924, LEAVES_3925, STICKS, TRIPWIRE, TRIPWIRE_3921),
RELEKKA_EAST_FENCE(57, "Fence", new WorldPoint(2688, 3697, 0), BROKEN_FENCE),
YANILLE_DUNGEON_MONKEY_BARS(57, "Monkey Bars", null, MONKEYBARS_23567),
PHASMATYS_ECTOPOOL_SHORTCUT(58, "Weathered Wall", null , WEATHERED_WALL, WEATHERED_WALL_16526),
PHASMATYS_ECTOPOOL_SHORTCUT(58, "Weathered Wall", null, WEATHERED_WALL, WEATHERED_WALL_16526),
ELVEN_OVERPASS_CLIFF_SCRAMBLE(59, "Rocks", new WorldPoint(2345, 3300, 0), ROCKS_16514, ROCKS_16515),
ELVEN_OVERPASS_CLIFF_SCRAMBLE_PRIFDDINAS(59, "Rocks", new WorldPoint(3369, 6052, 0), ROCKS_16514, ROCKS_16515),
WILDERNESS_GWD_CLIMB_EAST(60, "Rocks", new WorldPoint(2943, 3770, 0), ROCKY_HANDHOLDS_26400, ROCKY_HANDHOLDS_26401, ROCKY_HANDHOLDS_26402, ROCKY_HANDHOLDS_26404, ROCKY_HANDHOLDS_26405, ROCKY_HANDHOLDS_26406),
@@ -173,16 +184,16 @@ public enum AgilityShortcut
TAVERLEY_DUNGEON_PIPE_BLUE_DRAGON(70, "Pipe Squeeze", new WorldPoint(2886, 9798, 0), OBSTACLE_PIPE_16509),
TAVERLEY_DUNGEON_ROCKS_NORTH(70, "Rocks", new WorldPoint(2887, 9823, 0), ROCKS, ROCKS_14106),
TAVERLEY_DUNGEON_ROCKS_SOUTH(70, "Rocks", new WorldPoint(2887, 9631, 0), ROCKS, ROCKS_14106),
FOSSIL_ISLAND_HARDWOOD_NORTH(70, "Hole" , new WorldPoint(3713, 3827, 0), HOLE_31481, HOLE_31482),
FOSSIL_ISLAND_HARDWOOD_SOUTH(70, "Hole" , new WorldPoint(3715, 3817, 0), HOLE_31481, HOLE_31482),
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_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 ),
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),

View File

@@ -36,6 +36,7 @@ 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);

View File

@@ -45,7 +45,7 @@ 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.eventbus.EventBus;
import net.runelite.client.util.ImageUtil;
import net.runelite.client.util.Text;
@@ -98,10 +98,17 @@ public class ClanManager
private int offset;
@Inject
private ClanManager(Client client, SpriteManager spriteManager)
private ClanManager(
final Client client,
final SpriteManager spriteManager,
final EventBus eventbus
)
{
this.client = client;
this.spriteManager = spriteManager;
eventbus.subscribe(GameStateChanged.class, this, this::onGameStateChanged);
eventbus.subscribe(ClanChanged.class, this, this::onClanChanged);
}
public ClanMemberRank getRank(String playerName)
@@ -125,8 +132,7 @@ public class ClanManager
return offset + clanMemberRank.ordinal() - 1;
}
@Subscribe
public void onGameStateChanged(GameStateChanged gameStateChanged)
private void onGameStateChanged(GameStateChanged gameStateChanged)
{
if (gameStateChanged.getGameState() == GameState.LOGIN_SCREEN && offset == 0)
{
@@ -134,8 +140,7 @@ public class ClanManager
}
}
@Subscribe
public void onClanChanged(ClanChanged clanChanged)
private void onClanChanged(ClanChanged clanChanged)
{
clanRanksCache.invalidateAll();
}

View File

@@ -36,6 +36,7 @@ import static net.runelite.client.game.HiscoreManager.NONE;
import net.runelite.http.api.hiscore.HiscoreClient;
import net.runelite.http.api.hiscore.HiscoreEndpoint;
import net.runelite.http.api.hiscore.HiscoreResult;
import org.jetbrains.annotations.NotNull;
@Slf4j
class HiscoreLoader extends CacheLoader<HiscoreManager.HiscoreKey, HiscoreResult>
@@ -50,7 +51,7 @@ class HiscoreLoader extends CacheLoader<HiscoreManager.HiscoreKey, HiscoreResult
}
@Override
public HiscoreResult load(HiscoreManager.HiscoreKey hiscoreKey) throws Exception
public HiscoreResult load(@NotNull HiscoreManager.HiscoreKey hiscoreKey) throws Exception
{
return EMPTY;
}

View File

@@ -28,9 +28,14 @@ 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 com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import io.reactivex.schedulers.Schedulers;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -47,50 +52,25 @@ import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.Constants;
import static net.runelite.api.Constants.CLIENT_DEFAULT_ZOOM;
import static net.runelite.api.Constants.HIGH_ALCHEMY_MULTIPLIER;
import net.runelite.api.GameState;
import net.runelite.api.ItemComposition;
import net.runelite.api.ItemDefinition;
import net.runelite.api.ItemID;
import static net.runelite.api.ItemID.*;
import net.runelite.api.SpritePixels;
import net.runelite.api.Sprite;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.PostItemComposition;
import net.runelite.api.events.PostItemDefinition;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.eventbus.EventBus;
import net.runelite.http.api.item.ItemClient;
import net.runelite.http.api.item.ItemPrice;
import net.runelite.http.api.item.ItemStats;
import org.jetbrains.annotations.NotNull;
@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).
@@ -153,9 +133,24 @@ public class ItemManager
put(AGILITY_CAPET_13341, AGILITY_CAPET).
put(AGILITY_CAPE_13340, AGILITY_CAPE).
build();
private final Client client;
private final ScheduledExecutorService scheduledExecutorService;
private final ClientThread clientThread;
private final ItemClient itemClient = new ItemClient();
private final ImmutableMap<Integer, ItemStats> itemStatMap;
private final LoadingCache<ImageKey, AsyncBufferedImage> itemImages;
private final LoadingCache<Integer, ItemDefinition> itemDefinitions;
private final LoadingCache<OutlineKey, BufferedImage> itemOutlines;
private Map<Integer, ItemPrice> itemPrices = Collections.emptyMap();
private Map<Integer, ItemStats> itemStats = Collections.emptyMap();
@Inject
public ItemManager(Client client, ScheduledExecutorService executor, ClientThread clientThread)
public ItemManager(
Client client,
ScheduledExecutorService executor,
ClientThread clientThread,
EventBus eventbus
)
{
this.client = client;
this.scheduledExecutorService = executor;
@@ -170,19 +165,19 @@ public class ItemManager
.build(new CacheLoader<ImageKey, AsyncBufferedImage>()
{
@Override
public AsyncBufferedImage load(ImageKey key) throws Exception
public AsyncBufferedImage load(@NotNull ImageKey key) throws Exception
{
return loadImage(key.itemId, key.itemQuantity, key.stackable);
}
});
itemCompositions = CacheBuilder.newBuilder()
itemDefinitions = CacheBuilder.newBuilder()
.maximumSize(1024L)
.expireAfterAccess(1, TimeUnit.HOURS)
.build(new CacheLoader<Integer, ItemComposition>()
.build(new CacheLoader<Integer, ItemDefinition>()
{
@Override
public ItemComposition load(Integer key) throws Exception
public ItemDefinition load(@NotNull Integer key) throws Exception
{
return client.getItemDefinition(key);
}
@@ -194,77 +189,88 @@ public class ItemManager
.build(new CacheLoader<OutlineKey, BufferedImage>()
{
@Override
public BufferedImage load(OutlineKey key) throws Exception
public BufferedImage load(@NotNull OutlineKey key) throws Exception
{
return loadItemOutline(key.itemId, key.itemQuantity, key.outlineColor);
}
});
final Gson gson = new Gson();
final Type typeToken = new TypeToken<Map<Integer, ItemStats>>()
{
}.getType();
final InputStream statsFile = getClass().getResourceAsStream("/item_stats.json");
final Map<Integer, ItemStats> stats = gson.fromJson(new InputStreamReader(statsFile), typeToken);
itemStatMap = ImmutableMap.copyOf(stats);
eventbus.subscribe(GameStateChanged.class, this, this::onGameStateChanged);
eventbus.subscribe(PostItemDefinition.class, this, this::onPostItemDefinition);
}
private void loadPrices()
{
try
{
ItemPrice[] prices = itemClient.getPrices();
if (prices != null)
{
ImmutableMap.Builder<Integer, ItemPrice> map = ImmutableMap.builderWithExpectedSize(prices.length);
for (ItemPrice price : prices)
itemClient.getPrices()
.subscribeOn(Schedulers.io())
.subscribe(
(prices) ->
{
map.put(price.getId(), price);
}
itemPrices = map.build();
}
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);
}
log.debug("Loaded {} prices", itemPrices.size());
},
(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);
}
itemClient.getStats()
.subscribeOn(Schedulers.io())
.subscribe(
(stats) ->
{
if (stats != null)
{
itemStats = ImmutableMap.copyOf(stats);
}
log.debug("Loaded {} stats", itemStats.size());
}
catch (IOException e)
{
log.warn("error loading stats!", e);
}
log.debug("Loaded {} stats", itemStats.size());
},
(e) -> log.warn("error loading stats!", e)
);
}
@Subscribe
public void onGameStateChanged(final GameStateChanged event)
private void onGameStateChanged(final GameStateChanged event)
{
if (event.getGameState() == GameState.HOPPING || event.getGameState() == GameState.LOGIN_SCREEN)
{
itemCompositions.invalidateAll();
itemDefinitions.invalidateAll();
}
}
@Subscribe
public void onPostItemComposition(PostItemComposition event)
private void onPostItemDefinition(PostItemDefinition event)
{
itemCompositions.put(event.getItemComposition().getId(), event.getItemComposition());
itemDefinitions.put(event.getItemDefinition().getId(), event.getItemDefinition());
}
/**
* Invalidates internal item manager item composition cache (but not client item composition cache)
* @see Client#getItemCompositionCache()
*
* @see Client#getItemDefinitionCache()
*/
public void invalidateItemCompositionCache()
public void invalidateItemDefinitionCache()
{
itemCompositions.invalidateAll();
itemDefinitions.invalidateAll();
}
/**
@@ -281,7 +287,7 @@ public class ItemManager
/**
* Look up an item's price
*
* @param itemID item id
* @param itemID item id
* @param ignoreUntradeableMap should the price returned ignore the {@link UntradeableItemMapping}
* @return item price
*/
@@ -318,22 +324,63 @@ public class ItemManager
return price;
}
public int getAlchValue(ItemDefinition composition)
{
if (composition.getId() == ItemID.COINS_995)
{
return 1;
}
if (composition.getId() == ItemID.PLATINUM_TOKEN)
{
return 1000;
}
return (int) Math.max(1, composition.getPrice() * HIGH_ALCHEMY_MULTIPLIER);
}
public int getAlchValue(int itemID)
{
if (itemID == ItemID.COINS_995)
{
return 1;
}
if (itemID == ItemID.PLATINUM_TOKEN)
{
return 1000;
}
return (int) Math.max(1, getItemDefinition(itemID).getPrice() * HIGH_ALCHEMY_MULTIPLIER);
}
public int getBrokenValue(int itemId)
{
PvPValueBrokenItem b = PvPValueBrokenItem.of(itemId);
if (b != null)
{
return (int) (b.getValue() * (75.0f / 100.0f));
}
return 0;
}
/**
* Look up an item's stats
*
* @param itemId item id
* @return item stats
*/
@Nullable
public ItemStats getItemStats(int itemId, boolean allowNote)
{
ItemComposition itemComposition = getItemComposition(itemId);
ItemDefinition itemDefinition = getItemDefinition(itemId);
if (itemComposition == null || itemComposition.getName() == null || (!allowNote && itemComposition.getNote() != -1))
if (itemDefinition.getName() == null || !allowNote && itemDefinition.getNote() != -1)
{
return null;
}
return itemStats.get(canonicalize(itemId));
return itemStatMap.get(canonicalize(itemId));
}
/**
@@ -365,10 +412,10 @@ public class ItemManager
* @return item composition
*/
@Nonnull
public ItemComposition getItemComposition(int itemId)
public ItemDefinition getItemDefinition(int itemId)
{
assert client.isClientThread() : "getItemComposition must be called on client thread";
return itemCompositions.getUnchecked(itemId);
assert client.isClientThread() : "getItemDefinition must be called on client thread";
return itemDefinitions.getUnchecked(itemId);
}
/**
@@ -376,16 +423,16 @@ public class ItemManager
*/
public int canonicalize(int itemID)
{
ItemComposition itemComposition = getItemComposition(itemID);
ItemDefinition itemDefinition = getItemDefinition(itemID);
if (itemComposition.getNote() != -1)
if (itemDefinition.getNote() != -1)
{
return itemComposition.getLinkedNoteId();
return itemDefinition.getLinkedNoteId();
}
if (itemComposition.getPlaceholderTemplateId() != -1)
if (itemDefinition.getPlaceholderTemplateId() != -1)
{
return itemComposition.getPlaceholderId();
return itemDefinition.getPlaceholderId();
}
return WORN_ITEMS.getOrDefault(itemID, itemID);
@@ -406,7 +453,7 @@ public class ItemManager
{
return false;
}
SpritePixels sprite = client.createItemSprite(itemId, quantity, 1, SpritePixels.DEFAULT_SHADOW_COLOR,
Sprite sprite = client.createItemSprite(itemId, quantity, 1, Sprite.DEFAULT_SHADOW_COLOR,
stackable ? 1 : 0, false, CLIENT_DEFAULT_ZOOM);
if (sprite == null)
{
@@ -460,21 +507,21 @@ public class ItemManager
/**
* Create item sprite and applies an outline.
*
* @param itemId item id
* @param itemId item id
* @param itemQuantity item quantity
* @param outlineColor outline color
* @return image
*/
private BufferedImage loadItemOutline(final int itemId, final int itemQuantity, final Color outlineColor)
{
final SpritePixels itemSprite = client.createItemSprite(itemId, itemQuantity, 1, 0, 0, true, 710);
final Sprite 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 itemId item id
* @param itemQuantity item quantity
* @param outlineColor outline color
* @return image
@@ -490,4 +537,20 @@ public class ItemManager
return null;
}
}
@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;
}
}

View File

@@ -286,4 +286,9 @@ public enum ItemMapping
return mapping.iterator().next();
}
}
public static boolean isMapped(int itemId)
{
return MAPPINGS.containsValue(itemId);
}
}

View File

@@ -0,0 +1,27 @@
package net.runelite.client.game;
import lombok.Value;
@Value
public class ItemStat
{
private int slot;
private int astab;
private int aslash;
private int acrush;
private int amagic;
private int arange;
private int dstab;
private int dslash;
private int dcrush;
private int dmagic;
private int drange;
private int str;
private int rstr;
private int mdmg;
private int prayer;
private int aspeed;
}

View File

@@ -54,7 +54,6 @@ 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;
@@ -74,14 +73,24 @@ public class LootManager
private WorldPoint krakenPlayerLocation;
@Inject
private LootManager(EventBus eventBus, Client client)
private LootManager(
final EventBus eventBus,
final Client client
)
{
this.eventBus = eventBus;
this.client = client;
eventBus.subscribe(GameTick.class, this, this::onGameTick);
eventBus.subscribe(NpcDespawned.class, this, this::onNpcDespawned);
eventBus.subscribe(PlayerDespawned.class, this, this::onPlayerDespawned);
eventBus.subscribe(ItemSpawned.class, this, this::onItemSpawned);
eventBus.subscribe(ItemDespawned.class, this, this::onItemDespawned);
eventBus.subscribe(ItemQuantityChanged.class, this, this::onItemQuantityChanged);
eventBus.subscribe(AnimationChanged.class, this, this::onAnimationChanged);
}
@Subscribe
public void onNpcDespawned(NpcDespawned npcDespawned)
private void onNpcDespawned(NpcDespawned npcDespawned)
{
final NPC npc = npcDespawned.getNpc();
if (!npc.isDead())
@@ -123,8 +132,7 @@ public class LootManager
processNpcLoot(npc);
}
@Subscribe
public void onPlayerDespawned(PlayerDespawned playerDespawned)
private void onPlayerDespawned(PlayerDespawned playerDespawned)
{
final Player player = playerDespawned.getPlayer();
// Only care about dead Players
@@ -150,11 +158,10 @@ public class LootManager
}
killPoints.add(location);
eventBus.post(new PlayerLootReceived(player, items));
eventBus.post(PlayerLootReceived.class, new PlayerLootReceived(player, items));
}
@Subscribe
public void onItemSpawned(ItemSpawned itemSpawned)
private void onItemSpawned(ItemSpawned itemSpawned)
{
final TileItem item = itemSpawned.getItem();
final Tile tile = itemSpawned.getTile();
@@ -164,16 +171,14 @@ public class LootManager
log.debug("Item spawn {} ({}) location {}", item.getId(), item.getQuantity(), location);
}
@Subscribe
public void onItemDespawned(ItemDespawned itemDespawned)
private void onItemDespawned(ItemDespawned itemDespawned)
{
final TileItem item = itemDespawned.getItem();
final LocalPoint location = itemDespawned.getTile().getLocalLocation();
log.debug("Item despawn {} ({}) location {}", item.getId(), item.getQuantity(), location);
}
@Subscribe
public void onItemQuantityChanged(ItemQuantityChanged itemQuantityChanged)
private void onItemQuantityChanged(ItemQuantityChanged itemQuantityChanged)
{
final TileItem item = itemQuantityChanged.getItem();
final Tile tile = itemQuantityChanged.getTile();
@@ -189,8 +194,7 @@ public class LootManager
itemSpawns.put(packed, new ItemStack(item.getId(), diff, location));
}
@Subscribe
public void onAnimationChanged(AnimationChanged e)
private void onAnimationChanged(AnimationChanged e)
{
if (!(e.getActor() instanceof NPC))
{
@@ -219,8 +223,7 @@ public class LootManager
}
}
@Subscribe
public void onGameTick(GameTick gameTick)
private void onGameTick(GameTick gameTick)
{
playerLocationLastTick = client.getLocalPlayer().getWorldLocation();
itemSpawns.clear();
@@ -237,7 +240,7 @@ public class LootManager
final int x = location.getSceneX();
final int y = location.getSceneY();
final int size = npc.getComposition().getSize();
final int size = npc.getDefinition().getSize();
// Some NPCs drop items onto multiple tiles
final List<ItemStack> allItems = new ArrayList<>();
@@ -257,7 +260,7 @@ public class LootManager
}
killPoints.add(location);
eventBus.post(new NpcLootReceived(npc, allItems));
eventBus.post(NpcLootReceived.class, new NpcLootReceived(npc, allItems));
}
private WorldPoint getDropLocation(NPC npc, WorldPoint worldLocation)

View File

@@ -1,5 +1,6 @@
/*
* 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
@@ -24,6 +25,7 @@
*/
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;
@@ -33,33 +35,88 @@ 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 Map<String, Integer> healthMap;
private final ImmutableMap<Integer, NPCStats> statsMap;
@Inject
private NPCManager()
{
final Gson gson = new Gson();
final Type typeToken = new TypeToken<Map<String, Integer>>()
final Type typeToken = new TypeToken<Map<Integer, NPCStats>>()
{
}.getType();
final InputStream healthFile = getClass().getResourceAsStream("/npc_health.json");
healthMap = gson.fromJson(new InputStreamReader(healthFile), typeToken);
final InputStream statsFile = getClass().getResourceAsStream("/npc_stats.json");
final Map<Integer, NPCStats> stats = gson.fromJson(new InputStreamReader(statsFile), typeToken);
statsMap = ImmutableMap.copyOf(stats);
}
/**
* Returns health for target NPC based on it's combat level and name
* @param name npc name
* @param combatLevel npc combat level
* @return health or null if HP is unknown
* Returns the {@link NPCStats} for target NPC id
*
* @param npcId NPC id
* @return the {@link NPCStats} or null if unknown
*/
@Nullable
public Integer getHealth(final String name, final int combatLevel)
public NPCStats getStats(final int npcId)
{
return healthMap.get(name + "_" + combatLevel);
return statsMap.get(npcId);
}
/**
* Returns health for target NPC ID
*
* @param npcId NPC id
* @return health or null if unknown
*/
public int getHealth(final int npcId)
{
final NPCStats s = statsMap.get(npcId);
if (s == null || s.getHitpoints() == -1)
{
return -1;
}
return s.getHitpoints();
}
/**
* Returns the attack speed for target NPC ID.
*
* @param npcId NPC id
* @return attack speed in game ticks for NPC ID.
*/
public int getAttackSpeed(final int npcId)
{
final NPCStats s = statsMap.get(npcId);
if (s == null || s.getAttackSpeed() == -1)
{
return -1;
}
return s.getAttackSpeed();
}
/**
* Returns the exp modifier for target NPC ID based on its stats.
*
* @param npcId NPC id
* @return npcs exp modifier. Assumes default xp rate if npc stats are unknown (returns 1)
*/
public double getXpModifier(final int npcId)
{
final NPCStats s = statsMap.get(npcId);
if (s == null)
{
return 1;
}
return s.calculateXpModifier();
}
}

View File

@@ -0,0 +1,80 @@
/*
* 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 attackSpeed;
private final int attackLevel;
private final int strengthLevel;
private final int defenceLevel;
private final int rangeLevel;
private final int magicLevel;
private final int stab;
private final int slash;
private final int crush;
private final int range;
private final int magic;
private final int stabDef;
private final int slashDef;
private final int crushDef;
private final int rangeDef;
private final int magicDef;
private final int bonusAttack;
private final int bonusStrength;
private final int bonusRangeStrength;
private final int bonusMagicDamage;
private final boolean poisonImmune;
private final boolean venomImmune;
private final boolean dragon;
private final boolean demon;
private final boolean undead;
/**
* Based off the formula found here: http://services.runescape.com/m=forum/c=PLuJ4cy6gtA/forums.ws?317,318,712,65587452,209,337584542#209
* @return bonus XP modifier
*/
public double calculateXpModifier()
{
final double averageLevel = Math.floor((attackLevel + strengthLevel + defenceLevel + hitpoints) / 4);
final double averageDefBonus = Math.floor((stabDef + slashDef + crushDef) / 3);
return (1 + Math.floor(averageLevel * (averageDefBonus + bonusStrength + bonusAttack) / 5120) / 40);
}
}

View File

@@ -0,0 +1,122 @@
/*
* 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 javax.annotation.Nullable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import net.runelite.api.ItemID;
/**
* Some non tradeable items are kept on death inside low level wilderness (1-20) but are turned into a broken variant.
* <p>
* The non-broken variant will be shown inside the interface.
*/
@AllArgsConstructor
@Getter
public enum PvPValueBrokenItem
{
// Capes
FIRE_CAPE(ItemID.FIRE_CAPE, 50000),
FIRE_MAX_CAPE(ItemID.FIRE_MAX_CAPE, 50000),
INFERNAL_CAPE(ItemID.INFERNAL_CAPE, 50000),
INFERNAL_MAX_CAPE(ItemID.INFERNAL_MAX_CAPE, 50000),
AVAS_ASSEMBLER(ItemID.AVAS_ASSEMBLER, 75000),
ASSEMBLER_MAX_CAPE(ItemID.ASSEMBLER_MAX_CAPE, 75000),
// Defenders
BRONZE_DEFENDER(ItemID.BRONZE_DEFENDER, 1000),
IRON_DEFENDER(ItemID.IRON_DEFENDER, 2000),
STEEL_DEFENDER(ItemID.STEEL_DEFENDER, 2500),
BLACK_DEFENDER(ItemID.BLACK_DEFENDER, 5000),
MITHRIL_DEFENDER(ItemID.MITHRIL_DEFENDER, 15000),
ADAMANT_DEFENDER(ItemID.ADAMANT_DEFENDER, 25000),
RUNE_DEFENDER(ItemID.RUNE_DEFENDER, 35000),
DRAGON_DEFENDER(ItemID.DRAGON_DEFENDER, 40000),
AVERNIC_DEFENDER(ItemID.AVERNIC_DEFENDER, 1000000),
// Void
VOID_MAGE_HELM(ItemID.VOID_MAGE_HELM, 40000),
VOID_RANGER_HELM(ItemID.VOID_RANGER_HELM, 40000),
VOID_MELEE_HELM(ItemID.VOID_MELEE_HELM, 40000),
VOID_KNIGHT_TOP(ItemID.VOID_KNIGHT_TOP, 45000),
VOID_KNIGHT_ROBE(ItemID.VOID_KNIGHT_ROBE, 45000),
VOID_KNIGHT_GLOVES(ItemID.VOID_KNIGHT_GLOVES, 30000),
ELITE_VOID_TOP(ItemID.ELITE_VOID_TOP, 50000),
ELITE_VOID_ROBE(ItemID.ELITE_VOID_ROBE, 50000),
// Barb Assault
FIGHTER_HAT(ItemID.FIGHTER_HAT, 45000),
RANGER_HAT(ItemID.RANGER_HAT, 45000),
HEALER_HAT(ItemID.HEALER_HAT, 45000),
FIGHTER_TORSO(ItemID.FIGHTER_TORSO, 50000),
PENANCE_SKIRT(ItemID.PENANCE_SKIRT, 20000),
// Castle Wars
SARADOMIN_HALO(ItemID.SARADOMIN_HALO, 25000),
ZAMORAK_HALO(ItemID.ZAMORAK_HALO, 25000),
GUTHIX_HALO(ItemID.GUTHIX_HALO, 25000),
DECORATIVE_MAGIC_HAT(ItemID.DECORATIVE_ARMOUR_11898, 5000),
DECORATIVE_MAGIC_ROBE_TOP(ItemID.DECORATIVE_ARMOUR_11896, 5000),
DECORATIVE_MAGIC_ROBE_LEGS(ItemID.DECORATIVE_ARMOUR_11897, 5000),
DECORATIVE_RANGE_TOP(ItemID.DECORATIVE_ARMOUR_11899, 5000),
DECORATIVE_RANGE_BOTTOM(ItemID.DECORATIVE_ARMOUR_11900, 5000),
DECORATIVE_RANGE_QUIVER(ItemID.DECORATIVE_ARMOUR_11901, 5000),
GOLD_DECORATIVE_HELM(ItemID.DECORATIVE_HELM_4511, 5000),
GOLD_DECORATIVE_BODY(ItemID.DECORATIVE_ARMOUR_4509, 5000),
GOLD_DECORATIVE_LEGS(ItemID.DECORATIVE_ARMOUR_4510, 5000),
GOLD_DECORATIVE_SKIRT(ItemID.DECORATIVE_ARMOUR_11895, 5000),
GOLD_DECORATIVE_SHIELD(ItemID.DECORATIVE_SHIELD_4512, 5000),
GOLD_DECORATIVE_SWORD(ItemID.DECORATIVE_SWORD_4508, 5000);
private static final ImmutableMap<Integer, PvPValueBrokenItem> idMap;
static
{
ImmutableMap.Builder<Integer, PvPValueBrokenItem> builder = ImmutableMap.builder();
for (PvPValueBrokenItem items : values())
{
builder.put(items.itemID, items);
}
idMap = builder.build();
}
private final int itemID;
private final int value;
@Nullable
public static PvPValueBrokenItem of(int itemId)
{
return idMap.get(itemId);
}
public static boolean breaksOnDeath(int itemId)
{
return idMap.containsKey(itemId);
}
}

View File

@@ -40,7 +40,7 @@ public class SkillIconManager
public BufferedImage getSkillImage(Skill skill, boolean small)
{
int skillIdx = skill.ordinal() + (small ? Skill.values().length : 0);
BufferedImage skillImage = null;
BufferedImage skillImage;
if (imgCache[skillIdx] != null)
{

View File

@@ -0,0 +1,42 @@
package net.runelite.client.game;
public enum Sound
{
FIFTEEN_SECONDS(1, "net/runelite/client/game/sounds/15seconds.wav"),
FIVE_SECONDS(2, "net/runelite/client/game/sounds/5seconds.wav"),
ATTACK_WITH_MAGIC(3, "net/runelite/client/game/sounds/attackmagic.wav"),
ATTACK_WITH_MELEE(4, "net/runelite/client/game/sounds/attackmelee.wav"),
ATTACK_WITH_RANGED(5, "net/runelite/client/game/sounds/attackranged.wav"),
INCOMING(6, "net/runelite/client/game/sounds/incoming.wav"),
MOVE(7, "net/runelite/client/game/sounds/move.wav"),
PRAY_MAGIC(8, "net/runelite/client/game/sounds/praymagic.wav"),
PRAY_MELEE(9, "net/runelite/client/game/sounds/praymelee.wav"),
PRAY_RANGED(10, "net/runelite/client/game/sounds/prayranged.wav"),
REENABLE_PRAYER(11, "net/runelite/client/game/sounds/reenableprayer.wav"),
RUNAWAY(12, "net/runelite/client/game/sounds/runaway.wav"),
LOW_HEATLH(13, "net/runelite/client/game/sounds/lowhealth.wav"),
LOW_PRAYER(14, "net/runelite/client/game/sounds/lowprayer.wav"),
OUT_OF_COMBAT(15, "net/runelite/client/game/sounds/outofcombat.wav"),
RESTORED_SPECIAL_ATTACK(16, "net/runelite/client/game/sounds/restorespec.wav"),
IDLE(17, "net/runelite/client/game/sounds/idle.wav"),
BREAK(18, "net/runelite/client/game/sounds/break.wav");
private final String filePath;
private final int id;
Sound(int id, String filePath)
{
this.id = id;
this.filePath = filePath;
}
public String getFilePath()
{
return this.filePath;
}
public int getId()
{
return this.id;
}
}

View File

@@ -0,0 +1,95 @@
package net.runelite.client.game;
import com.google.inject.Inject;
import java.io.IOException;
import javax.inject.Singleton;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.BooleanControl;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;
import net.runelite.client.config.RuneLiteConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class SoundManager
{
private static final Logger log = LoggerFactory.getLogger(SoundManager.class);
private final RuneLiteConfig runeliteConfig;
@Inject
private SoundManager(RuneLiteConfig runeLiteConfig)
{
this.runeliteConfig = runeLiteConfig;
}
public void playSound(final Sound sound)
{
new Thread(new Runnable()
{
@Override
public void run()
{
try
{
AudioInputStream in = AudioSystem.getAudioInputStream(this.getClass().getClassLoader().getResource(sound.getFilePath()));
AudioFormat outFormat = SoundManager.this.getOutFormat(in.getFormat());
DataLine.Info info = new DataLine.Info(SourceDataLine.class, outFormat);
SourceDataLine line = (SourceDataLine)AudioSystem.getLine(info);
if (line != null)
{
line.open(outFormat, 2200);
if (line.isControlSupported(FloatControl.Type.MASTER_GAIN))
{
int volume = SoundManager.this.runeliteConfig.volume();
FloatControl gainControl = (FloatControl)line.getControl(FloatControl.Type.MASTER_GAIN);
BooleanControl muteControl = (BooleanControl)line.getControl(BooleanControl.Type.MUTE);
if (volume == 0)
{
muteControl.setValue(true);
}
else
{
muteControl.setValue(false);
gainControl.setValue((float)(Math.log((double)volume / 100.0) / Math.log(10.0) * 20.0));
}
}
line.start();
SoundManager.this.stream(AudioSystem.getAudioInputStream(outFormat, in), line);
line.drain();
line.stop();
}
}
catch (IOException | LineUnavailableException | UnsupportedAudioFileException e)
{
throw new IllegalStateException(e);
}
}
}).start();
}
private AudioFormat getOutFormat(AudioFormat inFormat)
{
int ch = inFormat.getChannels();
float rate = inFormat.getSampleRate();
return new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, rate, 16, ch, ch * 2, rate, false);
}
private void stream(AudioInputStream in, SourceDataLine line) throws IOException
{
byte[] buffer = new byte[2200];
int n = 0;
while (n != -1)
{
line.write(buffer, 0, n);
n = in.read(buffer, 0, buffer.length);
}
}
}

View File

@@ -39,7 +39,7 @@ import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.SpritePixels;
import net.runelite.api.Sprite;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.ui.overlay.infobox.InfoBox;
import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
@@ -78,7 +78,7 @@ public class SpriteManager
return cached;
}
SpritePixels[] sp = client.getSprites(client.getIndexSprites(), archive, 0);
Sprite[] sp = client.getSprites(client.getIndexSprites(), archive, 0);
if (sp == null)
{
return null;
@@ -127,12 +127,8 @@ public class SpriteManager
public void addSpriteTo(JButton c, int archive, int file)
{
getSpriteAsync(archive, file, img ->
{
SwingUtilities.invokeLater(() ->
{
c.setIcon(new ImageIcon(img));
});
});
c.setIcon(new ImageIcon(img))));
}
/**
@@ -141,12 +137,8 @@ public class SpriteManager
public void addSpriteTo(JLabel c, int archive, int file)
{
getSpriteAsync(archive, file, img ->
{
SwingUtilities.invokeLater(() ->
{
c.setIcon(new ImageIcon(img));
});
});
c.setIcon(new ImageIcon(img))));
}
public void addSpriteOverrides(SpriteOverride[] add)
@@ -158,12 +150,12 @@ public class SpriteManager
clientThread.invokeLater(() ->
{
Map<Integer, SpritePixels> overrides = client.getSpriteOverrides();
Map<Integer, Sprite> 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);
Sprite sp = ImageUtil.getImageSprite(image, client);
overrides.put(o.getSpriteId(), sp);
}
});
@@ -173,7 +165,7 @@ public class SpriteManager
{
clientThread.invokeLater(() ->
{
Map<Integer, SpritePixels> overrides = client.getSpriteOverrides();
Map<Integer, Sprite> overrides = client.getSpriteOverrides();
for (SpriteOverride o : remove)
{
overrides.remove(o.getSpriteId());

View File

@@ -24,14 +24,10 @@
*/
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();

View File

@@ -0,0 +1,335 @@
/*******************************************************************************
* Copyright (c) 2019 RuneLitePlus
* Redistributions and modifications of this software are permitted as long as this notice remains in its original unmodified state at the top of this file.
* If there are any questions comments, or feedback about this software, please direct all inquiries directly to the file authors:
* ST0NEWALL#9112
* RuneLitePlus Discord: https://discord.gg/Q7wFtCe
* RuneLitePlus website: https://runelitepl.us
******************************************************************************/
package net.runelite.client.game;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import lombok.Getter;
import net.runelite.api.coords.WorldArea;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.util.PvPUtil;
public enum WorldLocation
{
// Non-Wilderness Locations
AGILITY_PYRAMID("Agility Pyramid", new Location(3334, 2864, 3386, 2819), 0),
AL_KHARID_BANK("Al Kharid Bank", new Location(3265, 3173, 3272, 3161), 0),
AL_KHARID_GLIDER("Al Kharid_Glider", new Location(3276, 3214, 3283, 3209), 0),
AL_KHARID_PALACE("Al Kharid Palace", new Location(3281, 3177, 3304, 3158), 0),
ZUL_ANDRA("Zul-Andra", new Location(2182, 3070, 2214, 3042), 0),
APE_ATOLL_TEMPLE("Ape Atoll Temple", new Location(2784, 2802, 2810, 2770), 0),
ARDY_CASTLE("Ardy Castle", new Location(2567, 3311, 2591, 3283), 0),
ARDY_DOCKS("Ardy Docks", new Location(2660, 3284, 2689, 3264), 0),
ARDY_NORTH_BANK("Ardy North Bank", new Location(2611, 3336, 2622, 3329), 0),
ARDY_SOUTH_BANK("Ardy South Bank", new Location(2645, 3288, 2659, 3279), 0),
ARDY_STALLS("Ardy Stalls", new Location(2651, 3318, 2673, 3293), 0),
ARDY_ZOO("Ardy Zoo", new Location(2598, 3288, 2640, 3261), 0),
BARB_VILLAGE("Barb Village", new Location(3071, 3448, 3092, 3405), 0),
BARROWS("Barrows", new Location(3546, 3314, 3583, 3268), 0),
BEDABIN_CAMP("Bedabin Camp", new Location(3157, 3052, 3188, 3019), 0),
CAMELOT_CASTLE("Camelot Castle", new Location(2743, 3481, 2775, 3468), 0),
CASTLE_WARS("Castle Wars", new Location(2435, 3127, 2474, 3074), 0),
CATHERBY("Catherby", new Location(2791, 3457, 2833, 3436), 0),
CATHERBY_DOCKS("Catherby Docks", new Location(2790, 3432, 2808, 3409), 0),
CATHERBY_EAST("Catherby East", new Location(2834, 3441, 2862, 3425), 0),
CATHERBY_NORTH("Catherby North", new Location(2791, 3472, 2833, 3458), 0),
CLAN_WARS("Clan Wars", new Location(3344, 3176, 3391, 3142), 0),
COOKS_GUILD("Cooks Guild", new Location(3135, 3455, 3155, 3427), 0),
COX("CoX", new Location(1226, 3574, 1270, 3559), 0),
CRAB_CLAW_ISLE("Crab Claw Isle", new Location(1745, 3449, 1795, 3399), 0),
DESERT_BANDIT_CAMP("Desert Bandit Camp", new Location(3154, 2993, 3189, 2963), 0),
DIGSITE_NORTHWEST("Digsite NorthWest", new Location(3325, 3445, 3353, 3420), 0),
DIGSITE_NORTHEAST("Digsite NorthEast", new Location(3354, 3445, 3382, 3420), 0),
DIGSITE_SOUTHEAST("Digsite SouthEast", new Location(3354, 3419, 3382, 3393), 0),
DIGSITE_SOUTHWEST("Digsite SouthWest", new Location(3325, 3419, 3353, 3393), 0),
DRYANOR_VILLAGE("Dryanor Village", new Location(3074, 3283, 3112, 3241), 0),
DUEL_ARENA_ENTRANCE("Duel Arena Entrance", new Location(3311, 3247, 3328, 3223), 0),
DUEL_ARENA_NORTH("Duel Arena North", new Location(3329, 3266, 3388, 3264), 0),
DUEL_ARENA_NORTH_WEST("Duel Arena North West", new Location(3322, 3266, 3328, 3248), 0),
EDGE_BANK("Edge Bank", new Location(3090, 3499, 3099, 3487), 0),
FALADOR_PARTYROOM("Falador Partyroom", new Location(3035, 3386, 3056, 3370), 0),
FALLY_BANK("Fally Bank", new Location(2943, 3372, 2949, 3358), 0),
FALLY_CENTER("Fally Center", new Location(2959, 3385, 2972, 3374), 0),
FALLY_EAST_BANK("Fally East Bank", new Location(3008, 3358, 3021, 3353), 0),
FALLY_PARK("Fally Park", new Location(2982, 3390, 3025, 3368), 0),
FALLY_RESPAWN("Fally Respawn", new Location(2957, 3355, 2998, 3325), 0),
GNOME_AGILITY("Gnome Agility", new Location(2469, 3441, 2489, 3412), 0),
GNOME_BALL("Gnome Ball", new Location(2384, 3495, 2408, 3479), 0),
GRAND_EXCHANGE_NORTHEAST("Grand Exchange NorthEast", new Location(3165, 3516, 3197, 3490), 0),
GRAND_EXCHANGE_NORTHWEST("Grand Exchange NorthWest", new Location(3139, 3516, 3164, 3490), 0),
GRAND_EXCHANGE_SOUTHEAST("Grand Exchange SouthEast", new Location(3165, 3489, 3189, 3468), 0),
GRAND_EXCHANGE_SOUTHWEST("Grand Exchange SouthWest", new Location(3139, 3489, 3164, 3467), 0),
GRAND_TREE("Grand Tree", new Location(2442, 3515, 2490, 3478), 0),
ICE_MOUNTAIN("Ice Mountain", new Location(3001, 3508, 3024, 3463), 0),
TRAINING_GROUND("Training Ground", new Location(2501, 3387, 2534, 3358), 0),
LUMBRIDGE_CASTLE("Lumbridge Castle", new Location(3201, 3235, 3225, 3201), 0),
LUMBY_SWAMP("Lumby Swamp", new Location(3135, 3203, 3245, 3140), 0),
LUNAR_ISLE_CENTRAL("Lunar Isle Central", new Location(2055, 3933, 2112, 3888), 0),
LUNAR_ISLE_EAST("Lunar Isle East", new Location(2113, 3921, 2185, 3888), 0),
LUNAR_ISLE_NORTH("Lunar Isle North", new Location(2063, 3958, 2112, 3934), 0),
LUNAR_ISLE_NORTH_EAST("Lunar Isle North East", new Location(2113, 3958, 2185, 3922), 0),
LUNAR_ISLE_SOUTH("Lunar Isle South", new Location(2057, 3887, 2112, 3843), 0),
LUNAR_ISLE_SOUTHEAST("Lunar Isle SouthEast", new Location(2113, 3887, 2185, 3843), 0),
MONASTERY("Monastery", new Location(3044, 3507, 3060, 3471), 0),
NARDAH_NORTH("Nardah North", new Location(3397, 2942, 3453, 2915), 0),
NARDAH_SOUTH("Nardah South", new Location(3397, 2914, 3453, 2882), 0),
NIEVE("Nieve", new Location(2430, 3425, 2435, 3419), 0),
NIGHTMARE_ZONE("Nightmare Zone", new Location(2599, 3119, 2614, 3111), 0),
NORTH_MARIM("North Marim", new Location(2731, 2804, 2783, 2786), 0),
PEST_CONTROL("Pest Control", new Location(2630, 2679, 2682, 2627), 0),
POLLNIVNEACH_NORTH("Pollnivneach North", new Location(3331, 3004, 3379, 2974), 0),
POLLNIVNEACH_SOUTH("Pollnivneach South", new Location(3331, 2973, 3379, 2941), 0),
PORT_KHAZARD("Port Khazard", new Location(2624, 3182, 2680, 3143), 0),
PORT_SARIM("Port Sarim", new Location(3009, 3261, 3060, 3194), 0),
RELLEKA_SOUTH_WEST("Relleka South West", new Location(2609, 3678, 2649, 3644), 0),
RELLEKKA_NORTH_EAST("Rellekka North East", new Location(2650, 3712, 2690, 3679), 0),
RELLEKKA_NORTH_WEST("Rellekka North West", new Location(2609, 3712, 2649, 3679), 0),
RELLEKKA_SOUTH_EAST("Rellekka South East", new Location(2650, 3678, 2690, 3644), 0),
RIMMINGTON_PORTAL("Rimmington Portal", new Location(2946, 3228, 2960, 3218), 0),
ROCK_CRABS_EAST("Rock Crabs East", new Location(2691, 3738, 2730, 3713), 0),
ROCK_CRABS_WEST("Rock Crabs West", new Location(2650, 3738, 2690, 3713), 0),
SANDCRABS_CENTRAL("SandCrabs Central", new Location(1850, 3529, 1884, 3465), 0),
SANDCRABS_NORTH("SandCrabs North", new Location(1848, 3572, 1884, 3532), 0),
SANDCRABS_SOUTH("SandCrabs South", new Location(1796, 3468, 1849, 3436), 0),
SEERS_VILLAGE("Seers Village", new Location(2688, 3498, 2742, 3468), 0),
SHANTAY_PASS("Shantay Pass", new Location(3293, 3137, 3312, 3116), 0),
SHILO_VILLAGE_NORTH("Shilo Village North", new Location(2817, 3006, 2878, 2973), 0),
SHILO_VILLAGE_SOUTH("Shilo Village South", new Location(2816, 2972, 2879, 2944), 0),
SOUTH_MARIM("South Marim", new Location(2731, 2785, 2783, 2762), 0),
SOUTH_OF_GRAND_EXCHANGE("South of Grand Exchange", new Location(3156, 3466, 3190, 3448), 0),
TREEGOME_VILLAGE("TreeGome Village", new Location(2514, 3175, 2547, 3158), 0),
VARROCK_CENTRE("Varrock Centre", new Location(3201, 3444, 3229, 3412), 0),
VARROCK_EAST("Varrock East", new Location(3228, 3450, 3241, 3438), 0),
VARROCK_WEST("Varrock West", new Location(3172, 3447, 3200, 3427), 0),
WATERBIRTH_ISLAND("Waterbirth Island", new Location(2494, 3774, 2562, 3710), 0),
WEST_ARDY("West Ardy", new Location(2452, 3336, 2557, 3265), 0),
WINDERTODT_SOUTH_EAST("Windertodt South East", new Location(1630, 4007, 1651, 3987), 0),
WINTERTODT_CAMP("Wintertodt Camp", new Location(1616, 3963, 1645, 3932), 0),
WINTERTODT_ENTRANCE("Wintertodt Entrance", new Location(1617, 3986, 1641, 3964), 0),
WINTERTODT_NORTHEAST("Wintertodt NorthEast", new Location(1630, 4027, 1651, 4008), 0),
WINTERTODT_NORTHWEST("Wintertodt NorthWest", new Location(1608, 4028, 1629, 4008), 0),
WINTERTODT_SOUTHWEST("Wintertodt SouthWest", new Location(1608, 4007, 1629, 3987), 0),
WIZARDS_GUILD("Wizards Guild", new Location(2585, 3092, 2596, 3082), 0),
WIZARDS_TOWER("Wizards Tower", new Location(3093, 3171, 3121, 3146), 0),
YANILE_BANK("Yanile Bank", new Location(2608, 3097, 2616, 3087), 0),
YANILLE_EAST("Yanille East", new Location(2576, 3110, 2621, 3071), 0),
YANILLE_PORTAL("Yanille Portal", new Location(2537, 3108, 2551, 3091), 0),
YANILLE_WEST("Yanille West", new Location(2532, 3110, 2575, 3071), 0),
AXE_HUT("Axe Hut", new Location(3187, 3962, 3194, 3957), 0),
BANDIT_CAMP("Bandit Camp", new Location(3017, 3712, 3059, 3681), 0),
CALLISTO("Callisto", new Location(3266, 3863, 3315, 3827), 0),
CEMETERY("Cemetery", new Location(2956, 3767, 2996, 3736), 0),
CHAOS_FANATIC("Chaos Fanatic", new Location(2971, 3854, 2992, 3834), 0),
CHAOS_TEMPLE("Chaos Temple", new Location(3220, 3632, 3255, 3593), 0),
CHINS("Chins", new Location(3128, 3792, 3160, 3754), 0),
CORP_CAVE("Corp Cave", new Location(3201, 3684, 3219, 3672), 0),
CRAZY_ARCHAEOLOGIST("Crazy Archaeologist", new Location(2952, 3709, 2985, 3678), 0),
DARK_WARRIORS("Dark Warriors", new Location(3014, 3648, 3046, 3616), 0),
DWARVES("Dwarves", new Location(3230, 3805, 3264, 3779), 0),
EAST_DRAGS("East Drags", new Location(3326, 3704, 3365, 3671), 0),
ENTS("Ents", new Location(3300, 3627, 3320, 3584), 0),
FIFTY_PORTS("50 ports", new Location(3301, 3923, 3315, 3909), 0),
FIRE_GIANT_ENTRANCE("Fire Giant Entrance", new Location(3042, 3929, 3051, 3920), 0),
GAP("Gap", new Location(3238, 3855, 3258, 3841), 0),
GDZ("Gdz", new Location(3279, 3895, 3296, 3875), 0),
GHORROCK("44s", new Location(2973, 3870, 2987, 3859), 0),
GLORY_HILL("Glory Hill", new Location(3331, 3890, 3348, 3866), 0),
GLORY_HOLE("Glory Hole", new Location(3352, 3897, 3386, 3869), 0),
GRAVES("Graves", new Location(3128, 3686, 3181, 3658), 0),
GRAVEYARD_DRAGS("Graveyard Drags", new Location(3129, 3717, 3172, 3691), 0),
GOD_WARS_DUNGEON("God Wars Dungeon", new Location(3010, 3745, 3027, 3727), 0),
HIGH_ALTAR("High Altar", new Location(2945, 3826, 2970, 3813), 0),
HILL_GIANTS("Hill Giants", new Location(3282, 3687, 3300, 3674), 0),
HOB_OBELISK("35 Obelisk", new Location(3097, 3804, 3115, 3785), 0),
HOBGOBLINS("Hobgoblins", new Location(3073, 3775, 3104, 3745), 0),
ICE_GATE("Ice Gate", new Location(2945, 3913, 2978, 3878), 0),
ICE_ROCK("Ice Rock", new Location(2957, 3942, 2984, 3929), 0),
KBD_CAGE("KBD CAGE", new Location(3007, 3855, 3021, 3839), 0),
LAVA_DRAGS("Lava Drags", new Location(3175, 3857, 3221, 3805), 0),
LAVA_MAZE_TELE("Lava Maze Tele", new Location(3019, 3842, 3044, 3812), 0),
LEVER("Lever", new Location(3149, 3933, 3162, 3917), 0),
MAGE_ARENA("Mage Arena", new Location(3088, 3949, 3123, 3919), 0),
MAGE_BANK("Mage Bank", new Location(3082, 3960, 3103, 3952), 0),
NEW_GATE("New Gate", new Location(3345, 3957, 3390, 3916), 0),
PORTS_19("19s", new Location(3220, 3672, 3234, 3660), 0),
OLD_GATE("Old Gate", new Location(3211, 3906, 3238, 3882), 0),
PIRATE_HUT("Pirate Hut", new Location(3037, 3959, 3045, 3948), 0),
RESOURCE_ARENA("Resource Arena", new Location(3174, 3946, 3195, 3923), 0),
REV_BLACK_DRAGS("Rev Black Drags", new Location(3223, 10216, 3254, 10190), 0),
REV_CAVE("Rev Cave", new Location(3128, 10232, 3225, 10059), 0),
REV_DARK_BEAST("Rev Dark Beast", new Location(3243, 10154, 3264, 10136), 0),
REV_ENTRANCE("Rev Entrance", new Location(3118, 3837, 3142, 3818), 0),
INSIDE_REV_ENT("Inside Rev Ent.", new Location(3238, 10236, 3243, 10231), 0),
MAIN_REV_CHAMBER("Main Rev Chamber", new Location(3227, 10187, 3261, 10157), 0),
ROGUE_CASTLE("Rogue Castle", new Location(3275, 3947, 3299, 3920), 0),
RUNE_ROCKS("Rune Rocks", new Location(3055, 3890, 3072, 3876), 0),
SCORPIA("Scorpia", new Location(3216, 3949, 3248, 3935), 0),
SINGLE_STRIP("Single Strip", new Location(3333, 3842, 3348, 3774), 0),
LVL_18_REV_ENT("Lvl 18 Rev Ent", new Location(3071, 3660, 3092, 3645), 0),
SPIDER_HILL("Spider Hill", new Location(3156, 3896, 3182, 3871), 0),
PORTAL_13S("13s", new Location(3145, 3628, 3168, 3609), 0),
VENENATIS("Venenatis", new Location(3298, 3759, 3353, 3722), 0),
VETTION("Vet'tion", new Location(3183, 3796, 3227, 3765), 0),
VOLCANO("Volcano", new Location(3345, 3957, 3390, 3916), 0),
WEB("Web", new Location(3153, 3961, 3163, 3948), 0),
WEST_DRAGS("West Drags", new Location(2960, 3627, 2992, 3598), 0),
WILDY_AGILITY_COURSE("Wildy Agility Course", new Location(2988, 3967, 3008, 3906), 0);
@Getter
private final String name;
@Getter
private final WorldArea worldArea;
@Getter
private final Location location;
/**
* Creates a location used to get the name of a location by a WorldPoint
*
* @param name - The name that is used to represent the area in overlays etc
* @param location - A Location made out of 4 points on the world map
* @param plane - The plane of the World Area
*/
WorldLocation(String name, Location location, int plane)
{
this.name = name;
this.location = location;
this.worldArea = new WorldArea(location.x, location.y, location.width, location.height, plane);
}
/**
* Returns all locations that aren't in the wild
* @return - A Collection of non-wilderness WorldLocations
*/
public static Collection<WorldLocation> getNonWildernessLocations()
{
return Arrays.stream(WorldLocation.values()).filter(loc ->
PvPUtil.getWildernessLevelFrom(loc.worldArea.toWorldPoint()) < 0).collect(Collectors.toList());
}
/**
* Returns only the WorldLocations that are in the wilderness
* @return - A Collection of WorldLocations in the wilderness
*/
public static Collection<WorldLocation> getWildernessLocations()
{
return Arrays.stream(WorldLocation.values()).filter(loc ->
PvPUtil.getWildernessLevelFrom(loc.worldArea.toWorldPoint()) > 0).collect(Collectors.toList());
}
public static Map<WorldArea, String> getLocationMap()
{
Map<WorldArea, String> hashMap = new HashMap<>();
Arrays.stream(values()).forEach(worldLocation ->
hashMap.put(worldLocation.getWorldArea(), worldLocation.getName()));
return hashMap;
}
/**
* Returns the WorldLocation that a WorldPoint is in, or the closest WorldLocation to the point
* @param worldPoint - the WorldPoint to find the WorldLocation of
* @return - Containing location or closest location if it isn't in any
*/
public static String location(WorldPoint worldPoint)
{
final Map<WorldArea, String> locationMap = getLocationMap();
int dist = 10000;
String s = "";
WorldArea closestArea = null;
for (Map.Entry<WorldArea, String> entry : locationMap.entrySet())
{
WorldArea worldArea = entry.getKey();
if (worldArea.toWorldPointList().contains(worldPoint))
{
s = entry.getValue();
return s;
}
int distTo = worldArea.distanceTo(worldPoint);
if (distTo < dist)
{
dist = distTo;
closestArea = worldArea;
}
}
if (worldPoint.getY() > (Objects.requireNonNull(closestArea).toWorldPoint().getY() + closestArea.getHeight()))
{
s = s + "N";
}
if (worldPoint.getY() < closestArea.toWorldPoint().getY())
{
s = s + "S";
}
if (worldPoint.getX() < closestArea.toWorldPoint().getX())
{
s = s + "W";
}
if (worldPoint.getX() > (closestArea.toWorldPoint().getX() + closestArea.getWidth()))
{
s = s + "E";
}
s = s + " of ";
s = s + locationMap.get(closestArea);
if (s.startsWith(" of "))
{
s = s.substring(3);
}
return s;
}
public static class Location
{
@Getter
private final int x;
@Getter
private final int y;
@Getter
private final int x1;
@Getter
private final int y1;
final int width;
final int height;
Location(int x, int y, int x1, int y1)
{
this.x = x;
this.y = y;
this.x1 = x1;
this.y1 = y1;
this.width = x1 - x;
this.height = y - y1;
}
@Override
public String toString()
{
return "Location{" +
"x=" + x +
", y=" + y +
", width=" + width +
", height=" + height +
'}';
}
}
@Override
public String toString()
{
return "WorldLocation{" +
"name='" + name + '\'' +
", worldArea=" + worldArea +
'}';
}
}

View File

@@ -27,13 +27,13 @@ package net.runelite.client.game.chatbox;
/**
* A modal input that lives in the chatbox panel.
*/
public abstract class ChatboxInput
abstract class ChatboxInput
{
protected void open()
void open()
{
}
protected void close()
void close()
{
}
}

View File

@@ -34,7 +34,7 @@ import java.util.function.Consumer;
import javax.inject.Singleton;
import lombok.Getter;
import net.runelite.api.Client;
import net.runelite.api.ItemComposition;
import net.runelite.api.ItemDefinition;
import net.runelite.api.widgets.ItemQuantityMode;
import net.runelite.api.widgets.JavaScriptCallback;
import net.runelite.api.widgets.Widget;
@@ -59,7 +59,7 @@ public class ChatboxItemSearch extends ChatboxTextInput
private final ItemManager itemManager;
private final Client client;
private Map<Integer, ItemComposition> results = new LinkedHashMap<>();
private Map<Integer, ItemDefinition> results = new LinkedHashMap<>();
private String tooltipText;
private int index = -1;
@@ -121,7 +121,7 @@ public class ChatboxItemSearch extends ChatboxTextInput
int x = PADDING;
int y = PADDING * 3;
int idx = 0;
for (ItemComposition itemComposition : results.values())
for (ItemDefinition itemDefinition : results.values())
{
Widget item = container.createChild(-1, WidgetType.GRAPHIC);
item.setXPositionMode(WidgetPositionMode.ABSOLUTE_LEFT);
@@ -130,8 +130,8 @@ public class ChatboxItemSearch extends ChatboxTextInput
item.setOriginalY(y + FONT_SIZE * 2);
item.setOriginalHeight(ICON_HEIGHT);
item.setOriginalWidth(ICON_WIDTH);
item.setName("<col=ff9040>" + itemComposition.getName());
item.setItemId(itemComposition.getId());
item.setName("<col=ff9040>" + itemDefinition.getName());
item.setItemId(itemDefinition.getId());
item.setItemQuantity(10000);
item.setItemQuantityMode(ItemQuantityMode.NEVER);
item.setBorderType(1);
@@ -152,7 +152,7 @@ public class ChatboxItemSearch extends ChatboxTextInput
{
if (onItemSelected != null)
{
onItemSelected.accept(itemComposition.getId());
onItemSelected.accept(itemDefinition.getId());
}
chatboxPanelManager.close();
@@ -289,7 +289,7 @@ public class ChatboxItemSearch extends ChatboxTextInput
for (int i = 0; i < client.getItemCount() && results.size() < MAX_RESULTS; i++)
{
ItemComposition itemComposition = itemManager.getItemComposition(itemManager.canonicalize(i));
ItemDefinition itemComposition = itemManager.getItemDefinition(itemManager.canonicalize(i));
String name = itemComposition.getName().toLowerCase();
// The client assigns "null" to item names of items it doesn't know about
if (!name.equals("null") && name.contains(search))

View File

@@ -41,7 +41,6 @@ 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;
@@ -67,8 +66,8 @@ public class ChatboxPanelManager
@Inject
private ChatboxPanelManager(EventBus eventBus, Client client, ClientThread clientThread,
KeyManager keyManager, MouseManager mouseManager,
Provider<ChatboxTextMenuInput> chatboxTextMenuInputProvider, Provider<ChatboxTextInput> chatboxTextInputProvider)
KeyManager keyManager, MouseManager mouseManager,
Provider<ChatboxTextMenuInput> chatboxTextMenuInputProvider, Provider<ChatboxTextInput> chatboxTextInputProvider)
{
this.client = client;
this.clientThread = clientThread;
@@ -79,6 +78,10 @@ public class ChatboxPanelManager
this.chatboxTextMenuInputProvider = chatboxTextMenuInputProvider;
this.chatboxTextInputProvider = chatboxTextInputProvider;
eventBus.subscribe(ScriptCallbackEvent.class, this, this::onScriptCallbackEvent);
eventBus.subscribe(GameStateChanged.class, this, this::onGameStateChanged);
}
public void close()
@@ -103,7 +106,7 @@ public class ChatboxPanelManager
{
client.runScript(ScriptID.MESSAGE_LAYER_OPEN, 0);
eventBus.register(input);
// eventBus.register(input);
if (input instanceof KeyListener)
{
keyManager.registerKeyListener((KeyListener) input);
@@ -150,8 +153,7 @@ public class ChatboxPanelManager
.prompt(prompt);
}
@Subscribe
public void onScriptCallbackEvent(ScriptCallbackEvent ev)
private void onScriptCallbackEvent(ScriptCallbackEvent ev)
{
if (currentInput != null && "resetChatboxInput".equals(ev.getEventName()))
{
@@ -159,7 +161,6 @@ public class ChatboxPanelManager
}
}
@Subscribe
private void onGameStateChanged(GameStateChanged ev)
{
if (currentInput != null && ev.getGameState() == GameState.LOGIN_SCREEN)

View File

@@ -25,7 +25,6 @@
package net.runelite.client.game.chatbox;
import com.google.common.base.Strings;
import com.google.common.primitives.Ints;
import com.google.inject.Inject;
import java.awt.Point;
import java.awt.Rectangle;
@@ -58,6 +57,7 @@ import net.runelite.api.widgets.WidgetType;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.input.KeyListener;
import net.runelite.client.input.MouseListener;
import net.runelite.client.util.MiscUtils;
import net.runelite.client.util.Text;
@Slf4j
@@ -156,12 +156,12 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse
return this;
}
public ChatboxTextInput cursorAt(int index)
private ChatboxTextInput cursorAt(int index)
{
return cursorAt(index, index);
}
public ChatboxTextInput cursorAt(int indexA, int indexB)
private ChatboxTextInput cursorAt(int indexA, int indexB)
{
if (indexA < 0)
{
@@ -349,8 +349,8 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse
if (isStartLine || isEndLine || (cursorEnd > line.end && cursorStart < line.start))
{
final int cIdx = Ints.constrainToRange(cursorStart - line.start, 0, len);
final int ceIdx = Ints.constrainToRange(cursorEnd - line.start, 0, len);
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));
@@ -452,7 +452,7 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse
int cx = p.x - ccl.getX() - ox;
int cy = p.y - ccl.getY() - oy;
int currentLine = Ints.constrainToRange(cy / oh, 0, editLines.size() - 1);
int currentLine = MiscUtils.clamp(cy / oh, 0, editLines.size() - 1);
final Line line = editLines.get(currentLine);
final String tsValue = line.text;
@@ -489,7 +489,7 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse
break;
}
charIndex = Ints.constrainToRange(charIndex, 0, tsValue.length());
charIndex = MiscUtils.clamp(charIndex, 0, tsValue.length());
return line.start + charIndex;
};
@@ -722,9 +722,6 @@ public class ChatboxTextInput extends ChatboxInput implements KeyListener, Mouse
newPos++;
break;
case KeyEvent.VK_UP:
ev.consume();
newPos = getLineOffset.applyAsInt(code);
break;
case KeyEvent.VK_DOWN:
ev.consume();
newPos = getLineOffset.applyAsInt(code);

View File

@@ -32,12 +32,12 @@ import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import net.runelite.api.FontID;
import net.runelite.api.widgets.WidgetType;
import net.runelite.api.widgets.JavaScriptCallback;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetPositionMode;
import net.runelite.api.widgets.WidgetSizeMode;
import net.runelite.api.widgets.WidgetTextAlignment;
import net.runelite.api.widgets.WidgetType;
import net.runelite.client.input.KeyListener;
public class ChatboxTextMenuInput extends ChatboxInput implements KeyListener

View File

@@ -0,0 +1,36 @@
/*
* 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

@@ -0,0 +1,29 @@
package net.runelite.client.menus;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import net.runelite.api.MenuEntry;
@Getter
@Setter
@EqualsAndHashCode
public abstract class AbstractComparableEntry
{
String option = null;
String target = null;
int id = -1;
int type = -1;
boolean strictOption = true;
boolean strictTarget = true;
@EqualsAndHashCode.Exclude
private int priority = 0;
public abstract boolean matches(MenuEntry entry);
}

View File

@@ -0,0 +1,94 @@
/*
* 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 javax.annotation.Nonnull;
import joptsimple.internal.Strings;
import lombok.EqualsAndHashCode;
import net.runelite.api.MenuEntry;
import net.runelite.client.util.Text;
import org.apache.commons.lang3.StringUtils;
@EqualsAndHashCode(callSuper = true)
public class BaseComparableEntry extends AbstractComparableEntry
{
/**
* If two entries are both suppose to be left click,
* the entry with the higher priority will be selected.
* This only effects left click priority entries.
*/
public BaseComparableEntry(String option, String target, int id, int type, boolean strictOption, boolean strictTarget)
{
super.option = Text.standardize(option);
super.target = Text.standardize(target);
super.id = id;
super.type = type;
super.strictOption = strictOption;
super.strictTarget = strictTarget;
}
public boolean matches(@Nonnull MenuEntry entry)
{
String opt = entry.getOption();
if (strictOption && !StringUtils.equalsIgnoreCase(opt, option) || !strictOption && !StringUtils.containsIgnoreCase(opt, option))
{
return false;
}
if (strictTarget || !Strings.isNullOrEmpty(target))
{
String tgt = entry.getStandardizedTarget();
if (strictTarget && !tgt.equals(target) || !strictTarget && !tgt.contains(target))
{
return false;
}
}
if (id != -1)
{
int id = entry.getIdentifier();
if (this.id != id)
{
return false;
}
}
if (type != -1)
{
int type = entry.getOpcode();
if (this.type != type)
{
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,76 @@
package net.runelite.client.menus;
import net.runelite.api.Client;
import net.runelite.client.plugins.menuentryswapper.BankComparableEntry;
public interface ComparableEntries
{
/**
* BaseComparableEntries should only be used if there's
* no better ComparableEntry available.
*
* @param option has to equal entry option
* @param target has to equal entry option
*
* @return a new BaseComparableEntry
*/
static BaseComparableEntry newBaseComparableEntry(String option, String target)
{
return new BaseComparableEntry(option, target, -1, -1, true, true);
}
/**
* BaseComparableEntries should only be used if there's
* no better ComparableEntry available.
*
* @param option has to equal option
* @param target equal or contains depending on strictTarget
* @param strictTarget read up one line
*
* @return a new BaseComparableEntry
*/
static BaseComparableEntry newBaseComparableEntry(String option, String target, boolean strictTarget)
{
return new BaseComparableEntry(option, target, -1, -1, true, strictTarget);
}
/**
* BaseComparableEntries should only be used if there's
* no better ComparableEntry available.
*
* @param option equal or contains depending on strictOption
* @param target equal or contains depending on strictTarget
* @param id has to be the same, or -1 to skip checking
* @param type has to be the same, or -1 to skip checking
* @param strictOption strict option or nah
* @param strictTarget strict target or nah
*
* @return a new BaseComparableEntry
*/
static BaseComparableEntry newBaseComparableEntry(String option, String target, int id, int type, boolean strictOption, boolean strictTarget)
{
return new BaseComparableEntry(option, target, id, type, strictOption, strictTarget);
}
/**
* This comparable finds all items with itemName
* in their name. It then checks the ItemDefinition
* for each of them, to see if it's possible for
* the item to have option as one of their options.
*
* This has to be ran on the clientthread!
*/
static ItemComparableEntry newInvItemComparableEntry(Client client, String option, String itemName)
{
return new ItemComparableEntry.InvItemComparableEntry(client, option, itemName);
}
/**
* This will only match items in the bank or in
* your inventory if the bank is open. Withdraw-x anyone?
*/
static BankComparableEntry newBankComparableEntry(String option, String itemName)
{
return new BankComparableEntry(option, itemName, false);
}
}

View File

@@ -0,0 +1,96 @@
package net.runelite.client.menus;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import net.runelite.api.Client;
import net.runelite.api.ItemDefinition;
import net.runelite.api.MenuEntry;
import net.runelite.client.util.Text;
import org.apache.commons.lang3.StringUtils;
@EqualsAndHashCode(callSuper = true)
@Getter
@Setter
abstract class ItemComparableEntry extends AbstractComparableEntry
{
@EqualsAndHashCode.Exclude
short[] itemIds;
@EqualsAndHashCode.Exclude
short itemCount;
public abstract boolean matches(MenuEntry entry);
@EqualsAndHashCode(callSuper = true)
static class InvItemComparableEntry extends ItemComparableEntry
{
public InvItemComparableEntry(Client client, String option, String itemName)
{
assert client.isClientThread() : "You can only create these on the clientthread";
this.target = Text.standardize(itemName);
this.option = option;
short[] tmp = this.itemIds = new short[16];
final int itemCount = client.getItemCount();
short found = 0;
for (short i = 0; i < itemCount; i++)
{
ItemDefinition def = client.getItemDefinition(i);
if (def.getNote() != -1 || !StringUtils.containsIgnoreCase(def.getName(), target))
{
continue;
}
boolean notValid = true;
for (String opt : def.getInventoryActions())
{
if (opt != null && StringUtils.containsIgnoreCase(opt, option))
{
notValid = false;
break;
}
}
if (notValid && !"use".equals(this.option))
{
continue;
}
if (found >= tmp.length)
{
short[] rlyTmp = new short[found * 2];
System.arraycopy(tmp, 0, rlyTmp, 0, found);
tmp = rlyTmp;
}
tmp[found++] = i;
}
this.itemIds = new short[itemCount];
this.itemCount = found;
System.arraycopy(tmp, 0, this.itemIds, 0, found);
}
public boolean matches(MenuEntry entry)
{
if (!StringUtils.containsIgnoreCase(entry.getOption(), this.option))
{
return false;
}
int entryId = entry.getIdentifier();
for (short i = 0; i < itemCount; i++)
{
if (entryId == itemIds[i])
{
return true;
}
}
return false;
}
}
}

View File

@@ -1,5 +1,6 @@
/*
* 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
@@ -26,29 +27,42 @@ package net.runelite.client.menus;
import com.google.common.base.Preconditions;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.MenuAction;
import net.runelite.api.MenuEntry;
import net.runelite.api.NPCComposition;
import net.runelite.api.MenuOpcode;
import static net.runelite.api.MenuOpcode.MENU_ACTION_DEPRIORITIZE_OFFSET;
import net.runelite.api.NPCDefinition;
import net.runelite.api.events.BeforeRender;
import net.runelite.api.events.MenuEntryAdded;
import net.runelite.api.events.MenuOpened;
import net.runelite.api.events.MenuOptionClicked;
import net.runelite.api.events.NpcActionChanged;
import net.runelite.api.events.PlayerMenuOptionClicked;
import net.runelite.api.events.PlayerMenuOptionsChanged;
import net.runelite.api.events.WidgetMenuOptionClicked;
import net.runelite.api.events.WidgetPressed;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.eventbus.Subscribe;
import static net.runelite.client.menus.ComparableEntries.newBaseComparableEntry;
import net.runelite.client.util.Text;
@Singleton
@@ -69,12 +83,28 @@ public class MenuManager
//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 HashSet<AbstractComparableEntry> priorityEntries = new HashSet<>();
private LinkedHashMap<MenuEntry, AbstractComparableEntry> currentPriorityEntries = new LinkedHashMap<>();
private final HashSet<AbstractComparableEntry> hiddenEntries = new HashSet<>();
private final HashMap<AbstractComparableEntry, AbstractComparableEntry> swaps = new HashMap<>();
private MenuEntry leftClickEntry = null;
private MenuEntry firstEntry = null;
@Inject
private MenuManager(Client client, EventBus eventBus)
{
this.client = client;
this.eventBus = eventBus;
eventBus.subscribe(MenuOpened.class, this, this::onMenuOpened);
eventBus.subscribe(MenuEntryAdded.class, this, this::onMenuEntryAdded);
eventBus.subscribe(BeforeRender.class, this, this::onBeforeRender);
eventBus.subscribe(PlayerMenuOptionsChanged.class, this, this::onPlayerMenuOptionsChanged);
eventBus.subscribe(NpcActionChanged.class, this, this::onNpcActionChanged);
eventBus.subscribe(WidgetPressed.class, this, this::onWidgetPressed);
eventBus.subscribe(MenuOptionClicked.class, this, this::onMenuOptionClicked);
}
/**
@@ -114,30 +144,183 @@ public class MenuManager
return false;
}
@Subscribe
public void onMenuEntryAdded(MenuEntryAdded event)
private void onMenuOpened(MenuOpened event)
{
currentPriorityEntries.clear();
// Need to reorder the list to normal, then rebuild with swaps
MenuEntry[] oldEntries = event.getMenuEntries();
firstEntry = null;
List<MenuEntry> newEntries = Lists.newArrayList(oldEntries);
boolean shouldDeprioritize = false;
prioritizer:
for (MenuEntry entry : oldEntries)
{
for (AbstractComparableEntry p : priorityEntries)
{
// Create list of priority entries, and remove from menus
if (p.matches(entry))
{
// Other entries need to be deprioritized if their types are lower than 1000
if (entry.getOpcode() >= 1000 && !shouldDeprioritize)
{
shouldDeprioritize = true;
}
currentPriorityEntries.put(entry, p);
newEntries.remove(entry);
continue prioritizer;
}
}
if (newEntries.size() > 0)
{
// Swap first matching entry to top
for (AbstractComparableEntry src : swaps.keySet())
{
if (!src.matches(entry))
{
continue;
}
MenuEntry swapFrom = null;
AbstractComparableEntry from = swaps.get(src);
for (MenuEntry e : newEntries)
{
if (from.matches(e))
{
swapFrom = e;
break;
}
}
// Do not need to swap with itself or if the swapFrom is already the first entry
if (swapFrom != null && swapFrom != entry && swapFrom != Iterables.getLast(newEntries))
{
// Deprioritize entries if the swaps are not in similar opcode groups
if ((swapFrom.getOpcode() >= 1000 && entry.getOpcode() < 1000) || (entry.getOpcode() >= 1000 && swapFrom.getOpcode() < 1000) && !shouldDeprioritize)
{
shouldDeprioritize = true;
}
int indexFrom = newEntries.indexOf(swapFrom);
int indexTo = newEntries.indexOf(entry);
Collections.swap(newEntries, indexFrom, indexTo);
}
}
}
}
if (shouldDeprioritize)
{
for (MenuEntry entry : newEntries)
{
if (entry.getOpcode() <= MENU_ACTION_DEPRIORITIZE_OFFSET)
{
entry.setOpcode(entry.getOpcode() + MENU_ACTION_DEPRIORITIZE_OFFSET);
}
}
}
if (!currentPriorityEntries.isEmpty())
{
newEntries.addAll(currentPriorityEntries.entrySet().stream()
.sorted(Comparator.comparingInt(e -> e.getValue().getPriority()))
.map(Map.Entry::getKey)
.collect(Collectors.toList()));
}
MenuEntry[] arrayEntries = newEntries.toArray(new MenuEntry[0]);
// Need to set the event entries to prevent conflicts
event.setMenuEntries(arrayEntries);
client.setMenuEntries(arrayEntries);
}
private void onMenuEntryAdded(MenuEntryAdded event)
{
for (AbstractComparableEntry e : hiddenEntries)
{
if (e.matches(event.getMenuEntry()))
{
client.setMenuOptionCount(client.getMenuOptionCount() - 1);
return;
}
}
int widgetId = event.getActionParam1();
Collection<WidgetMenuOption> options = managedMenuOptions.get(widgetId);
MenuEntry[] menuEntries = client.getMenuEntries();
for (WidgetMenuOption currentMenu : options)
{
if (!menuContainsCustomMenu(currentMenu))//Don't add if we have already added it to this widget
{
MenuEntry[] menuEntries = client.getMenuEntries();
menuEntries = Arrays.copyOf(menuEntries, menuEntries.length + 1);
MenuEntry menuEntry = menuEntries[menuEntries.length - 1] = new MenuEntry();
menuEntry.setOption(currentMenu.getMenuOption());
menuEntry.setParam1(widgetId);
menuEntry.setTarget(currentMenu.getMenuTarget());
menuEntry.setType(MenuAction.RUNELITE.getId());
menuEntry.setOpcode(MenuOpcode.RUNELITE.getId());
client.setMenuEntries(menuEntries);
}
}
}
private void onBeforeRender(BeforeRender event)
{
rebuildLeftClickMenu();
}
private MenuEntry rebuildLeftClickMenu()
{
if (client.isMenuOpen())
{
return null;
}
int menuOptionCount = client.getMenuOptionCount();
if (menuOptionCount <= 2)
{
return null;
}
client.sortMenuEntries();
firstEntry = null;
MenuEntry[] entries = new MenuEntry[menuOptionCount + priorityEntries.size()];
System.arraycopy(client.getMenuEntries(), 0, entries, 0, menuOptionCount);
if (!priorityEntries.isEmpty())
{
indexPriorityEntries(entries, menuOptionCount);
}
if (firstEntry == null && !swaps.isEmpty())
{
indexSwapEntries(entries, menuOptionCount);
}
if (firstEntry == null)
{
// stop being null smh
firstEntry = entries[menuOptionCount - 1];
}
client.setMenuEntries(entries);
return firstEntry;
}
public void addPlayerMenuItem(String menuText)
{
Preconditions.checkNotNull(menuText);
@@ -164,8 +347,7 @@ public class MenuManager
}
}
@Subscribe
public void onPlayerMenuOptionsChanged(PlayerMenuOptionsChanged event)
private void onPlayerMenuOptionsChanged(PlayerMenuOptionsChanged event)
{
int idx = event.getIndex();
@@ -189,17 +371,16 @@ public class MenuManager
addPlayerMenuItem(newIdx, menuText);
}
@Subscribe
public void onNpcActionChanged(NpcActionChanged event)
private void onNpcActionChanged(NpcActionChanged event)
{
NPCComposition composition = event.getNpcComposition();
NPCDefinition composition = event.getNpcDefinition();
for (String npcOption : npcMenuOptions)
{
addNpcOption(composition, npcOption);
}
}
private void addNpcOption(NPCComposition composition, String npcOption)
private void addNpcOption(NPCDefinition composition, String npcOption)
{
String[] actions = composition.getActions();
int unused = -1;
@@ -221,7 +402,7 @@ public class MenuManager
actions[unused] = npcOption;
}
private void removeNpcOption(NPCComposition composition, String npcOption)
private void removeNpcOption(NPCDefinition composition, String npcOption)
{
String[] actions = composition.getActions();
@@ -239,49 +420,67 @@ public class MenuManager
}
}
@Subscribe
public void onMenuOptionClicked(MenuOptionClicked event)
private void onWidgetPressed(WidgetPressed event)
{
if (event.getMenuAction() != MenuAction.RUNELITE)
leftClickEntry = rebuildLeftClickMenu();
}
private void onMenuOptionClicked(MenuOptionClicked event)
{
if (!client.isMenuOpen() && event.isAuthentic())
{
if (event.getMouseButton() != 0)
{
leftClickEntry = rebuildLeftClickMenu();
}
if (leftClickEntry != null)
{
event.setMenuEntry(leftClickEntry);
leftClickEntry = null;
}
}
if (event.getMenuOpcode() != MenuOpcode.RUNELITE)
{
return; // not a player menu
}
int widgetId = event.getWidgetId();
int widgetId = event.getActionParam1();
Collection<WidgetMenuOption> options = managedMenuOptions.get(widgetId);
for (WidgetMenuOption curMenuOption : options)
{
if (curMenuOption.getMenuTarget().equals(event.getMenuTarget())
&& curMenuOption.getMenuOption().equals(event.getMenuOption()))
if (curMenuOption.getMenuTarget().equals(event.getTarget())
&& curMenuOption.getMenuOption().equals(event.getOption()))
{
WidgetMenuOptionClicked customMenu = new WidgetMenuOptionClicked();
customMenu.setMenuOption(event.getMenuOption());
customMenu.setMenuTarget(event.getMenuTarget());
customMenu.setMenuOption(event.getOption());
customMenu.setMenuTarget(event.getTarget());
customMenu.setWidget(curMenuOption.getWidget());
eventBus.post(customMenu);
eventBus.post(WidgetMenuOptionClicked.class, customMenu);
return; // don't continue because it's not a player option
}
}
String target = event.getMenuTarget();
String target = event.getTarget();
// 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.setMenuOption(event.getOption());
playerMenuOptionClicked.setMenuTarget(username);
eventBus.post(playerMenuOptionClicked);
eventBus.post(PlayerMenuOptionClicked.class, playerMenuOptionClicked);
}
private void addPlayerMenuItem(int playerOptionIndex, String menuText)
{
client.getPlayerOptions()[playerOptionIndex] = menuText;
client.getPlayerOptionsPriorities()[playerOptionIndex] = true;
client.getPlayerMenuTypes()[playerOptionIndex] = MenuAction.RUNELITE.getId();
client.getPlayerMenuTypes()[playerOptionIndex] = MenuOpcode.RUNELITE.getId();
playerMenuIndexMap.put(playerOptionIndex, menuText);
}
@@ -307,4 +506,402 @@ public class MenuManager
return index;
}
/**
* Adds to the set of menu entries which when present, will remove all entries except for this one
*/
public AbstractComparableEntry addPriorityEntry(String option, String target)
{
option = Text.standardize(option);
target = Text.standardize(target);
AbstractComparableEntry entry = newBaseComparableEntry(option, target);
priorityEntries.add(entry);
return entry;
}
public void removePriorityEntry(String option, String target)
{
option = Text.standardize(option);
target = Text.standardize(target);
AbstractComparableEntry entry = newBaseComparableEntry(option, target);
priorityEntries.removeIf(entry::equals);
}
/**
* Adds to the set of menu entries which when present, will remove all entries except for this one
* This method will add one with strict option, but not-strict target (contains for target, equals for option)
*/
public AbstractComparableEntry addPriorityEntry(String option)
{
option = Text.standardize(option);
AbstractComparableEntry entry = newBaseComparableEntry(option, "", false);
priorityEntries.add(entry);
return entry;
}
public AbstractComparableEntry addPriorityEntry(String option, boolean strictOption)
{
option = Text.standardize(option);
AbstractComparableEntry entry =
newBaseComparableEntry(option, "", -1, -1, false, strictOption);
priorityEntries.add(entry);
return entry;
}
public AbstractComparableEntry addPriorityEntry(AbstractComparableEntry entry)
{
priorityEntries.add(entry);
return entry;
}
public void removePriorityEntry(AbstractComparableEntry entry)
{
priorityEntries.removeIf(entry::equals);
}
public void removePriorityEntry(String option)
{
option = Text.standardize(option);
AbstractComparableEntry entry = newBaseComparableEntry(option, "", false);
priorityEntries.removeIf(entry::equals);
}
public void removePriorityEntry(String option, boolean strictOption)
{
option = Text.standardize(option);
AbstractComparableEntry entry =
newBaseComparableEntry(option, "", -1, -1, false, strictOption);
priorityEntries.removeIf(entry::equals);
}
/**
* Adds to the map of swaps. Strict options, not strict target but target1=target2
*/
public void addSwap(String option, String target, String option2)
{
addSwap(option, target, option2, target, true, false);
}
public void removeSwap(String option, String target, String option2)
{
removeSwap(option, target, option2, target, true, false);
}
/**
* Adds to the map of swaps.
*/
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);
AbstractComparableEntry swapFrom = newBaseComparableEntry(option, target, -1, -1, strictOption, strictTarget);
AbstractComparableEntry swapTo = newBaseComparableEntry(option2, target2, -1, -1, strictOption, strictTarget);
if (swapTo.equals(swapFrom))
{
log.warn("You shouldn't try swapping an entry for itself");
return;
}
swaps.put(swapFrom, swapTo);
}
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);
AbstractComparableEntry swapFrom = newBaseComparableEntry(option, target, -1, -1, strictOption, strictTarget);
AbstractComparableEntry swapTo = newBaseComparableEntry(option2, target2, -1, -1, strictOption, strictTarget);
removeSwap(swapFrom, swapTo);
}
/**
* Adds to the map of swaps. - Strict option + target
*/
public void addSwap(String option, String target, String option2, String target2)
{
addSwap(option, target, option2, target2, false, false);
}
public void removeSwap(String option, String target, String option2, String target2)
{
removeSwap(option, target, option2, target2, false, false);
}
/**
* Adds to the map of swaps - Pre-baked entry
*/
public void addSwap(AbstractComparableEntry swapFrom, AbstractComparableEntry swapTo)
{
if (swapTo.equals(swapFrom))
{
log.warn("You shouldn't try swapping an entry for itself");
return;
}
swaps.put(swapFrom, swapTo);
}
/**
* Adds to the map of swaps - Non-strict option/target, but with opcode & id
* ID's of -1 are ignored in matches()!
*/
public void addSwap(String option, String target, int id, int type, String option2, String target2, int id2, int type2)
{
option = Text.standardize(option);
target = Text.standardize(target);
option2 = Text.standardize(option2);
target2 = Text.standardize(target2);
AbstractComparableEntry swapFrom = newBaseComparableEntry(option, target, id, type, false, false);
AbstractComparableEntry swapTo = newBaseComparableEntry(option2, target2, id2, type2, false, false);
if (swapTo.equals(swapFrom))
{
log.warn("You shouldn't try swapping an entry for itself");
return;
}
swaps.put(swapFrom, swapTo);
}
public void removeSwap(String option, String target, int id, int type, String option2, String target2, int id2, int type2)
{
option = Text.standardize(option);
target = Text.standardize(target);
option2 = Text.standardize(option2);
target2 = Text.standardize(target2);
AbstractComparableEntry swapFrom = newBaseComparableEntry(option, target, id, type, false, false);
AbstractComparableEntry swapTo = newBaseComparableEntry(option2, target2, id2, type2, false, false);
swaps.entrySet().removeIf(e -> e.getKey().equals(swapFrom) && e.getValue().equals(swapTo));
}
public void removeSwap(AbstractComparableEntry swapFrom, AbstractComparableEntry swapTo)
{
swaps.entrySet().removeIf(e -> e.getKey().equals(swapFrom) && e.getValue().equals(swapTo));
}
/**
* Removes all swaps with target
*/
public void removeSwaps(String withTarget)
{
final String target = Text.standardize(withTarget);
swaps.keySet().removeIf(e -> e.getTarget().equals(target));
}
/**
* Adds to the set of menu entries which when present, will be hidden from the menu
*/
public void addHiddenEntry(String option, String target)
{
option = Text.standardize(option);
target = Text.standardize(target);
AbstractComparableEntry entry = newBaseComparableEntry(option, target);
hiddenEntries.add(entry);
}
public void removeHiddenEntry(String option, String target)
{
option = Text.standardize(option);
target = Text.standardize(target);
AbstractComparableEntry entry = newBaseComparableEntry(option, target);
hiddenEntries.removeIf(entry::equals);
}
/**
* Adds to the set of menu entries which when present, will be hidden from the menu
* This method will add one with strict option, but not-strict target (contains for target, equals for option)
*/
public void addHiddenEntry(String option)
{
option = Text.standardize(option);
AbstractComparableEntry entry = newBaseComparableEntry(option, "", false);
hiddenEntries.add(entry);
}
public void removeHiddenEntry(String option)
{
option = Text.standardize(option);
AbstractComparableEntry entry = newBaseComparableEntry(option, "", false);
hiddenEntries.removeIf(entry::equals);
}
/**
* Adds to the set of hidden entries.
*/
public void addHiddenEntry(String option, String target, boolean strictOption, boolean strictTarget)
{
option = Text.standardize(option);
target = Text.standardize(target);
AbstractComparableEntry entry = newBaseComparableEntry(option, target, -1, -1, strictOption, strictTarget);
hiddenEntries.add(entry);
}
public void removeHiddenEntry(String option, String target, boolean strictOption, boolean strictTarget)
{
option = Text.standardize(option);
target = Text.standardize(target);
AbstractComparableEntry entry = newBaseComparableEntry(option, target, -1, -1, strictOption, strictTarget);
hiddenEntries.remove(entry);
}
/**
* Adds to the set of hidden entries - Pre-baked Comparable entry
*/
public void addHiddenEntry(AbstractComparableEntry entry)
{
hiddenEntries.add(entry);
}
public void removeHiddenEntry(AbstractComparableEntry entry)
{
hiddenEntries.remove(entry);
}
// This could use some optimization
private void indexPriorityEntries(MenuEntry[] entries, int menuOptionCount)
{
// create a array of priority entries so we can sort those
final SortMapping[] prios = new SortMapping[entries.length - menuOptionCount];
int prioAmt = 0;
for (int i = 0; i < menuOptionCount; i++)
{
final MenuEntry entry = entries[i];
for (AbstractComparableEntry prio : priorityEntries)
{
if (!prio.matches(entry))
{
continue;
}
final SortMapping map = new SortMapping(prio.getPriority(), entry);
prios[prioAmt++] = map;
entries[i] = null;
break;
}
}
if (prioAmt == 0)
{
return;
}
// Sort em!
Arrays.sort(prios, 0, prioAmt);
int i;
// Just place them after the standard entries. clientmixin ignores null entries
for (i = 0; i < prioAmt; i++)
{
entries[menuOptionCount + i] = prios[i].entry;
}
firstEntry = entries[menuOptionCount + i - 1];
}
private void indexSwapEntries(MenuEntry[] entries, int menuOptionCount)
{
// firstEntry was null, so it's the entry at count - 1
final MenuEntry first = entries[menuOptionCount - 1];
if (first == null)
{
log.debug("First entry is null");
return;
}
Set<AbstractComparableEntry> values = new HashSet<>();
for (Map.Entry<AbstractComparableEntry, AbstractComparableEntry> pair : swaps.entrySet())
{
if (pair.getKey().matches(first))
{
values.add(pair.getValue());
}
}
if (values.isEmpty())
{
return;
}
// Backwards so we swap with the otherwise highest one
// Count - 2 so we don't compare the entry against itself
outer:
for (int i = menuOptionCount - 2; i > 0; i--)
{
final MenuEntry entry = entries[i];
for (AbstractComparableEntry swap : values)
{
if (!swap.matches(entry))
{
continue;
}
entries[i] = first;
entries[menuOptionCount - 1] = entry;
firstEntry = entry;
break outer;
}
}
}
@AllArgsConstructor
private class SortMapping implements Comparable<SortMapping>
{
private final int priority;
private final MenuEntry entry;
@Override
public int compareTo(@Nonnull SortMapping mapping)
{
return Integer.compare(this.priority, mapping.priority);
}
}
}

View File

@@ -24,24 +24,23 @@
*/
package net.runelite.client.menus;
import net.runelite.api.widgets.WidgetInfo;
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"
* The left hand text to be displayed on the menu option. Ex. the option 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"
* The right hand text to be displayed on the menu option Ex. the target 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.
* The color that the target should be. Defaults to the brownish color that most menu options have.
*/
private Color color = JagexColors.MENU_TARGET;
@@ -60,7 +59,7 @@ public final class WidgetMenuOption
public WidgetMenuOption(String menuOption, String menuTarget, WidgetInfo widget)
{
this.menuOption = menuOption;
setMenuTarget(menuTarget);
this.menuTarget = menuTarget;
this.widget = widget;
}

View File

@@ -27,11 +27,15 @@ 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)
{

View File

@@ -0,0 +1,67 @@
/*
* 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

@@ -58,4 +58,6 @@ public @interface PluginDescriptor
boolean developerPlugin() default false;
boolean loadWhenOutdated() default false;
PluginType type() default PluginType.GENERAL_USE;
}

View File

@@ -43,12 +43,18 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Named;
@@ -57,20 +63,19 @@ import javax.inject.Singleton;
import javax.swing.SwingUtilities;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.client.events.SessionClose;
import net.runelite.client.events.SessionOpen;
import net.runelite.client.RuneLite;
import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.config.RuneLiteConfig;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.PluginChanged;
import net.runelite.client.events.SessionClose;
import net.runelite.client.events.SessionOpen;
import net.runelite.client.task.Schedule;
import net.runelite.client.task.ScheduledMethod;
import net.runelite.client.task.Scheduler;
import net.runelite.client.ui.SplashScreen;
import net.runelite.client.ui.RuneLiteSplashScreen;
import net.runelite.client.util.GameEventManager;
@Singleton
@@ -91,7 +96,10 @@ public class PluginManager
private final List<Plugin> plugins = new CopyOnWriteArrayList<>();
private final List<Plugin> activePlugins = new CopyOnWriteArrayList<>();
private final String runeliteGroupName = RuneLiteConfig.class
.getAnnotation(ConfigGroup.class).value();
.getAnnotation(ConfigGroup.class).value();
@Inject
PluginWatcher pluginWatcher;
@Setter
boolean isOutdated;
@@ -112,16 +120,25 @@ public class PluginManager
this.configManager = configManager;
this.executor = executor;
this.sceneTileManager = sceneTileManager;
if (eventBus != null)
{
eventBus.subscribe(SessionOpen.class, this, this::onSessionOpen);
eventBus.subscribe(SessionClose.class, this, this::onSessionClose);
}
}
@Subscribe
public void onSessionOpen(SessionOpen event)
public void watch()
{
pluginWatcher.start();
}
private void onSessionOpen(SessionOpen event)
{
refreshPlugins();
}
@Subscribe
public void onSessionClose(SessionClose event)
private void onSessionClose(SessionClose event)
{
refreshPlugins();
}
@@ -162,7 +179,7 @@ public class PluginManager
return null;
}
public List<Config> getPluginConfigProxies()
private List<Config> getPluginConfigProxies()
{
List<Injector> injectors = new ArrayList<>();
injectors.add(RuneLite.getInjector());
@@ -202,6 +219,7 @@ public class PluginManager
{
List<Plugin> scannedPlugins = new ArrayList<>(plugins);
int loaded = 0;
for (Plugin plugin : scannedPlugins)
{
try
@@ -215,18 +233,19 @@ public class PluginManager
}
loaded++;
SplashScreen.stage(.80, 1, null, "Starting plugins", loaded, scannedPlugins.size(), false);
RuneLiteSplashScreen.stage(.80, 1, "Starting plugins", loaded, scannedPlugins.size());
}
}
@SuppressWarnings("unchecked")
List<Plugin> scanAndInstantiate(ClassLoader classLoader, String packageName) throws IOException
{
SplashScreen.stage(.59, null, "Loading Plugins");
RuneLiteSplashScreen.stage(.59, "Loading plugins");
MutableGraph<Class<? extends Plugin>> graph = GraphBuilder
.directed()
.build();
List<Plugin> scannedPlugins = new ArrayList<>();
ClassPath classPath = ClassPath.from(classLoader);
ImmutableSet<ClassInfo> classes = packageName == null ? classPath.getAllClasses()
@@ -241,7 +260,7 @@ public class PluginManager
if (clazz.getSuperclass() == Plugin.class)
{
log.warn("Class {} is a plugin, but has no plugin descriptor",
clazz);
clazz);
}
continue;
}
@@ -249,7 +268,7 @@ public class PluginManager
if (clazz.getSuperclass() != Plugin.class)
{
log.warn("Class {} has plugin descriptor, but is not a plugin",
clazz);
clazz);
continue;
}
@@ -263,7 +282,7 @@ public class PluginManager
continue;
}
Class<Plugin> pluginClass = (Class<Plugin>) clazz;
@SuppressWarnings("unchecked") Class<Plugin> pluginClass = (Class<Plugin>) clazz;
graph.addNode(pluginClass);
}
@@ -283,26 +302,52 @@ public class PluginManager
throw new RuntimeException("Plugin dependency graph contains a cycle!");
}
List<Class<? extends Plugin>> sortedPlugins = topologicalSort(graph);
List<List<Class<? extends Plugin>>> sortedPlugins = topologicalGroupSort(graph);
sortedPlugins = Lists.reverse(sortedPlugins);
AtomicInteger loaded = new AtomicInteger();
int loaded = 0;
for (Class<? extends Plugin> pluginClazz : sortedPlugins)
final long start = System.currentTimeMillis();
// some plugins get stuck on IO, so add some extra threads
ExecutorService exec = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);
List<Plugin> scannedPlugins = new CopyOnWriteArrayList<>();
sortedPlugins.forEach(group ->
{
Plugin plugin;
try
{
plugin = instantiate(scannedPlugins, (Class<Plugin>) pluginClazz);
scannedPlugins.add(plugin);
}
catch (PluginInstantiationException ex)
{
log.warn("Error instantiating plugin!", ex);
}
List<Future<?>> curGroup = new ArrayList<>();
group.forEach(pluginClazz ->
curGroup.add(exec.submit(() ->
{
Plugin plugin;
try
{
plugin = instantiate(scannedPlugins, (Class<Plugin>) pluginClazz);
scannedPlugins.add(plugin);
}
catch (PluginInstantiationException e)
{
log.warn("Error instantiating plugin!", e);
return;
}
loaded++;
SplashScreen.stage(.60, .70, null, "Loading Plugins", loaded, sortedPlugins.size(), false);
}
loaded.getAndIncrement();
RuneLiteSplashScreen.stage(.60, .70, "Loading plugins", loaded.get(), scannedPlugins.size());
})));
curGroup.forEach(future ->
{
try
{
future.get();
}
catch (InterruptedException | ExecutionException e)
{
e.printStackTrace();
}
});
});
log.info("Plugin instantiation took {}ms", System.currentTimeMillis() - start);
return scannedPlugins;
}
@@ -341,9 +386,9 @@ public class PluginManager
}
}
eventBus.register(plugin);
// eventBus.register(plugin);
schedule(plugin);
eventBus.post(new PluginChanged(plugin, true));
eventBus.post(PluginChanged.class, new PluginChanged(plugin, true));
}
catch (InterruptedException | InvocationTargetException | IllegalArgumentException ex)
{
@@ -365,7 +410,6 @@ public class PluginManager
try
{
unschedule(plugin);
eventBus.unregister(plugin);
// plugins always stop in the event thread
SwingUtilities.invokeAndWait(() ->
@@ -381,7 +425,7 @@ public class PluginManager
});
log.debug("Plugin {} is now stopped", plugin.getClass().getSimpleName());
eventBus.post(new PluginChanged(plugin, false));
eventBus.post(PluginChanged.class, new PluginChanged(plugin, false));
}
catch (InterruptedException | InvocationTargetException ex)
@@ -405,13 +449,14 @@ public class PluginManager
if (value != null)
{
return Boolean.valueOf(value);
return Boolean.parseBoolean(value);
}
final PluginDescriptor pluginDescriptor = plugin.getClass().getAnnotation(PluginDescriptor.class);
return pluginDescriptor == null || pluginDescriptor.enabledByDefault();
}
@SuppressWarnings("unchecked")
private Plugin instantiate(List<Plugin> scannedPlugins, Class<Plugin> clazz) throws PluginInstantiationException
{
PluginDependency[] pluginDependencies = clazz.getAnnotationsByType(PluginDependency.class);
@@ -515,39 +560,54 @@ public class PluginManager
}
/**
* Topologically sort a graph. Uses Kahn's algorithm.
* Topologically sort a graph into separate groups.
* Each group represents the dependency level of the plugins.
* Plugins in group (index) 0 has no dependents.
* Plugins in group 1 has dependents in group 0.
* Plugins in group 2 has dependents in group 1, etc.
* This allows for loading dependent groups serially, starting from the last group,
* while loading plugins within each group in parallel.
* @param graph
* @param <T>
* @return
*/
private <T> List<T> topologicalSort(Graph<T> graph)
private <T> List<List<T>> topologicalGroupSort(Graph<T> graph)
{
MutableGraph<T> graphCopy = Graphs.copyOf(graph);
List<T> l = new ArrayList<>();
Set<T> s = graphCopy.nodes().stream()
.filter(node -> graphCopy.inDegree(node) == 0)
final Set<T> root = graph.nodes().stream()
.filter(node -> graph.inDegree(node) == 0)
.collect(Collectors.toSet());
while (!s.isEmpty())
{
Iterator<T> it = s.iterator();
T n = it.next();
it.remove();
final Map<T, Integer> dependencyCount = new HashMap<>();
l.add(n);
root.forEach(n -> dependencyCount.put(n, 0));
root.forEach(n -> graph.successors(n)
.forEach(m -> incrementChildren(graph, dependencyCount, m, dependencyCount.get(n) + 1)));
for (T m : graphCopy.successors(n))
// create list<list> dependency grouping
final List<List<T>> dependencyGroups = new ArrayList<>();
final int[] curGroup = {-1};
dependencyCount.entrySet().stream()
.sorted(Map.Entry.comparingByValue())
.forEach(entry ->
{
graphCopy.removeEdge(n, m);
if (graphCopy.inDegree(m) == 0)
if (entry.getValue() != curGroup[0])
{
s.add(m);
curGroup[0] = entry.getValue();
dependencyGroups.add(new ArrayList<>());
}
}
}
if (!graphCopy.edges().isEmpty())
dependencyGroups.get(dependencyGroups.size() - 1).add(entry.getKey());
});
return dependencyGroups;
}
private <T> void incrementChildren(Graph<T> graph, Map<T, Integer> dependencyCount, T n, int val)
{
if (!dependencyCount.containsKey(n) || dependencyCount.get(n) < val)
{
throw new RuntimeException("Graph has at least one cycle");
dependencyCount.put(n, val);
graph.successors(n).forEach(m ->
incrementChildren(graph, dependencyCount, m, val + 1));
}
return l;
}
}

View File

@@ -0,0 +1,12 @@
package net.runelite.client.plugins;
public enum PluginType
{
PVM,
PVP,
SKILLING,
UTILITY,
GENERAL_USE,
EXTERNAL,
PLUGIN_ORGANIZER
}

View File

@@ -0,0 +1,273 @@
/*
* 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.Injector;
import com.google.inject.Key;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URLClassLoader;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
import java.nio.file.WatchEvent;
import java.nio.file.WatchEvent.Kind;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import net.runelite.client.RuneLite;
import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.config.RuneLitePlusConfig;
@Singleton
@Slf4j
public class PluginWatcher extends Thread
{
private static final File BASE = RuneLite.PLUGIN_DIR;
private final RuneLitePlusConfig runelitePlusConfig;
private final PluginManager pluginManager;
private final WatchService watchService;
private final WatchKey watchKey;
@Inject
private ConfigManager configManager;
@Inject
public PluginWatcher(RuneLitePlusConfig runelitePlusConfig, PluginManager pluginManager) throws IOException
{
this.runelitePlusConfig = runelitePlusConfig;
this.pluginManager = pluginManager;
setName("Plugin Watcher");
setDaemon(true);
watchService = FileSystems.getDefault().newWatchService();
BASE.mkdirs();
Path dir = BASE.toPath();
watchKey = dir.register(watchService, ENTRY_MODIFY, ENTRY_DELETE);
}
public void cancel()
{
watchKey.cancel();
}
@Override
public void run()
{
if (runelitePlusConfig.enablePlugins())
{
scan();
}
for (; ; )
{
try
{
WatchKey key = watchService.take();
Thread.sleep(50);
if (!runelitePlusConfig.enablePlugins())
{
key.reset();
continue;
}
for (WatchEvent<?> event : key.pollEvents())
{
Kind<?> kind = event.kind();
Path path = (Path) event.context();
File file = new File(BASE, path.toFile().getName());
log.debug("Event {} file {}", kind, file);
if (kind == ENTRY_MODIFY)
{
Plugin existing = findPluginForFile(file);
if (existing != null)
{
log.info("Reloading plugin {}", file);
unload(existing);
}
else
{
log.info("Loading plugin {}", file);
}
load(file);
}
else if (kind == ENTRY_DELETE)
{
Plugin existing = findPluginForFile(file);
if (existing != null)
{
log.info("Unloading plugin {}", file);
unload(existing);
}
}
}
key.reset();
}
catch (InterruptedException ex)
{
log.warn("error polling for plugins", ex);
}
}
}
private void scan()
{
for (File file : BASE.listFiles())
{
if (!file.getName().endsWith(".jar"))
{
continue;
}
log.info("Loading plugin from {}", file);
load(file);
}
}
private Plugin findPluginForFile(File file)
{
for (Plugin plugin : pluginManager.getPlugins())
{
if (plugin.file != null && plugin.file.equals(file))
{
return plugin;
}
}
return null;
}
private void load(File pluginFile)
{
PluginClassLoader loader;
try
{
loader = new PluginClassLoader(pluginFile, getClass().getClassLoader());
}
catch (MalformedURLException ex)
{
log.warn("Error loading plugin", ex);
return;
}
List<Plugin> loadedPlugins;
try
{
loadedPlugins = pluginManager.scanAndInstantiate(loader, null);
}
catch (IOException ex)
{
close(loader);
log.warn("Error loading plugin", ex);
return;
}
if (loadedPlugins.isEmpty())
{
close(loader);
log.warn("No plugin found in plugin {}", pluginFile);
return;
}
if (loadedPlugins.size() != 1)
{
close(loader);
log.warn("You can not have more than one plugin per jar");
return;
}
Plugin plugin = loadedPlugins.get(0);
plugin.file = pluginFile;
plugin.loader = loader;
// Initialize default configuration
Injector injector = plugin.getInjector();
for (Key<?> key : injector.getAllBindings().keySet())
{
Class<?> type = key.getTypeLiteral().getRawType();
if (Config.class.isAssignableFrom(type))
{
Config config = (Config) injector.getInstance(key);
configManager.setDefaultConfiguration(config, false);
}
}
try
{
pluginManager.startPlugin(plugin);
}
catch (PluginInstantiationException ex)
{
close(loader);
log.warn("unable to start plugin", ex);
return;
}
// Plugin is now running
pluginManager.add(plugin);
}
private void unload(Plugin plugin)
{
try
{
pluginManager.stopPlugin(plugin);
}
catch (PluginInstantiationException ex)
{
log.warn("unable to stop plugin", ex);
}
pluginManager.remove(plugin); // remove it regardless
close(plugin.loader);
}
private void close(URLClassLoader classLoader)
{
try
{
classLoader.close();
}
catch (IOException ex1)
{
log.warn(null, ex1);
}
}
}

View File

@@ -24,21 +24,17 @@
*/
package net.runelite.client.plugins.account;
import java.awt.image.BufferedImage;
import java.util.concurrent.ScheduledExecutorService;
import javax.inject.Inject;
import javax.swing.JOptionPane;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import net.runelite.client.account.AccountSession;
import net.runelite.client.account.SessionManager;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.SessionClose;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.events.SessionOpen;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.ui.ClientToolbar;
import net.runelite.client.ui.NavigationButton;
import net.runelite.client.util.ImageUtil;
@PluginDescriptor(
name = "Account",
@@ -47,6 +43,7 @@ import net.runelite.client.util.ImageUtil;
loadWhenOutdated = true
)
@Slf4j
@Singleton
public class AccountPlugin extends Plugin
{
@Inject
@@ -58,76 +55,28 @@ public class AccountPlugin extends Plugin
@Inject
private ScheduledExecutorService executor;
private NavigationButton loginButton;
private NavigationButton logoutButton;
@Inject
private EventBus eventBus;
private static final BufferedImage LOGIN_IMAGE, LOGOUT_IMAGE;
static
{
LOGIN_IMAGE = ImageUtil.getResourceStreamFromClass(AccountPlugin.class, "login_icon.png");
LOGOUT_IMAGE = ImageUtil.getResourceStreamFromClass(AccountPlugin.class, "logout_icon.png");
}
@Override
protected void startUp() throws Exception
{
loginButton = NavigationButton.builder()
.tab(false)
.icon(LOGIN_IMAGE)
.tooltip("Login to RuneLite")
.onClick(this::loginClick)
.build();
logoutButton = NavigationButton.builder()
.tab(false)
.icon(LOGOUT_IMAGE)
.tooltip("Logout of RuneLite")
.onClick(this::logoutClick)
.build();
addAndRemoveButtons();
}
private void addAndRemoveButtons()
{
clientToolbar.removeNavigation(loginButton);
clientToolbar.removeNavigation(logoutButton);
clientToolbar.addNavigation(sessionManager.getAccountSession() == null
? loginButton
: logoutButton);
addSubscriptions();
}
@Override
protected void shutDown() throws Exception
{
clientToolbar.removeNavigation(loginButton);
clientToolbar.removeNavigation(logoutButton);
eventBus.unregister(this);
}
private void loginClick()
private void addSubscriptions()
{
executor.execute(sessionManager::login);
eventBus.subscribe(SessionOpen.class, this, this::onSessionOpen);
}
private void logoutClick()
{
if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(null,
"Are you sure you want to logout from RuneLite?", "Logout Confirmation",
JOptionPane.YES_NO_OPTION))
{
executor.execute(sessionManager::logout);
}
}
@Subscribe
public void onSessionClose(SessionClose e)
{
addAndRemoveButtons();
}
@Subscribe
public void onSessionOpen(SessionOpen sessionOpen)
private void onSessionOpen(SessionOpen sessionOpen)
{
AccountSession session = sessionManager.getAccountSession();
@@ -137,8 +86,6 @@ public class AccountPlugin extends Plugin
}
log.debug("Session opened as {}", session.getUsername());
addAndRemoveButtons();
}
}

View File

@@ -32,6 +32,7 @@ import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.FontTypeFace;
@@ -43,7 +44,7 @@ import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetID;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.achievementdiary.diaries.ArdougneDiaryRequirement;
@@ -66,6 +67,7 @@ import net.runelite.client.util.Text;
description = "Display level requirements in Achievement Diary interface",
tags = {"achievements", "tasks"}
)
@Singleton
public class DiaryRequirementsPlugin extends Plugin
{
private static final String AND_JOINER = ", ";
@@ -77,8 +79,22 @@ public class DiaryRequirementsPlugin extends Plugin
@Inject
private ClientThread clientThread;
@Subscribe
public void onWidgetLoaded(final WidgetLoaded event)
@Inject
private EventBus eventBus;
@Override
protected void startUp() throws Exception
{
eventBus.subscribe(WidgetLoaded.class, this, this::onWidgetLoaded);
}
@Override
protected void shutDown() throws Exception
{
eventBus.unregister(this);
}
private void onWidgetLoaded(final WidgetLoaded event)
{
if (event.getGroupId() == WidgetID.DIARY_QUEST_GROUP_ID)
{
@@ -122,10 +138,6 @@ public class DiaryRequirementsPlugin extends Plugin
}
Map<String, String> skillRequirements = buildRequirements(requirements.getRequirements());
if (skillRequirements == null)
{
return;
}
int offset = 0;
String taskBuffer = "";

View File

@@ -87,7 +87,7 @@ public class ArdougneDiaryRequirement extends GenericDiaryRequirement
new QuestRequirement(Quest.LEGENDS_QUEST));
add("Enter the Magic Guild.",
new SkillRequirement(Skill.MAGIC, 66));
add("Attempt to steal from a chest in Ardougne Castle.",
add("Attempt to steal from King Lathas' chest.",
new SkillRequirement(Skill.THIEVING, 72));
add("Have a zookeeper put you in Ardougne Zoo's monkey cage.",
new QuestRequirement(Quest.MONKEY_MADNESS_I, true));

View File

@@ -27,10 +27,10 @@ package net.runelite.client.plugins.achievementdiary.diaries;
import net.runelite.api.Favour;
import net.runelite.api.Quest;
import net.runelite.api.Skill;
import net.runelite.client.plugins.achievementdiary.GenericDiaryRequirement;
import net.runelite.client.plugins.achievementdiary.SkillRequirement;
import net.runelite.client.plugins.achievementdiary.QuestRequirement;
import net.runelite.client.plugins.achievementdiary.FavourRequirement;
import net.runelite.client.plugins.achievementdiary.GenericDiaryRequirement;
import net.runelite.client.plugins.achievementdiary.QuestRequirement;
import net.runelite.client.plugins.achievementdiary.SkillRequirement;
public class KourendDiaryRequirement extends GenericDiaryRequirement
{

View File

@@ -28,15 +28,42 @@ import java.awt.Color;
import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem;
import net.runelite.client.config.Stub;
@ConfigGroup("agility")
public interface AgilityConfig extends Config
{
@ConfigItem(
position = 0,
keyName = "mainConfig",
name = "Main Config",
description = ""
)
default Stub mainConfig()
{
return new Stub();
}
@ConfigItem(
keyName = "removeDistanceCap",
name = "Remove Distance Cap",
description = "This will remove the distance cap on rendering overlays for agility.",
warning = "<html><center>Enabling this setting on a low end machine may severely affect your fps." +
"<br>Click yes to enable this setting, knowing it might affect performance.</center></html>",
position = 1,
parent = "mainConfig"
)
default boolean removeDistanceCap()
{
return false;
}
@ConfigItem(
keyName = "showLapCount",
name = "Show Lap Count",
description = "Enable/disable the lap counter",
position = 1
position = 2,
parent = "mainConfig"
)
default boolean showLapCount()
{
@@ -45,9 +72,12 @@ public interface AgilityConfig extends Config
@ConfigItem(
keyName = "lapTimeout",
name = "Hide Lap Count (minutes)",
description = "Time until the lap counter hides/resets",
position = 2
name = "Hide Lap Count",
description = "Time in minutes until the lap counter hides/resets",
position = 3,
parent = "mainConfig",
hidden = true,
unhide = "showLapCount"
)
default int lapTimeout()
{
@@ -55,43 +85,86 @@ public interface AgilityConfig extends Config
}
@ConfigItem(
keyName = "overlayColor",
name = "Overlay Color",
description = "Color of Agility overlay",
position = 3
keyName = "lapsToLevel",
name = "Show Laps Until Level",
description = "Show number of laps remaining until next level is reached.",
position = 4,
parent = "mainConfig",
hidden = true,
unhide = "showLapCount"
)
default Color getOverlayColor()
default boolean lapsToLevel()
{
return Color.GREEN;
return false;
}
@ConfigItem(
keyName = "lapsToGoal",
name = "Show Laps Until Goal",
description = "Show number of laps remaining until experience tracker goal is reached",
position = 5,
parent = "mainConfig",
hidden = true,
unhide = "showLapCount"
)
default boolean lapsToGoal()
{
return false;
}
@ConfigItem(
keyName = "agilityArenaTimer",
name = "Agility Arena timer",
description = "Configures whether Agility Arena timer is displayed",
position = 6,
parent = "mainConfig"
)
default boolean showAgilityArenaTimer()
{
return true;
}
@ConfigItem(
keyName = "addLevelsToShortcutOptions",
name = "Show Shortcut Agility Reqs",
description = "Enable/disable showing shortcut agility level requirements in right-click options",
position = 7,
parent = "mainConfig"
)
default boolean showShortcutLevel()
{
return false;
}
@ConfigItem(
position = 8,
keyName = "miscConfig",
name = "Misc Config",
description = ""
)
default Stub miscConfig()
{
return new Stub();
}
@ConfigItem(
keyName = "highlightMarks",
name = "Highlight Marks of Grace",
description = "Enable/disable the highlighting of retrievable Marks of Grace",
position = 4
position = 9,
parent = "miscConfig"
)
default boolean highlightMarks()
{
return true;
}
@ConfigItem(
keyName = "markHighlight",
name = "Mark Highlight Color",
description = "Color of highlighted Marks of Grace",
position = 5
)
default Color getMarkColor()
{
return Color.RED;
}
@ConfigItem(
keyName = "highlightShortcuts",
name = "Highlight Agility Shortcuts",
description = "Enable/disable the highlighting of Agility shortcuts",
position = 6
position = 10,
parent = "miscConfig"
)
default boolean highlightShortcuts()
{
@@ -99,21 +172,49 @@ public interface AgilityConfig extends Config
}
@ConfigItem(
keyName = "trapOverlay",
name = "Show Trap Overlay",
keyName = "showTrapOverlay",
name = "Highlight Traps",
description = "Enable/disable the highlighting of traps on Agility courses",
position = 7
position = 11,
parent = "miscConfig"
)
default boolean showTrapOverlay()
{
return true;
return false;
}
@ConfigItem(
keyName = "agilityArenaNotifier",
name = "Agility Arena notifier",
description = "Notify on ticket location change in Agility Arena",
position = 12,
parent = "miscConfig"
)
default boolean notifyAgilityArena()
{
return false;
}
@ConfigItem(
keyName = "overlayColor",
name = "Global Overlay Color",
description = "Color of Agility overlay",
position = 13,
parent = "miscConfig"
)
default Color getOverlayColor()
{
return Color.GREEN;
}
@ConfigItem(
keyName = "trapHighlight",
name = "Trap Overlay Color",
description = "Color of Agility trap overlay",
position = 8
position = 14,
parent = "miscConfig",
hidden = true,
unhide = "showTrapOverlay"
)
default Color getTrapColor()
{
@@ -121,24 +222,16 @@ public interface AgilityConfig extends Config
}
@ConfigItem(
keyName = "agilityArenaNotifier",
name = "Agility Arena notifier",
description = "Notify on ticket location change in Agility Arena",
position = 9
keyName = "markHighlight",
name = "Mark Highlight Color",
description = "Color of highlighted Marks of Grace",
position = 15,
parent = "miscConfig",
hidden = true,
unhide = "highlightMarks"
)
default boolean notifyAgilityArena()
default Color getMarkColor()
{
return true;
}
@ConfigItem(
keyName = "agilityArenaTimer",
name = "Agility Arena timer",
description = "Configures whether Agility Arena timer is displayed",
position = 10
)
default boolean showAgilityArenaTimer()
{
return true;
return Color.RED;
}
}

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