client: Externals rework
This commit is contained in:
@@ -25,7 +25,7 @@
|
||||
|
||||
import org.apache.tools.ant.filters.ReplaceTokens
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.Date
|
||||
|
||||
plugins {
|
||||
id(Plugins.shadow.first) version Plugins.shadow.second
|
||||
@@ -39,6 +39,7 @@ description = "RuneLite Client"
|
||||
|
||||
dependencies {
|
||||
annotationProcessor(Libraries.lombok)
|
||||
annotationProcessor(Libraries.pf4j)
|
||||
|
||||
compileOnly(Libraries.javax)
|
||||
compileOnly(Libraries.lombok)
|
||||
@@ -58,7 +59,6 @@ dependencies {
|
||||
implementation(Libraries.substance)
|
||||
implementation(Libraries.jopt)
|
||||
implementation(Libraries.apacheCommonsText)
|
||||
implementation(Libraries.plexus)
|
||||
implementation(Libraries.annotations)
|
||||
implementation(Libraries.jogampGluegen)
|
||||
implementation(Libraries.jogampJogl)
|
||||
@@ -67,6 +67,10 @@ dependencies {
|
||||
implementation(Libraries.jooqMeta)
|
||||
implementation(Libraries.sentry)
|
||||
implementation(Libraries.slf4jApi)
|
||||
implementation(Libraries.pf4j) {
|
||||
exclude(group = "org.slf4j")
|
||||
}
|
||||
implementation(Libraries.pf4jUpdate)
|
||||
implementation(project(":http-api"))
|
||||
api(project(":runelite-api"))
|
||||
implementation(Libraries.naturalMouse)
|
||||
@@ -108,6 +112,14 @@ fun launcherVersion(): String {
|
||||
return "-1"
|
||||
}
|
||||
|
||||
fun pluginPath(): String {
|
||||
if (project.hasProperty("pluginPath")) {
|
||||
print(project.property("pluginPath").toString())
|
||||
return project.property("pluginPath").toString()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
tasks {
|
||||
build {
|
||||
finalizedBy("shadowJar")
|
||||
@@ -123,7 +135,8 @@ tasks {
|
||||
"rs.version" to ProjectVersions.rsversion.toString(),
|
||||
"open.osrs.version" to ProjectVersions.openosrsVersion,
|
||||
"open.osrs.builddate" to formatDate(Date()),
|
||||
"launcher.version" to launcherVersion()
|
||||
"launcher.version" to launcherVersion(),
|
||||
"plugin.path" to pluginPath()
|
||||
)
|
||||
|
||||
inputs.properties(tokens)
|
||||
@@ -151,7 +164,6 @@ tasks {
|
||||
group = "openosrs"
|
||||
}
|
||||
|
||||
|
||||
register<JavaExec>("RuneLite.main()") {
|
||||
group = "openosrs"
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ import net.runelite.client.game.XpDropManager;
|
||||
import net.runelite.client.game.chatbox.ChatboxPanelManager;
|
||||
import net.runelite.client.graphics.ModelOutlineRenderer;
|
||||
import net.runelite.client.menus.MenuManager;
|
||||
import net.runelite.client.plugins.ExternalPluginManager;
|
||||
import net.runelite.client.plugins.PluginManager;
|
||||
import net.runelite.client.rs.ClientLoader;
|
||||
import net.runelite.client.rs.ClientUpdateCheckMode;
|
||||
@@ -93,10 +94,13 @@ import org.slf4j.LoggerFactory;
|
||||
@Slf4j
|
||||
public class RuneLite
|
||||
{
|
||||
public static final String SYSTEM_VERSION = "0.0.1";
|
||||
|
||||
public static final File RUNELITE_DIR = new File(System.getProperty("user.home"), ".runelite");
|
||||
public static final File CACHE_DIR = new File(RUNELITE_DIR, "cache");
|
||||
public static final File PROFILES_DIR = new File(RUNELITE_DIR, "profiles");
|
||||
public static final File PLUGIN_DIR = new File(RUNELITE_DIR, "plugins");
|
||||
public static final File EXTERNALPLUGIN_DIR = new File(RUNELITE_DIR, "externalmanager");
|
||||
public static final File SCREENSHOT_DIR = new File(RUNELITE_DIR, "screenshots");
|
||||
public static final File LOGS_DIR = new File(RUNELITE_DIR, "logs");
|
||||
public static final File PLUGINS_DIR = new File(RUNELITE_DIR, "plugins");
|
||||
@@ -105,16 +109,24 @@ public class RuneLite
|
||||
|
||||
@Getter
|
||||
private static Injector injector;
|
||||
|
||||
@Inject
|
||||
public DiscordService discordService;
|
||||
|
||||
@Inject
|
||||
private WorldService worldService;
|
||||
@Inject
|
||||
private PluginManager pluginManager;
|
||||
|
||||
@Inject
|
||||
private ExternalPluginManager externalPluginManager;
|
||||
|
||||
@Inject
|
||||
private ConfigManager configManager;
|
||||
|
||||
@Inject
|
||||
private SessionManager sessionManager;
|
||||
|
||||
@Inject
|
||||
private ClientSessionManager clientSessionManager;
|
||||
|
||||
@@ -351,25 +363,34 @@ public class RuneLite
|
||||
// Tell the plugin manager if client is outdated or not
|
||||
pluginManager.setOutdated(isOutdated);
|
||||
|
||||
// Load external plugins
|
||||
pluginManager.loadExternalPlugins();
|
||||
// Initialize UI
|
||||
RuneLiteSplashScreen.stage(.60, "Initialize UI");
|
||||
clientUI.init(this);
|
||||
|
||||
// Load the plugins, but does not start them yet.
|
||||
// This will initialize configuration
|
||||
pluginManager.loadCorePlugins();
|
||||
RuneLiteSplashScreen.stage(.70, "Finalizing configuration");
|
||||
|
||||
// Load external plugins
|
||||
externalPluginManager.startExternalPluginManager();
|
||||
|
||||
RuneLiteSplashScreen.stage(.75, "Finalizing configuration");
|
||||
|
||||
// Plugins have provided their config, so set default config
|
||||
// to main settings
|
||||
pluginManager.loadDefaultPluginConfiguration();
|
||||
|
||||
// Start client session
|
||||
RuneLiteSplashScreen.stage(.75, "Starting core interface");
|
||||
clientSessionManager.start();
|
||||
externalPluginManager.startExternalUpdateManager();
|
||||
|
||||
// Initialize UI
|
||||
RuneLiteSplashScreen.stage(.80, "Initialize UI");
|
||||
clientUI.init(this);
|
||||
RuneLiteSplashScreen.stage(.77, "Updating external plugins");
|
||||
externalPluginManager.update();
|
||||
|
||||
// Load external plugins
|
||||
pluginManager.loadExternalPlugins();
|
||||
|
||||
// Start client session
|
||||
RuneLiteSplashScreen.stage(.80, "Starting core interface");
|
||||
clientSessionManager.start();
|
||||
|
||||
//Set the world if specified via CLI args - will not work until clientUI.init is called
|
||||
Optional<Integer> worldArg = Optional.ofNullable(System.getProperty("cli.world")).map(Integer::parseInt);
|
||||
@@ -409,6 +430,7 @@ public class RuneLite
|
||||
|
||||
// Start plugins
|
||||
pluginManager.startCorePlugins();
|
||||
externalPluginManager.loadPlugins();
|
||||
|
||||
// Register additional schedulers
|
||||
if (this.client != null)
|
||||
|
||||
@@ -42,6 +42,7 @@ public class RuneLiteProperties
|
||||
private static final String WIKI_LINK = "runelite.wiki.link";
|
||||
private static final String PATREON_LINK = "runelite.patreon.link";
|
||||
private static final String LAUNCHER_VERSION_PROPERTY = "launcher.version";
|
||||
private static final String PLUGIN_PATH = "plugin.path";
|
||||
private static final String TROUBLESHOOTING_LINK = "runelite.wiki.troubleshooting.link";
|
||||
private static final String BUILDING_LINK = "runelite.wiki.building.link";
|
||||
private static final String DNS_CHANGE_LINK = "runelite.dnschange.link";
|
||||
@@ -137,4 +138,11 @@ public class RuneLiteProperties
|
||||
String launcherVersion = properties.getProperty(LAUNCHER_VERSION_PROPERTY);
|
||||
return launcherVersion.equals("-1") ? null : launcherVersion;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getPluginPath()
|
||||
{
|
||||
String pluginPath = properties.getProperty(PLUGIN_PATH);
|
||||
return pluginPath.equals("") ? null : pluginPath;
|
||||
}
|
||||
}
|
||||
@@ -281,8 +281,8 @@ public interface OpenOSRSConfig extends Config
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "enablePlugins",
|
||||
name = "Enable loading of external plugins",
|
||||
description = "Enable loading of external plugins",
|
||||
name = "Enable loading of legacy external plugins",
|
||||
description = "Enable loading of legacy external plugins",
|
||||
position = 16,
|
||||
titleSection = "externalPluginsTitle"
|
||||
)
|
||||
@@ -364,4 +364,23 @@ public interface OpenOSRSConfig extends Config
|
||||
{
|
||||
return Keybind.NOT_SET;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "externalRepositories",
|
||||
name = "",
|
||||
description = "",
|
||||
hidden = true
|
||||
)
|
||||
default String getExternalRepositories()
|
||||
{
|
||||
return "OpenOSRS:https://raw.githubusercontent.com/open-osrs/plugin-hosting/master/";
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "externalRepositories",
|
||||
name = "",
|
||||
description = "",
|
||||
hidden = true
|
||||
)
|
||||
void setExternalRepositories(String val);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Alexander V.
|
||||
* Copyright (c) 2019 Owain van Brakel <https://github.com/Owain94>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -22,23 +22,16 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.client.plugins.chatboxperformance;
|
||||
package net.runelite.client.events;
|
||||
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
import lombok.Data;
|
||||
import net.runelite.api.events.Event;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
|
||||
@ConfigGroup("chatboxperformance")
|
||||
public interface ChatboxPerformanceConfig extends Config
|
||||
@Data
|
||||
public class ExternalPluginChanged implements Event
|
||||
{
|
||||
@ConfigItem(
|
||||
position = 1,
|
||||
keyName = "Chatbox",
|
||||
name = "Toggle gradient",
|
||||
description = "Toggles the gradient inside the chatbox."
|
||||
)
|
||||
default boolean transparentChatBox()
|
||||
{
|
||||
return true; //default enabled, just like in game.
|
||||
}
|
||||
}
|
||||
private final String pluginId;
|
||||
private final Plugin plugin;
|
||||
private final boolean added;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Owain van Brakel <https://github.com/Owain94>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.client.events;
|
||||
|
||||
import lombok.Data;
|
||||
import net.runelite.api.events.Event;
|
||||
|
||||
@Data
|
||||
public class ExternalPluginsLoaded implements Event
|
||||
{}
|
||||
@@ -0,0 +1,49 @@
|
||||
package net.runelite.client.menus;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import net.runelite.api.MenuEntry;
|
||||
import net.runelite.api.util.Text;
|
||||
import net.runelite.api.widgets.WidgetID;
|
||||
import net.runelite.api.widgets.WidgetInfo;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class BankComparableEntry extends AbstractComparableEntry
|
||||
{
|
||||
public BankComparableEntry(String option, String itemName, boolean strictTarget)
|
||||
{
|
||||
this.setOption(option);
|
||||
this.setTarget(Text.standardize(itemName));
|
||||
this.setStrictTarget(strictTarget);
|
||||
}
|
||||
|
||||
public boolean matches(MenuEntry entry)
|
||||
{
|
||||
if (isNotBankWidget(entry.getParam1()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isStrictTarget() && !Text.standardize(entry.getTarget()).equals(this.getTarget()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return StringUtils.containsIgnoreCase(entry.getOption(), this.getOption()) && Text.standardize(entry.getTarget()).contains(this.getTarget());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriority()
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
|
||||
static boolean isNotBankWidget(int widgetID)
|
||||
{
|
||||
final int groupId = WidgetInfo.TO_GROUP(widgetID);
|
||||
|
||||
return groupId != WidgetID.BANK_GROUP_ID
|
||||
&& groupId != WidgetID.BANK_INVENTORY_GROUP_ID
|
||||
&& groupId != WidgetID.GRAND_EXCHANGE_GROUP_ID;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package net.runelite.client.menus;
|
||||
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.client.plugins.menuentryswapper.comparables.BankComparableEntry;
|
||||
|
||||
public interface ComparableEntries
|
||||
{
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package net.runelite.client.menus;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import net.runelite.api.MenuEntry;
|
||||
import net.runelite.api.util.Text;
|
||||
import net.runelite.api.widgets.WidgetID;
|
||||
import net.runelite.api.widgets.WidgetInfo;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class EquipmentComparableEntry extends AbstractComparableEntry
|
||||
{
|
||||
public EquipmentComparableEntry(String option, String itemName)
|
||||
{
|
||||
this.setOption(option);
|
||||
this.setTarget(Text.standardize(itemName));
|
||||
}
|
||||
|
||||
public boolean matches(MenuEntry entry)
|
||||
{
|
||||
final int groupId = WidgetInfo.TO_GROUP(entry.getParam1());
|
||||
|
||||
if (groupId != WidgetID.EQUIPMENT_GROUP_ID)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return StringUtils.equalsIgnoreCase(entry.getOption(), this.getOption())
|
||||
&& Text.standardize(entry.getTarget()).contains(this.getTarget());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package net.runelite.client.menus;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import net.runelite.api.MenuEntry;
|
||||
import net.runelite.api.util.Text;
|
||||
import net.runelite.api.widgets.WidgetID;
|
||||
import net.runelite.api.widgets.WidgetInfo;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class InventoryComparableEntry extends AbstractComparableEntry
|
||||
{
|
||||
public InventoryComparableEntry(String option, String itemName, boolean strictTarget)
|
||||
{
|
||||
this.setOption(option);
|
||||
this.setTarget(Text.standardize(itemName));
|
||||
this.setStrictTarget(strictTarget);
|
||||
}
|
||||
|
||||
public boolean matches(MenuEntry entry)
|
||||
{
|
||||
final int groupId = WidgetInfo.TO_GROUP(entry.getParam1());
|
||||
|
||||
if (groupId != WidgetID.INVENTORY_GROUP_ID)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isStrictTarget() && Text.standardize(entry.getTarget()).equals(this.getTarget()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return StringUtils.containsIgnoreCase(entry.getOption(), this.getOption()) && Text.standardize(entry.getTarget()).contains(this.getTarget());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package net.runelite.client.menus;
|
||||
|
||||
import java.util.List;
|
||||
import net.runelite.api.MenuEntry;
|
||||
import net.runelite.api.util.Text;
|
||||
|
||||
public class ShopComparableEntry extends AbstractComparableEntry
|
||||
{
|
||||
private ShopComparableEntry(final boolean buy, final int amount, final String item)
|
||||
{
|
||||
assert amount == 1 || amount == 5 || amount == 10 || amount == 50 : "Only 1, 5, 10, or 50 are valid amounts";
|
||||
|
||||
this.setOption((buy ? "buy " : "sell ") + amount);
|
||||
this.setTarget(Text.standardize(item));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(final MenuEntry entry)
|
||||
{
|
||||
return Text.standardize(entry.getOption()).equals(this.getOption()) && Text.standardize(entry.getTarget()).equals(this.getTarget());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriority()
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other)
|
||||
{
|
||||
return other instanceof ShopComparableEntry && super.equals(other);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills the array with ShopComparableEntries, getting the items from the fed list
|
||||
*/
|
||||
public static void populateArray(final AbstractComparableEntry[] array, final List<String> items, final boolean buy, final int amount)
|
||||
{
|
||||
for (int i = 0; i < array.length; i++)
|
||||
{
|
||||
array[i] = new ShopComparableEntry(buy, amount, items.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package net.runelite.client.menus;
|
||||
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import net.runelite.api.MenuEntry;
|
||||
import net.runelite.api.util.Text;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class WithdrawComparableEntry extends AbstractComparableEntry
|
||||
{
|
||||
private static String x;
|
||||
|
||||
private final Amount amount;
|
||||
|
||||
private WithdrawComparableEntry(Amount amount, String item)
|
||||
{
|
||||
this.amount = amount;
|
||||
this.setTarget(Text.standardize(item));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(MenuEntry entry)
|
||||
{
|
||||
if (BankComparableEntry.isNotBankWidget(entry.getParam1()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final String option = entry.getOption();
|
||||
|
||||
if (!option.startsWith("Withdraw") && !option.startsWith("Deposit"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (amount == Amount.X)
|
||||
{
|
||||
if (!option.endsWith(x))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!option.endsWith(amount.suffix))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Text.standardize(entry.getTarget()).contains(this.getTarget());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriority()
|
||||
{
|
||||
return 10;
|
||||
}
|
||||
|
||||
public static void setX(int amount)
|
||||
{
|
||||
x = String.valueOf(amount);
|
||||
}
|
||||
|
||||
public static void populateArray(AbstractComparableEntry[] array, List<String> items, Amount amount)
|
||||
{
|
||||
for (int i = 0; i < array.length; i++)
|
||||
{
|
||||
array[i] = new WithdrawComparableEntry(amount, items.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
public enum Amount
|
||||
{
|
||||
ONE("1"),
|
||||
FIVE("5"),
|
||||
TEN("10"),
|
||||
X(null),
|
||||
ALL("All");
|
||||
|
||||
private String suffix;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,521 @@
|
||||
package net.runelite.client.plugins;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.inject.Binder;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.Module;
|
||||
import java.io.File;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import javax.swing.JOptionPane;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.client.RuneLite;
|
||||
import static net.runelite.client.RuneLite.EXTERNALPLUGIN_DIR;
|
||||
import static net.runelite.client.RuneLite.SYSTEM_VERSION;
|
||||
import net.runelite.client.RuneLiteProperties;
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.config.OpenOSRSConfig;
|
||||
import net.runelite.client.eventbus.EventBus;
|
||||
import net.runelite.client.events.ExternalPluginChanged;
|
||||
import net.runelite.client.events.ExternalPluginsLoaded;
|
||||
import net.runelite.client.ui.RuneLiteSplashScreen;
|
||||
import net.runelite.client.util.SwingUtil;
|
||||
import org.pf4j.DefaultPluginManager;
|
||||
import org.pf4j.DependencyResolver;
|
||||
import org.pf4j.JarPluginLoader;
|
||||
import org.pf4j.JarPluginRepository;
|
||||
import org.pf4j.ManifestPluginDescriptorFinder;
|
||||
import org.pf4j.PluginDependency;
|
||||
import org.pf4j.PluginDescriptorFinder;
|
||||
import org.pf4j.PluginLoader;
|
||||
import org.pf4j.PluginRepository;
|
||||
import org.pf4j.PluginWrapper;
|
||||
import org.pf4j.RuntimeMode;
|
||||
import org.pf4j.update.DefaultUpdateRepository;
|
||||
import org.pf4j.update.PluginInfo;
|
||||
import org.pf4j.update.UpdateManager;
|
||||
import org.pf4j.update.UpdateRepository;
|
||||
import org.pf4j.update.VerifyException;
|
||||
|
||||
@Slf4j
|
||||
@Singleton
|
||||
public
|
||||
class ExternalPluginManager
|
||||
{
|
||||
private final PluginManager runelitePluginManager;
|
||||
private final org.pf4j.PluginManager externalPluginManager;
|
||||
|
||||
@Getter(AccessLevel.PUBLIC)
|
||||
private final List<UpdateRepository> repositories = new ArrayList<>();
|
||||
private final OpenOSRSConfig openOSRSConfig;
|
||||
private final ConfigManager configManager;
|
||||
private final EventBus eventBus;
|
||||
|
||||
@Getter(AccessLevel.PUBLIC)
|
||||
private UpdateManager updateManager;
|
||||
|
||||
@Inject
|
||||
public ExternalPluginManager(
|
||||
PluginManager pluginManager,
|
||||
OpenOSRSConfig openOSRSConfig,
|
||||
ConfigManager configManager,
|
||||
EventBus eventBus)
|
||||
{
|
||||
this.runelitePluginManager = pluginManager;
|
||||
this.openOSRSConfig = openOSRSConfig;
|
||||
this.configManager = configManager;
|
||||
this.eventBus = eventBus;
|
||||
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
EXTERNALPLUGIN_DIR.mkdirs();
|
||||
|
||||
boolean debug = RuneLiteProperties.getLauncherVersion() == null && RuneLiteProperties.getPluginPath() != null;
|
||||
|
||||
this.externalPluginManager = new DefaultPluginManager(debug ? Paths.get(RuneLiteProperties.getPluginPath() + File.separator + "release") : EXTERNALPLUGIN_DIR.toPath())
|
||||
{
|
||||
@Override
|
||||
protected PluginDescriptorFinder createPluginDescriptorFinder()
|
||||
{
|
||||
return new ManifestPluginDescriptorFinder();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PluginRepository createPluginRepository()
|
||||
{
|
||||
return new JarPluginRepository(getPluginsRoot());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PluginLoader createPluginLoader()
|
||||
{
|
||||
return new JarPluginLoader(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RuntimeMode getRuntimeMode()
|
||||
{
|
||||
return debug ? RuntimeMode.DEVELOPMENT : RuntimeMode.DEPLOYMENT;
|
||||
}
|
||||
};
|
||||
this.externalPluginManager.setSystemVersion(SYSTEM_VERSION);
|
||||
}
|
||||
|
||||
private static URL toRepositoryUrl(String owner, String name) throws MalformedURLException
|
||||
{
|
||||
return new URL("https://raw.githubusercontent.com/" + owner + "/" + name + "/master/");
|
||||
}
|
||||
|
||||
public static boolean testRepository(String owner, String name)
|
||||
{
|
||||
final List<UpdateRepository> repositories = new ArrayList<>();
|
||||
try
|
||||
{
|
||||
repositories.add(new DefaultUpdateRepository("github", new URL("https://raw.githubusercontent.com/" + owner + "/" + name + "/master/")));
|
||||
}
|
||||
catch (MalformedURLException e)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
DefaultPluginManager testPluginManager = new DefaultPluginManager(EXTERNALPLUGIN_DIR.toPath());
|
||||
UpdateManager updateManager = new UpdateManager(testPluginManager, repositories);
|
||||
|
||||
return updateManager.getPlugins().size() <= 0;
|
||||
}
|
||||
|
||||
public static <T> Predicate<T> not(Predicate<T> t)
|
||||
{
|
||||
return t.negate();
|
||||
}
|
||||
|
||||
public void startExternalPluginManager()
|
||||
{
|
||||
this.externalPluginManager.loadPlugins();
|
||||
}
|
||||
|
||||
public void startExternalUpdateManager()
|
||||
{
|
||||
try
|
||||
{
|
||||
for (String keyval : openOSRSConfig.getExternalRepositories().split(";"))
|
||||
{
|
||||
String[] repository = keyval.split(":", 2);
|
||||
repositories.add(new DefaultUpdateRepository(repository[0], new URL(repository[1])));
|
||||
}
|
||||
}
|
||||
catch (MalformedURLException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
this.updateManager = new UpdateManager(this.externalPluginManager, repositories);
|
||||
}
|
||||
|
||||
public void addRepository(String owner, String name)
|
||||
{
|
||||
try
|
||||
{
|
||||
DefaultUpdateRepository respository = new DefaultUpdateRepository(owner, toRepositoryUrl(owner, name));
|
||||
updateManager.addRepository(respository);
|
||||
saveConfig();
|
||||
}
|
||||
catch (MalformedURLException e)
|
||||
{
|
||||
log.error("Repostitory could not be added");
|
||||
}
|
||||
}
|
||||
|
||||
public void removeRepository(String owner)
|
||||
{
|
||||
updateManager.removeRepository(owner);
|
||||
saveConfig();
|
||||
}
|
||||
|
||||
private void saveConfig()
|
||||
{
|
||||
StringBuilder config = new StringBuilder();
|
||||
|
||||
for (UpdateRepository repository : updateManager.getRepositories())
|
||||
{
|
||||
config.append(repository.getId());
|
||||
config.append(":");
|
||||
config.append(repository.getUrl().toString());
|
||||
config.append(";");
|
||||
}
|
||||
config.deleteCharAt(config.lastIndexOf(";"));
|
||||
|
||||
openOSRSConfig.setExternalRepositories(config.toString());
|
||||
}
|
||||
|
||||
private void instantiatePlugin(String pluginId, Plugin plugin) throws PluginInstantiationException
|
||||
{
|
||||
List<Plugin> scannedPlugins = new ArrayList<>(runelitePluginManager.getPlugins());
|
||||
Class<? extends Plugin> clazz = plugin.getClass();
|
||||
|
||||
PluginDescriptor[] pluginDescriptors = clazz.getAnnotationsByType(PluginDescriptor.class);
|
||||
|
||||
for (PluginDescriptor pluginDescriptor : pluginDescriptors)
|
||||
{
|
||||
if (pluginDescriptor.type() == PluginType.EXTERNAL)
|
||||
{
|
||||
log.error("Class {} is using the the new external plugin loader, it should not use PluginType.EXTERNAL", clazz);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
net.runelite.client.plugins.PluginDependency[] pluginDependencies = clazz.getAnnotationsByType(net.runelite.client.plugins.PluginDependency.class);
|
||||
List<Plugin> deps = new ArrayList<>();
|
||||
for (net.runelite.client.plugins.PluginDependency pluginDependency : pluginDependencies)
|
||||
{
|
||||
Optional<Plugin> dependency = scannedPlugins.stream().filter(p -> p.getClass() == pluginDependency.value()).findFirst();
|
||||
if (dependency.isEmpty())
|
||||
{
|
||||
throw new PluginInstantiationException("Unmet dependency for " + clazz.getSimpleName() + ": " + pluginDependency.value().getSimpleName());
|
||||
}
|
||||
deps.add(dependency.get());
|
||||
}
|
||||
|
||||
Module pluginModule = (Binder binder) ->
|
||||
{
|
||||
//noinspection unchecked
|
||||
binder.bind((Class<Plugin>) plugin.getClass()).toInstance(plugin);
|
||||
binder.install(plugin);
|
||||
|
||||
for (Plugin p : deps)
|
||||
{
|
||||
Module p2 = (Binder binder2) ->
|
||||
{
|
||||
//noinspection unchecked
|
||||
binder2.bind((Class<Plugin>) p.getClass()).toInstance(p);
|
||||
binder2.install(p);
|
||||
};
|
||||
binder.install(p2);
|
||||
}
|
||||
};
|
||||
Injector pluginInjector = RuneLite.getInjector().createChildInjector(pluginModule);
|
||||
pluginInjector.injectMembers(plugin);
|
||||
plugin.injector = pluginInjector;
|
||||
|
||||
// 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
|
||||
{
|
||||
runelitePluginManager.startPlugin(plugin);
|
||||
}
|
||||
catch (PluginInstantiationException ex)
|
||||
{
|
||||
log.warn("unable to start plugin", ex);
|
||||
return;
|
||||
}
|
||||
|
||||
runelitePluginManager.add(plugin);
|
||||
eventBus.post(ExternalPluginChanged.class, new ExternalPluginChanged(pluginId, plugin, true));
|
||||
}
|
||||
|
||||
public void loadPlugins()
|
||||
{
|
||||
this.externalPluginManager.startPlugins();
|
||||
List<PluginWrapper> startedPlugins = getStartedPlugins();
|
||||
int index = 1;
|
||||
|
||||
for (PluginWrapper plugin : startedPlugins)
|
||||
{
|
||||
RuneLiteSplashScreen.stage(.90, 1, "Starting external plugins", index++, startedPlugins.size());
|
||||
loadPlugin(plugin.getPluginId());
|
||||
}
|
||||
|
||||
eventBus.post(ExternalPluginsLoaded.class, new ExternalPluginsLoaded());
|
||||
}
|
||||
|
||||
private void loadPlugin(String pluginId)
|
||||
{
|
||||
try
|
||||
{
|
||||
List<Plugin> extensions = externalPluginManager.getExtensions(Plugin.class, pluginId);
|
||||
for (Plugin plugin : extensions)
|
||||
{
|
||||
try
|
||||
{
|
||||
instantiatePlugin(pluginId, plugin);
|
||||
}
|
||||
catch (PluginInstantiationException e)
|
||||
{
|
||||
log.warn("Error instantiating plugin!", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (NoClassDefFoundError ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
SwingUtil.syncExec(() ->
|
||||
JOptionPane.showMessageDialog(null,
|
||||
pluginId + " could not be loaded due to the following error: " + ex.getMessage(),
|
||||
"External plugin error",
|
||||
JOptionPane.ERROR_MESSAGE));
|
||||
}
|
||||
catch (InvocationTargetException | InterruptedException ignored)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void stopPlugins()
|
||||
{
|
||||
List<PluginWrapper> startedPlugins = ImmutableList.copyOf(getStartedPlugins());
|
||||
|
||||
for (PluginWrapper pluginWrapper : startedPlugins)
|
||||
{
|
||||
String pluginId = pluginWrapper.getDescriptor().getPluginId();
|
||||
List<Plugin> extensions = externalPluginManager.getExtensions(Plugin.class, pluginId);
|
||||
|
||||
for (Plugin plugin : runelitePluginManager.getPlugins())
|
||||
{
|
||||
if (!extensions.get(0).getClass().getName().equals(plugin.getClass().getName()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
runelitePluginManager.stopPlugin(plugin);
|
||||
runelitePluginManager.remove(plugin);
|
||||
|
||||
eventBus.post(ExternalPluginChanged.class, new ExternalPluginChanged(pluginId, plugin, false));
|
||||
}
|
||||
catch (PluginInstantiationException ex)
|
||||
{
|
||||
log.warn("unable to stop plugin", ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Path stopPlugin(String pluginId)
|
||||
{
|
||||
List<PluginWrapper> startedPlugins = ImmutableList.copyOf(getStartedPlugins());
|
||||
|
||||
for (PluginWrapper pluginWrapper : startedPlugins)
|
||||
{
|
||||
if (!pluginId.equals(pluginWrapper.getDescriptor().getPluginId()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Plugin> extensions = externalPluginManager.getExtensions(Plugin.class, pluginId);
|
||||
|
||||
for (Plugin plugin : runelitePluginManager.getPlugins())
|
||||
{
|
||||
if (!extensions.get(0).getClass().getName().equals(plugin.getClass().getName()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
runelitePluginManager.stopPlugin(plugin);
|
||||
runelitePluginManager.remove(plugin);
|
||||
|
||||
eventBus.post(ExternalPluginChanged.class, new ExternalPluginChanged(pluginId, plugin, false));
|
||||
|
||||
return pluginWrapper.getPluginPath();
|
||||
}
|
||||
catch (PluginInstantiationException ex)
|
||||
{
|
||||
log.warn("unable to stop plugin", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void install(String pluginId) throws VerifyException
|
||||
{
|
||||
|
||||
if (getDisabledPlugins().contains(pluginId))
|
||||
{
|
||||
this.externalPluginManager.enablePlugin(pluginId);
|
||||
this.externalPluginManager.startPlugin(pluginId);
|
||||
|
||||
loadPlugin(pluginId);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (getStartedPlugins().stream().anyMatch(ev -> ev.getPluginId().equals(pluginId)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Null version returns the last release version of this plugin for given system version
|
||||
try
|
||||
{
|
||||
PluginInfo.PluginRelease latest = updateManager.getLastPluginRelease(pluginId);
|
||||
|
||||
if (latest == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
SwingUtil.syncExec(() ->
|
||||
JOptionPane.showMessageDialog(null,
|
||||
pluginId + " is outdated and cannot be installed",
|
||||
"Installation error",
|
||||
JOptionPane.ERROR_MESSAGE));
|
||||
}
|
||||
catch (InvocationTargetException | InterruptedException ignored)
|
||||
{
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
updateManager.installPlugin(pluginId, null);
|
||||
|
||||
loadPlugin(pluginId);
|
||||
}
|
||||
catch (DependencyResolver.DependenciesNotFoundException ex)
|
||||
{
|
||||
uninstall(pluginId);
|
||||
|
||||
for (String dep : ex.getDependencies())
|
||||
{
|
||||
install(dep);
|
||||
}
|
||||
|
||||
install(pluginId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void uninstall(String pluginId)
|
||||
{
|
||||
Path pluginPath = stopPlugin(pluginId);
|
||||
|
||||
if (pluginPath == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
externalPluginManager.stopPlugin(pluginId);
|
||||
externalPluginManager.disablePlugin(pluginId);
|
||||
}
|
||||
|
||||
public void update()
|
||||
{
|
||||
if (updateManager.hasUpdates())
|
||||
{
|
||||
List<PluginInfo> updates = updateManager.getUpdates();
|
||||
for (PluginInfo plugin : updates)
|
||||
{
|
||||
PluginInfo.PluginRelease lastRelease = updateManager.getLastPluginRelease(plugin.id);
|
||||
String lastVersion = lastRelease.version;
|
||||
boolean updated = updateManager.updatePlugin(plugin.id, lastVersion);
|
||||
|
||||
if (!updated)
|
||||
{
|
||||
log.warn("Cannot update plugin '{}'", plugin.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Set<String> getDependencies()
|
||||
{
|
||||
Set<String> deps = new HashSet<>();
|
||||
List<PluginWrapper> startedPlugins = getStartedPlugins();
|
||||
|
||||
for (PluginWrapper pluginWrapper : startedPlugins)
|
||||
{
|
||||
for (PluginDependency pluginDependency : pluginWrapper.getDescriptor().getDependencies())
|
||||
{
|
||||
deps.add(pluginDependency.getPluginId());
|
||||
}
|
||||
}
|
||||
|
||||
return deps;
|
||||
}
|
||||
|
||||
public List<String> getDisabledPlugins()
|
||||
{
|
||||
return this.externalPluginManager.getResolvedPlugins()
|
||||
.stream()
|
||||
.filter(not(this.externalPluginManager.getStartedPlugins()::contains))
|
||||
.map(PluginWrapper::getPluginId)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<PluginWrapper> getStartedPlugins()
|
||||
{
|
||||
return this.externalPluginManager.getStartedPlugins();
|
||||
}
|
||||
}
|
||||
@@ -24,23 +24,26 @@
|
||||
*/
|
||||
package net.runelite.client.plugins;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.inject.Binder;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Module;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.Collection;
|
||||
import io.reactivex.functions.Consumer;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Set;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import net.runelite.client.eventbus.AccessorGenerator;
|
||||
import lombok.Value;
|
||||
import net.runelite.api.events.Event;
|
||||
import net.runelite.client.eventbus.EventBus;
|
||||
import net.runelite.client.eventbus.Subscription;
|
||||
import net.runelite.client.eventbus.EventScheduler;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import org.pf4j.ExtensionPoint;
|
||||
|
||||
public abstract class Plugin implements Module
|
||||
public abstract class Plugin implements Module, ExtensionPoint
|
||||
{
|
||||
private Set<Subscription> annotatedSubscriptions = null;
|
||||
private final Set<Subscription> annotatedSubscriptions = findSubscriptions();
|
||||
private final Object annotatedSubsLock = new Object();
|
||||
|
||||
@Getter(AccessLevel.PROTECTED)
|
||||
protected Injector injector;
|
||||
@@ -58,33 +61,53 @@ public abstract class Plugin implements Module
|
||||
{
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final void addAnnotatedSubscriptions(EventBus eventBus)
|
||||
{
|
||||
if (annotatedSubscriptions == null)
|
||||
{
|
||||
Observable.fromCallable(this::findSubscriptions)
|
||||
.subscribeOn(Schedulers.computation())
|
||||
.observeOn(Schedulers.single())
|
||||
.subscribe(subs -> addSubs(eventBus, (annotatedSubscriptions = subs)));
|
||||
}
|
||||
else
|
||||
{
|
||||
addSubs(eventBus, annotatedSubscriptions);
|
||||
}
|
||||
annotatedSubscriptions.forEach(sub -> eventBus.subscribe(sub.type, annotatedSubsLock, sub.method, sub.takeUntil, sub.subscribe, sub.observe));
|
||||
}
|
||||
|
||||
final void removeAnnotatedSubscriptions(EventBus eventBus)
|
||||
{
|
||||
eventBus.unregister(this);
|
||||
eventBus.unregister(annotatedSubsLock);
|
||||
}
|
||||
|
||||
private Set<Subscription> findSubscriptions()
|
||||
{
|
||||
return AccessorGenerator.scanSubscribes(MethodHandles.lookup(), this);
|
||||
ImmutableSet.Builder<Subscription> builder = ImmutableSet.builder();
|
||||
|
||||
for (Method method : this.getClass().getDeclaredMethods())
|
||||
{
|
||||
Subscribe annotation = method.getAnnotation(Subscribe.class);
|
||||
if (annotation == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
assert method.getParameterCount() == 1 : "Methods annotated with @Subscribe should have only one parameter";
|
||||
|
||||
Class<?> type = method.getParameterTypes()[0];
|
||||
|
||||
assert Event.class.isAssignableFrom(type) : "Parameters of methods annotated with @Subscribe should implement net.runelite.api.events.Event";
|
||||
assert method.getReturnType() == void.class : "Methods annotated with @Subscribe should have a void return type";
|
||||
|
||||
method.setAccessible(true);
|
||||
|
||||
Subscription sub = new Subscription(type.asSubclass(Event.class), event -> method.invoke(this, event), annotation.takeUntil(), annotation.subscribe(), annotation.observe());
|
||||
|
||||
builder.add(sub);
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private void addSubs(EventBus eventBus, Collection<Subscription> subs)
|
||||
@Value
|
||||
private static class Subscription
|
||||
{
|
||||
subs.forEach(s -> s.subscribe(eventBus, this));
|
||||
private final Class type;
|
||||
private final Consumer method;
|
||||
private final int takeUntil;
|
||||
private final EventScheduler subscribe;
|
||||
private final EventScheduler observe;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ public enum PluginType
|
||||
SKILLING("Skilling"),
|
||||
UTILITY("Utilities"),
|
||||
MISCELLANEOUS("Miscellaneous"),
|
||||
EXTERNAL("External"),
|
||||
EXTERNAL("Legacy External"),
|
||||
IMPORTANT("System"),
|
||||
MINIGAME("Minigame"),
|
||||
GAMEMODE("Gamemode"),
|
||||
@@ -26,4 +26,4 @@ public enum PluginType
|
||||
{
|
||||
return getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,9 +24,7 @@
|
||||
*/
|
||||
package net.runelite.client.plugins.chatboxperformance;
|
||||
|
||||
import com.google.inject.Provides;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.GameState;
|
||||
import net.runelite.api.ScriptID;
|
||||
@@ -37,18 +35,14 @@ import net.runelite.api.widgets.WidgetPositionMode;
|
||||
import net.runelite.api.widgets.WidgetSizeMode;
|
||||
import net.runelite.api.widgets.WidgetType;
|
||||
import net.runelite.client.callback.ClientThread;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.events.ConfigChanged;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import net.runelite.client.plugins.PluginType;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "Chatbox performance",
|
||||
type = PluginType.MISCELLANEOUS
|
||||
hidden = true
|
||||
)
|
||||
@Singleton
|
||||
public class ChatboxPerformancePlugin extends Plugin
|
||||
{
|
||||
@Inject
|
||||
@@ -57,24 +51,6 @@ public class ChatboxPerformancePlugin extends Plugin
|
||||
@Inject
|
||||
private ClientThread clientThread;
|
||||
|
||||
@Inject
|
||||
private ChatboxPerformanceConfig config;
|
||||
|
||||
@Subscribe
|
||||
public void onConfigChanged(ConfigChanged event)
|
||||
{
|
||||
if (event.getGroup().equals("chatboxperformance"))
|
||||
{
|
||||
fixDarkBackground();
|
||||
}
|
||||
}
|
||||
|
||||
@Provides
|
||||
ChatboxPerformanceConfig getConfig(ConfigManager configManager)
|
||||
{
|
||||
return configManager.getConfig(ChatboxPerformanceConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startUp()
|
||||
{
|
||||
@@ -108,7 +84,7 @@ public class ChatboxPerformancePlugin extends Plugin
|
||||
|
||||
private void fixDarkBackground()
|
||||
{
|
||||
int currOpacity = 255;
|
||||
int currOpacity = 256;
|
||||
int prevY = 0;
|
||||
Widget[] children = client.getWidget(WidgetInfo.CHATBOX_TRANSPARENT_BACKGROUND).getDynamicChildren();
|
||||
Widget prev = null;
|
||||
@@ -132,10 +108,7 @@ public class ChatboxPerformancePlugin extends Plugin
|
||||
}
|
||||
|
||||
prevY = w.getRelativeY();
|
||||
if (config.transparentChatBox())
|
||||
{
|
||||
currOpacity -= 3;
|
||||
}
|
||||
currOpacity -= 3; // Rough number, can't get exactly the same as Jagex because of rounding
|
||||
prev = w;
|
||||
}
|
||||
if (prev != null)
|
||||
@@ -146,7 +119,7 @@ public class ChatboxPerformancePlugin extends Plugin
|
||||
|
||||
private void fixWhiteLines(boolean upperLine)
|
||||
{
|
||||
int currOpacity = 255;
|
||||
int currOpacity = 256;
|
||||
int prevWidth = 0;
|
||||
Widget[] children = client.getWidget(WidgetInfo.CHATBOX_TRANSPARENT_LINES).getDynamicChildren();
|
||||
Widget prev = null;
|
||||
@@ -184,4 +157,4 @@ public class ChatboxPerformancePlugin extends Plugin
|
||||
prev.setOpacity(currOpacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1122,9 +1122,7 @@ class ConfigPanel extends PluginPanel
|
||||
if (event.getPlugin() == this.pluginConfig.getPlugin())
|
||||
{
|
||||
SwingUtilities.invokeLater(() ->
|
||||
{
|
||||
pluginToggle.setSelected(event.isLoaded());
|
||||
});
|
||||
pluginToggle.setSelected(event.isLoaded()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,6 +72,8 @@ import net.runelite.client.config.RuneLiteConfig;
|
||||
import net.runelite.client.eventbus.EventBus;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.events.ConfigChanged;
|
||||
import net.runelite.client.events.ExternalPluginChanged;
|
||||
import net.runelite.client.events.ExternalPluginsLoaded;
|
||||
import net.runelite.client.events.PluginChanged;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
@@ -183,6 +185,14 @@ public class PluginListPanel extends PluginPanel
|
||||
}
|
||||
});
|
||||
|
||||
eventBus.subscribe(ExternalPluginsLoaded.class, this, ignored -> {
|
||||
eventBus.subscribe(ExternalPluginChanged.class, this, ev -> {
|
||||
SwingUtilities.invokeLater(this::rebuildPluginList);
|
||||
});
|
||||
|
||||
SwingUtilities.invokeLater(this::rebuildPluginList);
|
||||
});
|
||||
|
||||
muxer = new MultiplexingPluginPanel(this);
|
||||
|
||||
searchBar = new IconTextField();
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
package net.runelite.client.plugins.info;
|
||||
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
|
||||
@ConfigGroup("info")
|
||||
public interface InfoConfig extends Config
|
||||
{
|
||||
@ConfigItem(
|
||||
keyName = "showGithub",
|
||||
name = "Show the OpenOSRS Github",
|
||||
description = "Configures if you want to show the OpenOSRS Github or not.",
|
||||
position = 0
|
||||
)
|
||||
default boolean showGithub()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "showLauncher",
|
||||
name = "Show the Launcher download",
|
||||
description = "Configures if you want to show the OpenOSRS Launcher download or not.",
|
||||
position = 1
|
||||
)
|
||||
default boolean showLauncher()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "showLogDir",
|
||||
name = "Show Log Directory",
|
||||
description = "Configures if you want to show the Log Directory or not.",
|
||||
position = 2
|
||||
)
|
||||
default boolean showLogDir()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "showRuneliteDir",
|
||||
name = "Show Runelite Directory",
|
||||
description = "Configures if you want to show the Runelite directory or not.",
|
||||
position = 3
|
||||
)
|
||||
default boolean showRuneliteDir()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "showPluginsDir",
|
||||
name = "Show Plugins Directory",
|
||||
description = "Configures if you want to show the Plugins Directory or not.",
|
||||
position = 4
|
||||
)
|
||||
default boolean showPluginsDir()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "showScreenshotsDir",
|
||||
name = "Show Screenshots Directory",
|
||||
description = "Configures if you want to show the Screenshots Directory or not.",
|
||||
position = 5
|
||||
)
|
||||
default boolean showScreenshotsDir()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "showPhysicalDir",
|
||||
name = "Show Physical Locations",
|
||||
description = "Configures if you want to show the Physical Directory Locations or not.",
|
||||
position = 6
|
||||
)
|
||||
default boolean showPhysicalDir()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -143,57 +143,36 @@ class InfoPanel extends PluginPanel
|
||||
actionsContainer.add(buildLinkPanel(GITHUB_ICON, "License info", "for distribution", "https://github.com/open-osrs/runelite/blob/master/LICENSE"));
|
||||
actionsContainer.add(buildLinkPanel(PATREON_ICON, "Patreon to support", "the OpenOSRS Devs", RuneLiteProperties.getPatreonLink()));
|
||||
actionsContainer.add(buildLinkPanel(DISCORD_ICON, "Talk to us on our", "Discord Server", "https://discord.gg/OpenOSRS"));
|
||||
if (plugin.isShowGithub())
|
||||
{
|
||||
actionsContainer.add(buildLinkPanel(GITHUB_ICON, "OpenOSRS Github", "", "https://github.com/open-osrs"));
|
||||
}
|
||||
if (plugin.isShowLauncher())
|
||||
{
|
||||
actionsContainer.add(buildLinkPanel(IMPORT_ICON, "Launcher Download", "for the latest launcher", "https://github.com/open-osrs/launcher/releases"));
|
||||
}
|
||||
if (plugin.isShowRuneliteDir())
|
||||
{
|
||||
actionsContainer.add(buildLinkPanel(FOLDER_ICON, "Open Runelite Directory", "for your .properties file", RUNELITE_DIR));
|
||||
}
|
||||
if (plugin.isShowLogDir())
|
||||
{
|
||||
actionsContainer.add(buildLinkPanel(FOLDER_ICON, "Open Logs Directory", "for bug reports", LOGS_DIR));
|
||||
}
|
||||
if (plugin.isShowPluginsDir())
|
||||
{
|
||||
actionsContainer.add(buildLinkPanel(FOLDER_ICON, "Open Plugins Directory", "for external plugins", PLUGINS_DIR));
|
||||
}
|
||||
if (plugin.isShowScreenshotsDir())
|
||||
{
|
||||
actionsContainer.add(buildLinkPanel(FOLDER_ICON, "Open Screenshots Directory", "for your screenshots", SCREENSHOT_DIR));
|
||||
}
|
||||
actionsContainer.add(buildLinkPanel(GITHUB_ICON, "OpenOSRS Github", "", "https://github.com/open-osrs"));
|
||||
actionsContainer.add(buildLinkPanel(IMPORT_ICON, "Launcher Download", "for the latest launcher", "https://github.com/open-osrs/launcher/releases"));
|
||||
actionsContainer.add(buildLinkPanel(FOLDER_ICON, "Open Runelite Directory", "for your .properties file", RUNELITE_DIR));
|
||||
actionsContainer.add(buildLinkPanel(FOLDER_ICON, "Open Logs Directory", "for bug reports", LOGS_DIR));
|
||||
actionsContainer.add(buildLinkPanel(FOLDER_ICON, "Open Plugins Directory", "for external plugins", PLUGINS_DIR));
|
||||
actionsContainer.add(buildLinkPanel(FOLDER_ICON, "Open Screenshots Directory", "for your screenshots", SCREENSHOT_DIR));
|
||||
|
||||
if (plugin.isShowPhysicalDir())
|
||||
{
|
||||
JPanel pathPanel = new JPanel();
|
||||
pathPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
pathPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
|
||||
pathPanel.setLayout(new GridLayout(0, 1));
|
||||
JPanel pathPanel = new JPanel();
|
||||
pathPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
pathPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
|
||||
pathPanel.setLayout(new GridLayout(0, 1));
|
||||
|
||||
JLabel rldirectory = new JLabel(htmlLabel("Runelite Directory: ", RUNELITE_DIRECTORY));
|
||||
rldirectory.setFont(smallFont);
|
||||
JLabel rldirectory = new JLabel(htmlLabel("Runelite Directory: ", RUNELITE_DIRECTORY));
|
||||
rldirectory.setFont(smallFont);
|
||||
|
||||
JLabel logdirectory = new JLabel(htmlLabel("Log Directory: ", LOG_DIRECTORY));
|
||||
logdirectory.setFont(smallFont);
|
||||
JLabel logdirectory = new JLabel(htmlLabel("Log Directory: ", LOG_DIRECTORY));
|
||||
logdirectory.setFont(smallFont);
|
||||
|
||||
JLabel pluginsdirectory = new JLabel(htmlLabel("Plugins Directory: ", PLUGINS_DIRECTORY));
|
||||
pluginsdirectory.setFont(smallFont);
|
||||
JLabel pluginsdirectory = new JLabel(htmlLabel("Plugins Directory: ", PLUGINS_DIRECTORY));
|
||||
pluginsdirectory.setFont(smallFont);
|
||||
|
||||
JLabel screenshotsdirectory = new JLabel(htmlLabel("Screenshot Directory: ", SCREENSHOT_DIRECTORY));
|
||||
screenshotsdirectory.setFont(smallFont);
|
||||
JLabel screenshotsdirectory = new JLabel(htmlLabel("Screenshot Directory: ", SCREENSHOT_DIRECTORY));
|
||||
screenshotsdirectory.setFont(smallFont);
|
||||
|
||||
pathPanel.add(rldirectory);
|
||||
pathPanel.add(logdirectory);
|
||||
pathPanel.add(pluginsdirectory);
|
||||
pathPanel.add(screenshotsdirectory);
|
||||
pathPanel.add(rldirectory);
|
||||
pathPanel.add(logdirectory);
|
||||
pathPanel.add(pluginsdirectory);
|
||||
pathPanel.add(screenshotsdirectory);
|
||||
|
||||
add(pathPanel, BorderLayout.SOUTH);
|
||||
}
|
||||
add(pathPanel, BorderLayout.SOUTH);
|
||||
add(versionPanel, BorderLayout.NORTH);
|
||||
add(actionsContainer, BorderLayout.CENTER);
|
||||
|
||||
|
||||
@@ -24,13 +24,9 @@
|
||||
*/
|
||||
package net.runelite.client.plugins.info;
|
||||
|
||||
import com.google.inject.Provides;
|
||||
import java.awt.image.BufferedImage;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.events.ConfigChanged;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
@@ -50,35 +46,11 @@ import net.runelite.client.util.ImageUtil;
|
||||
@Singleton
|
||||
public class InfoPlugin extends Plugin
|
||||
{
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private boolean showLogDir;
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private boolean showRuneliteDir;
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private boolean showPluginsDir;
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private boolean showScreenshotsDir;
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private boolean showGithub;
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private boolean showLauncher;
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private boolean showPhysicalDir;
|
||||
|
||||
@Inject
|
||||
private ClientToolbar clientToolbar;
|
||||
|
||||
@Inject
|
||||
private InfoConfig config;
|
||||
|
||||
private NavigationButton navButton;
|
||||
|
||||
@Provides
|
||||
InfoConfig provideConfig(ConfigManager configManager)
|
||||
{
|
||||
return configManager.getConfig(InfoConfig.class);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
private void onConfigChanged(ConfigChanged event)
|
||||
{
|
||||
@@ -86,15 +58,11 @@ public class InfoPlugin extends Plugin
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
updateConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startUp()
|
||||
{
|
||||
updateConfig();
|
||||
|
||||
InfoPanel panel = injector.getInstance(InfoPanel.class);
|
||||
|
||||
final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "info_icon.png");
|
||||
@@ -114,15 +82,4 @@ public class InfoPlugin extends Plugin
|
||||
{
|
||||
clientToolbar.removeNavigation(navButton);
|
||||
}
|
||||
|
||||
private void updateConfig()
|
||||
{
|
||||
this.showGithub = config.showGithub();
|
||||
this.showLauncher = config.showLauncher();
|
||||
this.showLogDir = config.showLogDir();
|
||||
this.showRuneliteDir = config.showRuneliteDir();
|
||||
this.showPluginsDir = config.showPluginsDir();
|
||||
this.showScreenshotsDir = config.showScreenshotsDir();
|
||||
this.showPhysicalDir = config.showPhysicalDir();
|
||||
}
|
||||
}
|
||||
@@ -27,42 +27,29 @@
|
||||
package net.runelite.client.plugins.openosrs;
|
||||
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.awt.image.BufferedImage;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.api.AnimationID;
|
||||
import net.runelite.api.ChatMessageType;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.GameObject;
|
||||
import static net.runelite.api.ObjectID.CANNON_BASE;
|
||||
import net.runelite.api.Player;
|
||||
import net.runelite.api.Projectile;
|
||||
import static net.runelite.api.ProjectileID.CANNONBALL;
|
||||
import static net.runelite.api.ProjectileID.GRANITE_CANNONBALL;
|
||||
import static net.runelite.api.ScriptID.BANK_PIN_OP;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
import net.runelite.api.events.CannonChanged;
|
||||
import net.runelite.api.events.CannonPlaced;
|
||||
import net.runelite.api.events.ChatMessage;
|
||||
import net.runelite.api.events.GameObjectSpawned;
|
||||
import net.runelite.api.events.GameTick;
|
||||
import net.runelite.api.events.ProjectileSpawned;
|
||||
import net.runelite.api.events.ScriptCallbackEvent;
|
||||
import net.runelite.api.widgets.WidgetID;
|
||||
import static net.runelite.api.widgets.WidgetInfo.*;
|
||||
import net.runelite.client.callback.ClientThread;
|
||||
import net.runelite.client.config.Keybind;
|
||||
import net.runelite.client.config.OpenOSRSConfig;
|
||||
import net.runelite.client.eventbus.EventBus;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.events.ConfigChanged;
|
||||
import net.runelite.client.input.KeyListener;
|
||||
import net.runelite.client.input.KeyManager;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import net.runelite.client.plugins.openosrs.externals.PluginManagerPanel;
|
||||
import net.runelite.client.ui.ClientToolbar;
|
||||
import net.runelite.client.ui.NavigationButton;
|
||||
import net.runelite.client.util.HotkeyListener;
|
||||
import net.runelite.client.util.ImageUtil;
|
||||
|
||||
@PluginDescriptor(
|
||||
loadWhenOutdated = true, // prevent users from disabling
|
||||
@@ -88,21 +75,10 @@ public class OpenOSRSPlugin extends Plugin
|
||||
private ClientThread clientThread;
|
||||
|
||||
@Inject
|
||||
private EventBus eventBus;
|
||||
private ClientToolbar clientToolbar;
|
||||
|
||||
private static final Pattern NUMBER_PATTERN = Pattern.compile("([0-9]+)");
|
||||
private static final int MAX_CBALLS = 30;
|
||||
private int cballsLeft;
|
||||
private WorldPoint cannonPosition;
|
||||
private GameObject cannon;
|
||||
private boolean cannonPlaced;
|
||||
private boolean skipProjectileCheckThisTick;
|
||||
private NavigationButton navButton;
|
||||
|
||||
private int entered = -1;
|
||||
private int enterIdx;
|
||||
private boolean expectInput;
|
||||
private boolean detach;
|
||||
private Keybind keybind;
|
||||
private final HotkeyListener hotkeyListener = new HotkeyListener(() -> this.keybind)
|
||||
{
|
||||
@Override
|
||||
@@ -113,10 +89,27 @@ public class OpenOSRSPlugin extends Plugin
|
||||
client.setOculusOrbNormalSpeed(detach ? 36 : 12);
|
||||
}
|
||||
};
|
||||
private int entered = -1;
|
||||
private int enterIdx;
|
||||
private boolean expectInput;
|
||||
private boolean detach;
|
||||
private Keybind keybind;
|
||||
|
||||
@Override
|
||||
protected void startUp()
|
||||
{
|
||||
PluginManagerPanel panel = injector.getInstance(PluginManagerPanel.class);
|
||||
|
||||
final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "externalmanager_icon.png");
|
||||
|
||||
navButton = NavigationButton.builder()
|
||||
.tooltip("External Plugin Manager")
|
||||
.icon(icon)
|
||||
.priority(1)
|
||||
.panel(panel)
|
||||
.build();
|
||||
clientToolbar.addNavigation(navButton);
|
||||
|
||||
entered = -1;
|
||||
enterIdx = 0;
|
||||
expectInput = false;
|
||||
@@ -127,6 +120,8 @@ public class OpenOSRSPlugin extends Plugin
|
||||
@Override
|
||||
protected void shutDown()
|
||||
{
|
||||
clientToolbar.removeNavigation(navButton);
|
||||
|
||||
entered = 0;
|
||||
enterIdx = 0;
|
||||
expectInput = false;
|
||||
@@ -189,136 +184,6 @@ public class OpenOSRSPlugin extends Plugin
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
private void onChatMessage(ChatMessage event)
|
||||
{
|
||||
if (event.getType() != ChatMessageType.SPAM && event.getType() != ChatMessageType.GAMEMESSAGE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.getMessage().equals("You add the furnace."))
|
||||
{
|
||||
cballsLeft = 0;
|
||||
eventBus.post(CannonPlaced.class, new CannonPlaced(true, cannonPosition, cannon));
|
||||
eventBus.post(CannonChanged.class, new CannonChanged(null, cballsLeft));
|
||||
cannonPlaced = true;
|
||||
}
|
||||
|
||||
if (event.getMessage().contains("You pick up the cannon")
|
||||
|| event.getMessage().contains("Your cannon has decayed. Speak to Nulodion to get a new one!"))
|
||||
{
|
||||
cballsLeft = 0;
|
||||
eventBus.post(CannonPlaced.class, new CannonPlaced(false, null, null));
|
||||
eventBus.post(CannonChanged.class, new CannonChanged(null, cballsLeft));
|
||||
cannonPlaced = false;
|
||||
}
|
||||
|
||||
if (event.getMessage().startsWith("You load the cannon with"))
|
||||
{
|
||||
Matcher m = NUMBER_PATTERN.matcher(event.getMessage());
|
||||
if (m.find())
|
||||
{
|
||||
// The cannon will usually refill to MAX_CBALLS, but if the
|
||||
// player didn't have enough cannonballs in their inventory,
|
||||
// it could fill up less than that. Filling the cannon to
|
||||
// cballsLeft + amt is not always accurate though because our
|
||||
// counter doesn't decrease if the player has been too far away
|
||||
// from the cannon due to the projectiels not being in memory,
|
||||
// so our counter can be higher than it is supposed to be.
|
||||
int amt = Integer.parseInt(m.group());
|
||||
if (cballsLeft + amt >= MAX_CBALLS)
|
||||
{
|
||||
skipProjectileCheckThisTick = true;
|
||||
cballsLeft = MAX_CBALLS;
|
||||
}
|
||||
else
|
||||
{
|
||||
cballsLeft += amt;
|
||||
}
|
||||
}
|
||||
else if (event.getMessage().equals("You load the cannon with one cannonball."))
|
||||
{
|
||||
if (cballsLeft + 1 >= MAX_CBALLS)
|
||||
{
|
||||
skipProjectileCheckThisTick = true;
|
||||
cballsLeft = MAX_CBALLS;
|
||||
}
|
||||
else
|
||||
{
|
||||
cballsLeft++;
|
||||
}
|
||||
}
|
||||
|
||||
eventBus.post(CannonChanged.class, new CannonChanged(null, cballsLeft));
|
||||
}
|
||||
|
||||
if (event.getMessage().contains("Your cannon is out of ammo!"))
|
||||
{
|
||||
skipProjectileCheckThisTick = true;
|
||||
|
||||
// If the player was out of range of the cannon, some cannonballs
|
||||
// may have been used without the client knowing, so having this
|
||||
// extra check is a good idea.
|
||||
cballsLeft = 0;
|
||||
|
||||
eventBus.post(CannonChanged.class, new CannonChanged(null, cballsLeft));
|
||||
}
|
||||
|
||||
if (event.getMessage().startsWith("You unload your cannon and receive Cannonball")
|
||||
|| event.getMessage().startsWith("You unload your cannon and receive Granite cannonball"))
|
||||
{
|
||||
skipProjectileCheckThisTick = true;
|
||||
|
||||
cballsLeft = 0;
|
||||
|
||||
eventBus.post(CannonChanged.class, new CannonChanged(null, cballsLeft));
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
private void onGameTick(GameTick event)
|
||||
{
|
||||
skipProjectileCheckThisTick = false;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
private void onGameObjectSpawned(GameObjectSpawned event)
|
||||
{
|
||||
final GameObject gameObject = event.getGameObject();
|
||||
|
||||
final Player localPlayer = client.getLocalPlayer();
|
||||
if (gameObject.getId() == CANNON_BASE && !cannonPlaced &&
|
||||
localPlayer != null && localPlayer.getWorldLocation().distanceTo(gameObject.getWorldLocation()) <= 2 &&
|
||||
localPlayer.getAnimation() == AnimationID.BURYING_BONES)
|
||||
{
|
||||
cannonPosition = gameObject.getWorldLocation();
|
||||
cannon = gameObject;
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
private void onProjectileSpawned(ProjectileSpawned event)
|
||||
{
|
||||
if (!cannonPlaced)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final Projectile projectile = event.getProjectile();
|
||||
|
||||
if ((projectile.getId() == CANNONBALL || projectile.getId() == GRANITE_CANNONBALL) && cannonPosition != null)
|
||||
{
|
||||
final WorldPoint projectileLoc = WorldPoint.fromLocal(client, projectile.getX1(), projectile.getY1(), client.getPlane());
|
||||
|
||||
if (projectileLoc.equals(cannonPosition) && !skipProjectileCheckThisTick)
|
||||
{
|
||||
cballsLeft--;
|
||||
eventBus.post(CannonChanged.class, new CannonChanged(projectile.getId(), cballsLeft));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleKey(char c)
|
||||
{
|
||||
if (client.getWidget(WidgetID.BANK_PIN_GROUP_ID, BANK_PIN_INSTRUCTION_TEXT.getChildId()) == null
|
||||
@@ -397,4 +262,4 @@ public class OpenOSRSPlugin extends Plugin
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
97
runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/ExternalBox.java
vendored
Normal file
97
runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/ExternalBox.java
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
package net.runelite.client.plugins.openosrs.externals;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.net.URL;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.border.CompoundBorder;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.text.DefaultCaret;
|
||||
import net.runelite.client.ui.ColorScheme;
|
||||
import net.runelite.client.ui.FontManager;
|
||||
import org.pf4j.update.PluginInfo;
|
||||
|
||||
public class ExternalBox extends JPanel
|
||||
{
|
||||
private static final Font normalFont = FontManager.getRunescapeFont();
|
||||
private static final Font smallFont = FontManager.getRunescapeSmallFont();
|
||||
|
||||
PluginInfo pluginInfo;
|
||||
JLabel install = new JLabel();
|
||||
JMultilineLabel description = new JMultilineLabel();
|
||||
|
||||
ExternalBox(String name, URL url)
|
||||
{
|
||||
this(name, url.toString().replace("https://raw.githubusercontent.com/", "").replace("/master/", ""));
|
||||
}
|
||||
|
||||
ExternalBox(PluginInfo pluginInfo)
|
||||
{
|
||||
this(pluginInfo.name, pluginInfo.description);
|
||||
}
|
||||
|
||||
ExternalBox(String name, String desc)
|
||||
{
|
||||
setLayout(new BorderLayout());
|
||||
setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
|
||||
JPanel titleWrapper = new JPanel(new BorderLayout());
|
||||
titleWrapper.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
titleWrapper.setBorder(new CompoundBorder(
|
||||
BorderFactory.createMatteBorder(0, 0, 1, 0, ColorScheme.DARK_GRAY_COLOR),
|
||||
BorderFactory.createLineBorder(ColorScheme.DARKER_GRAY_COLOR)
|
||||
));
|
||||
|
||||
JLabel title = new JLabel();
|
||||
title.setText(name);
|
||||
title.setFont(normalFont);
|
||||
title.setBorder(null);
|
||||
title.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
title.setPreferredSize(new Dimension(0, 24));
|
||||
title.setForeground(Color.WHITE);
|
||||
title.setBorder(new EmptyBorder(0, 8, 0, 0));
|
||||
|
||||
JPanel titleActions = new JPanel(new BorderLayout(3, 0));
|
||||
titleActions.setBorder(new EmptyBorder(0, 0, 0, 8));
|
||||
titleActions.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
|
||||
titleActions.add(install, BorderLayout.EAST);
|
||||
|
||||
titleWrapper.add(title, BorderLayout.CENTER);
|
||||
titleWrapper.add(titleActions, BorderLayout.EAST);
|
||||
|
||||
description.setText(desc);
|
||||
description.setFont(smallFont);
|
||||
description.setDisabledTextColor(Color.WHITE);
|
||||
description.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
|
||||
add(titleWrapper, BorderLayout.NORTH);
|
||||
add(description, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
public static class JMultilineLabel extends JTextArea
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public JMultilineLabel()
|
||||
{
|
||||
super();
|
||||
setEditable(false);
|
||||
setCursor(null);
|
||||
setOpaque(false);
|
||||
setFocusable(false);
|
||||
setWrapStyleWord(true);
|
||||
setLineWrap(true);
|
||||
setBorder(new EmptyBorder(0, 8, 0, 8));
|
||||
setAlignmentY(JLabel.CENTER_ALIGNMENT);
|
||||
|
||||
DefaultCaret caret = (DefaultCaret) getCaret();
|
||||
caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
831
runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/PluginManagerPanel.java
vendored
Normal file
831
runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/PluginManagerPanel.java
vendored
Normal file
@@ -0,0 +1,831 @@
|
||||
package net.runelite.client.plugins.openosrs.externals;
|
||||
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.Insets;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import javax.inject.Inject;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JRadioButton;
|
||||
import javax.swing.JScrollBar;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.JToggleButton;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.client.eventbus.EventBus;
|
||||
import net.runelite.client.events.ExternalPluginChanged;
|
||||
import net.runelite.client.events.ExternalPluginsLoaded;
|
||||
import net.runelite.client.plugins.ExternalPluginManager;
|
||||
import net.runelite.client.ui.ColorScheme;
|
||||
import net.runelite.client.ui.DynamicGridLayout;
|
||||
import net.runelite.client.ui.FontManager;
|
||||
import net.runelite.client.ui.PluginPanel;
|
||||
import net.runelite.client.ui.components.IconTextField;
|
||||
import net.runelite.client.ui.components.shadowlabel.JShadowedLabel;
|
||||
import net.runelite.client.util.DeferredDocumentChangedListener;
|
||||
import net.runelite.client.util.ImageUtil;
|
||||
import net.runelite.client.util.SwingUtil;
|
||||
import org.apache.commons.text.similarity.JaroWinklerDistance;
|
||||
import org.pf4j.update.PluginInfo;
|
||||
import org.pf4j.update.UpdateManager;
|
||||
import org.pf4j.update.UpdateRepository;
|
||||
import org.pf4j.update.VerifyException;
|
||||
|
||||
@Slf4j
|
||||
public class PluginManagerPanel extends PluginPanel
|
||||
{
|
||||
private static final JaroWinklerDistance DISTANCE = new JaroWinklerDistance();
|
||||
|
||||
private static final ImageIcon SECTION_EXPAND_ICON;
|
||||
private static final ImageIcon SECTION_EXPAND_ICON_HOVER;
|
||||
private static final ImageIcon SECTION_RETRACT_ICON;
|
||||
private static final ImageIcon SECTION_RETRACT_ICON_HOVER;
|
||||
private static final ImageIcon ADD_ICON;
|
||||
private static final ImageIcon ADD_HOVER_ICON;
|
||||
private static final ImageIcon DELETE_ICON;
|
||||
private static final ImageIcon DELETE_HOVER_ICON;
|
||||
private static final ImageIcon DELETE_ICON_GRAY;
|
||||
private static final ImageIcon DELETE_HOVER_ICON_GRAY;
|
||||
|
||||
static
|
||||
{
|
||||
final BufferedImage backIcon = ImageUtil.getResourceStreamFromClass(PluginManagerPanel.class, "config_back_icon.png");
|
||||
final BufferedImage orangeBackIcon = ImageUtil.fillImage(backIcon, ColorScheme.BRAND_BLUE);
|
||||
|
||||
final BufferedImage sectionRetractIcon = ImageUtil.rotateImage(orangeBackIcon, Math.PI * 1.5);
|
||||
SECTION_RETRACT_ICON = new ImageIcon(sectionRetractIcon);
|
||||
SECTION_RETRACT_ICON_HOVER = new ImageIcon(ImageUtil.alphaOffset(sectionRetractIcon, -100));
|
||||
|
||||
final BufferedImage sectionExpandIcon = ImageUtil.rotateImage(orangeBackIcon, Math.PI);
|
||||
SECTION_EXPAND_ICON = new ImageIcon(sectionExpandIcon);
|
||||
SECTION_EXPAND_ICON_HOVER = new ImageIcon(ImageUtil.alphaOffset(sectionExpandIcon, -100));
|
||||
|
||||
final BufferedImage addIcon =
|
||||
ImageUtil.recolorImage(
|
||||
ImageUtil.getResourceStreamFromClass(PluginManagerPanel.class, "add_icon.png"), ColorScheme.BRAND_BLUE
|
||||
);
|
||||
ADD_ICON = new ImageIcon(addIcon);
|
||||
ADD_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(addIcon, 0.53f));
|
||||
|
||||
final BufferedImage deleteImg =
|
||||
ImageUtil.recolorImage(
|
||||
ImageUtil.resizeCanvas(
|
||||
ImageUtil.getResourceStreamFromClass(PluginManagerPanel.class, "delete_icon.png"), 14, 14
|
||||
), ColorScheme.BRAND_BLUE
|
||||
);
|
||||
DELETE_ICON = new ImageIcon(deleteImg);
|
||||
DELETE_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(deleteImg, 0.53f));
|
||||
|
||||
DELETE_ICON_GRAY = new ImageIcon(ImageUtil.grayscaleImage(deleteImg));
|
||||
DELETE_HOVER_ICON_GRAY = new ImageIcon(ImageUtil.alphaOffset(ImageUtil.grayscaleImage(deleteImg), 0.53f));
|
||||
}
|
||||
|
||||
private final ExternalPluginManager externalPluginManager;
|
||||
private final UpdateManager updateManager;
|
||||
private final ScheduledExecutorService executor;
|
||||
private final IconTextField searchBar = new IconTextField();
|
||||
private final List<PluginInfo> installedPluginsList = new ArrayList<>();
|
||||
private final List<PluginInfo> availablePluginsList = new ArrayList<>();
|
||||
private final JPanel repositoriesPanel = new JPanel();
|
||||
private final JPanel installedPluginsPanel = new JPanel(new GridBagLayout());
|
||||
private final JPanel availablePluginsPanel = new JPanel(new GridBagLayout());
|
||||
private String filterMode = "Available plugins (All)";
|
||||
private int scrollBarPosition;
|
||||
private JScrollBar scrollbar;
|
||||
private Set<String> deps;
|
||||
|
||||
@Inject
|
||||
private PluginManagerPanel(ExternalPluginManager externalPluginManager, EventBus eventBus, ScheduledExecutorService executor)
|
||||
{
|
||||
super(false);
|
||||
|
||||
this.externalPluginManager = externalPluginManager;
|
||||
this.updateManager = externalPluginManager.getUpdateManager();
|
||||
this.executor = executor;
|
||||
|
||||
eventBus.subscribe(ExternalPluginsLoaded.class, "loading-externals", (e) -> {
|
||||
eventBus.unregister("loading-externals");
|
||||
eventBus.subscribe(ExternalPluginChanged.class, this, this::onExternalPluginChanged);
|
||||
reloadPlugins();
|
||||
});
|
||||
|
||||
DeferredDocumentChangedListener listener = new DeferredDocumentChangedListener();
|
||||
listener.addChangeListener(e ->
|
||||
onSearchBarChanged());
|
||||
|
||||
searchBar.setIcon(IconTextField.Icon.SEARCH);
|
||||
searchBar.setPreferredSize(new Dimension(PluginPanel.PANEL_WIDTH - 20, 30));
|
||||
searchBar.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
searchBar.setHoverBackgroundColor(ColorScheme.DARK_GRAY_HOVER_COLOR);
|
||||
searchBar.getDocument().addDocumentListener(listener);
|
||||
|
||||
buildPanel();
|
||||
}
|
||||
|
||||
private static boolean mismatchesSearchTerms(String search, PluginInfo pluginInfo)
|
||||
{
|
||||
final String[] searchTerms = search.toLowerCase().split(" ");
|
||||
final String[] pluginTerms = (pluginInfo.name + " " + pluginInfo.description).toLowerCase().split("[/\\s]");
|
||||
for (String term : searchTerms)
|
||||
{
|
||||
if (Arrays.stream(pluginTerms).noneMatch((t) -> t.contains(term) ||
|
||||
DISTANCE.apply(t, term) > 0.9))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private JPanel addSection(String name, JPanel sectionContent)
|
||||
{
|
||||
final JPanel section = new JPanel();
|
||||
section.setLayout(new BoxLayout(section, BoxLayout.Y_AXIS));
|
||||
|
||||
JPanel item = new JPanel();
|
||||
item.setLayout(new BorderLayout());
|
||||
|
||||
JLabel headerLabel = new JLabel(name);
|
||||
headerLabel.setFont(FontManager.getRunescapeFont());
|
||||
headerLabel.setForeground(ColorScheme.BRAND_BLUE);
|
||||
|
||||
final JToggleButton collapse = new JToggleButton(SECTION_EXPAND_ICON);
|
||||
|
||||
SwingUtil.removeButtonDecorations(collapse);
|
||||
collapse.setRolloverIcon(SECTION_EXPAND_ICON_HOVER);
|
||||
collapse.setSelectedIcon(SECTION_RETRACT_ICON);
|
||||
collapse.setRolloverSelectedIcon(SECTION_RETRACT_ICON_HOVER);
|
||||
collapse.setToolTipText("Retract");
|
||||
collapse.setPreferredSize(new Dimension(20, 20));
|
||||
collapse.setFont(collapse.getFont().deriveFont(16.0f));
|
||||
collapse.setBorder(null);
|
||||
collapse.setMargin(new Insets(0, 0, 0, 0));
|
||||
headerLabel.setBorder(new EmptyBorder(0, 6, 0, 0));
|
||||
|
||||
item.add(collapse, BorderLayout.WEST);
|
||||
item.add(headerLabel, BorderLayout.CENTER);
|
||||
|
||||
final JPanel sectionContents = new JPanel();
|
||||
sectionContents.setLayout(new DynamicGridLayout(0, 1, 0, 5));
|
||||
sectionContents.setBorder(new EmptyBorder(6, 5, 0, 0));
|
||||
section.add(item, BorderLayout.NORTH);
|
||||
section.add(sectionContents, BorderLayout.SOUTH);
|
||||
|
||||
sectionContents.add(sectionContent);
|
||||
|
||||
final MouseAdapter adapter = new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e)
|
||||
{
|
||||
toggleSection(collapse, sectionContents);
|
||||
}
|
||||
};
|
||||
collapse.addActionListener(e -> toggleSection(collapse, sectionContents));
|
||||
headerLabel.addMouseListener(adapter);
|
||||
|
||||
return section;
|
||||
}
|
||||
|
||||
private void toggleSection(JToggleButton button, JPanel contents)
|
||||
{
|
||||
boolean newState = !contents.isVisible();
|
||||
button.setSelected(newState);
|
||||
contents.setVisible(newState);
|
||||
button.setToolTipText(newState ? "Retract" : "Expand");
|
||||
SwingUtilities.invokeLater(() ->
|
||||
{
|
||||
contents.revalidate();
|
||||
contents.repaint();
|
||||
});
|
||||
}
|
||||
|
||||
private JPanel filterPanel()
|
||||
{
|
||||
JPanel filterPanel = new JPanel();
|
||||
filterPanel.setLayout(new BorderLayout(0, 5));
|
||||
filterPanel.setBorder(new EmptyBorder(0, 10, 10, 10));
|
||||
|
||||
JRadioButton repositories = new JRadioButton("Repositories");
|
||||
repositories.setSelected(filterMode.equals("Repositories"));
|
||||
JRadioButton plugins = new JRadioButton("Plugins");
|
||||
plugins.setSelected(filterMode.contains("plugins"));
|
||||
|
||||
JRadioButton available = new JRadioButton("Available");
|
||||
available.setSelected(filterMode.contains("Available"));
|
||||
JRadioButton installed = new JRadioButton("Installed");
|
||||
installed.setSelected(filterMode.contains("Installed"));
|
||||
|
||||
List<UpdateRepository> updateRepositories = externalPluginManager.getRepositories();
|
||||
List<JRadioButton> authors = new ArrayList<>();
|
||||
JRadioButton allPlugins = new JRadioButton("All");
|
||||
allPlugins.setSelected(filterMode.contains("All"));
|
||||
|
||||
authors.add(allPlugins);
|
||||
for (UpdateRepository repository : updateRepositories)
|
||||
{
|
||||
JRadioButton author = new JRadioButton(repository.getId());
|
||||
author.setSelected(filterMode.contains(repository.getId()));
|
||||
|
||||
author.addActionListener(ev -> {
|
||||
filterMode = filterMode.contains("Installed") ? "Installed plugins (" + repository.getId() + ")" : "Available plugins (" + repository.getId() + ")";
|
||||
onSearchBarChanged();
|
||||
buildPanel();
|
||||
});
|
||||
|
||||
authors.add(author);
|
||||
}
|
||||
|
||||
repositories.addActionListener(ev -> {
|
||||
filterMode = "Repositories";
|
||||
buildPanel();
|
||||
});
|
||||
|
||||
plugins.addActionListener(ev -> {
|
||||
filterMode = "Available plugins (All)";
|
||||
onSearchBarChanged();
|
||||
buildPanel();
|
||||
});
|
||||
|
||||
available.addActionListener(ev -> {
|
||||
filterMode = "Available plugins (All)";
|
||||
onSearchBarChanged();
|
||||
buildPanel();
|
||||
});
|
||||
|
||||
installed.addActionListener(ev -> {
|
||||
filterMode = "Installed plugins (All)";
|
||||
onSearchBarChanged();
|
||||
buildPanel();
|
||||
});
|
||||
|
||||
allPlugins.addActionListener(ev -> {
|
||||
filterMode = filterMode.contains("Installed") ? "Installed plugins (All)" : "Available plugins (All)";
|
||||
onSearchBarChanged();
|
||||
buildPanel();
|
||||
});
|
||||
|
||||
RadioButtonPanel mainRadioPanel = new RadioButtonPanel("Show", repositories, plugins);
|
||||
RadioButtonPanel pluginRadioPanel = new RadioButtonPanel("Plugins", available, installed);
|
||||
RadioButtonPanel authorRadioPanel = new RadioButtonPanel("Author", authors.toArray(new JRadioButton[0]));
|
||||
|
||||
filterPanel.add(mainRadioPanel, BorderLayout.NORTH);
|
||||
|
||||
if (!filterMode.equals("Repositories"))
|
||||
{
|
||||
filterPanel.add(pluginRadioPanel, BorderLayout.CENTER);
|
||||
}
|
||||
if (!filterMode.equals("Repositories") && updateRepositories.size() > 1)
|
||||
{
|
||||
filterPanel.add(authorRadioPanel, BorderLayout.SOUTH);
|
||||
}
|
||||
|
||||
return filterPanel;
|
||||
}
|
||||
|
||||
private void buildPanel()
|
||||
{
|
||||
removeAll();
|
||||
|
||||
setLayout(new BorderLayout(0, 10));
|
||||
setBackground(ColorScheme.DARK_GRAY_COLOR);
|
||||
|
||||
add(titleBar(), BorderLayout.NORTH);
|
||||
add(wrapContainer(getContentPanels()), BorderLayout.CENTER);
|
||||
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
|
||||
private JLabel titleLabel(String text)
|
||||
{
|
||||
JLabel title = new JShadowedLabel();
|
||||
|
||||
title.setFont(FontManager.getRunescapeSmallFont());
|
||||
title.setForeground(Color.WHITE);
|
||||
title.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
title.setText("<html><body style = 'text-align:center'>" + text + "</body></html>");
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
private JPanel titleBar()
|
||||
{
|
||||
JPanel titlePanel = new JPanel(new BorderLayout());
|
||||
titlePanel.setBorder(new EmptyBorder(10, 10, 10, 10));
|
||||
|
||||
JLabel title = new JLabel();
|
||||
JLabel addRepo = new JLabel(ADD_ICON);
|
||||
|
||||
title.setText("External Plugin Manager");
|
||||
title.setForeground(Color.WHITE);
|
||||
|
||||
addRepo.setToolTipText("Add new repository");
|
||||
addRepo.addMouseListener(new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mousePressed(MouseEvent mouseEvent)
|
||||
{
|
||||
JTextField owner = new JTextField();
|
||||
JTextField name = new JTextField();
|
||||
Object[] message = {
|
||||
"Repository owner:", owner,
|
||||
"Repository name:", name
|
||||
};
|
||||
|
||||
int option = JOptionPane.showConfirmDialog(null, message, "Add repository", JOptionPane.OK_CANCEL_OPTION);
|
||||
if (option != JOptionPane.OK_OPTION || owner.getText().equals("") || name.getText().equals(""))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ExternalPluginManager.testRepository(owner.getText(), name.getText()))
|
||||
{
|
||||
JOptionPane.showMessageDialog(null, "This doesn't appear to be a valid repository.", "Error!", JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
externalPluginManager.addRepository(owner.getText(), name.getText());
|
||||
|
||||
repositories();
|
||||
reloadPlugins();
|
||||
buildPanel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent mouseEvent)
|
||||
{
|
||||
addRepo.setIcon(ADD_HOVER_ICON);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent mouseEvent)
|
||||
{
|
||||
addRepo.setIcon(ADD_ICON);
|
||||
}
|
||||
});
|
||||
|
||||
titlePanel.add(title, BorderLayout.WEST);
|
||||
titlePanel.add(addRepo, BorderLayout.EAST);
|
||||
|
||||
return titlePanel;
|
||||
}
|
||||
|
||||
// Wrap the panel inside a scroll pane
|
||||
private JScrollPane wrapContainer(final JPanel container)
|
||||
{
|
||||
final JPanel wrapped = new JPanel(new BorderLayout());
|
||||
wrapped.add(container, BorderLayout.NORTH);
|
||||
|
||||
final JScrollPane scroller = new JScrollPane(wrapped);
|
||||
scroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
scroller.getVerticalScrollBar().setPreferredSize(new Dimension(8, 0));
|
||||
|
||||
this.scrollbar = scroller.getVerticalScrollBar();
|
||||
|
||||
return scroller;
|
||||
}
|
||||
|
||||
private void onExternalPluginChanged(ExternalPluginChanged externalPluginChanged)
|
||||
{
|
||||
String pluginId = externalPluginChanged.getPluginId();
|
||||
Optional<Component> externalBox;
|
||||
|
||||
if (externalPluginChanged.isAdded())
|
||||
{
|
||||
externalBox = Arrays.stream(
|
||||
availablePluginsPanel.getComponents()
|
||||
).filter(extBox ->
|
||||
extBox instanceof ExternalBox && ((ExternalBox) extBox).pluginInfo.id.equals(pluginId)
|
||||
).findFirst();
|
||||
}
|
||||
else
|
||||
{
|
||||
externalBox = Arrays.stream(
|
||||
installedPluginsPanel.getComponents()
|
||||
).filter(extBox ->
|
||||
extBox instanceof ExternalBox && ((ExternalBox) extBox).pluginInfo.id.equals(pluginId)
|
||||
).findFirst();
|
||||
}
|
||||
|
||||
if (externalBox.isEmpty())
|
||||
{
|
||||
log.info("EXTERNALBOX IS EMPTY: {}", pluginId);
|
||||
return;
|
||||
}
|
||||
|
||||
ExternalBox extBox = (ExternalBox) externalBox.get();
|
||||
deps = externalPluginManager.getDependencies();
|
||||
|
||||
try
|
||||
{
|
||||
SwingUtil.syncExec(() ->
|
||||
{
|
||||
if (externalPluginChanged.isAdded())
|
||||
{
|
||||
availablePluginsPanel.remove(externalBox.get());
|
||||
availablePluginsList.remove(extBox.pluginInfo);
|
||||
|
||||
installedPluginsList.add(extBox.pluginInfo);
|
||||
installedPluginsList.sort(Comparator.naturalOrder());
|
||||
|
||||
installedPlugins();
|
||||
|
||||
pluginInstallButton(extBox.install, extBox.pluginInfo, true, deps.contains(extBox.pluginInfo.id));
|
||||
}
|
||||
else
|
||||
{
|
||||
installedPluginsPanel.remove(externalBox.get());
|
||||
installedPluginsList.remove(extBox.pluginInfo);
|
||||
|
||||
availablePluginsList.add(extBox.pluginInfo);
|
||||
availablePluginsList.sort(Comparator.naturalOrder());
|
||||
|
||||
availablePlugins();
|
||||
|
||||
pluginInstallButton(extBox.install, extBox.pluginInfo, false, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (InvocationTargetException | InterruptedException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void reloadPlugins()
|
||||
{
|
||||
fetchPlugins();
|
||||
|
||||
try
|
||||
{
|
||||
SwingUtil.syncExec(() -> {
|
||||
this.installedPlugins();
|
||||
this.availablePlugins();
|
||||
|
||||
resetScrollValue();
|
||||
});
|
||||
|
||||
}
|
||||
catch (InvocationTargetException | InterruptedException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
resetScrollValue();
|
||||
}
|
||||
|
||||
private void onSearchBarChanged()
|
||||
{
|
||||
if (filterMode.contains("Installed plugins"))
|
||||
{
|
||||
installedPlugins();
|
||||
}
|
||||
else if (filterMode.contains("Available plugins"))
|
||||
{
|
||||
availablePlugins();
|
||||
}
|
||||
}
|
||||
|
||||
private JPanel getContentPanels()
|
||||
{
|
||||
JPanel contentPanel = new JPanel();
|
||||
contentPanel.setLayout(new GridBagLayout());
|
||||
GridBagConstraints c = new GridBagConstraints();
|
||||
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
c.weightx = 1;
|
||||
c.gridx = 0;
|
||||
c.gridy = 0;
|
||||
c.insets = new Insets(5, 0, 5, 0);
|
||||
|
||||
contentPanel.add(addSection("Filter", filterPanel()), c);
|
||||
|
||||
if (filterMode.equals("Repositories"))
|
||||
{
|
||||
c.gridy++;
|
||||
contentPanel.add(repositoriesPanel(), c);
|
||||
}
|
||||
else if (filterMode.contains("Installed plugins"))
|
||||
{
|
||||
c.gridy++;
|
||||
contentPanel.add(installedPluginsPanel(), c);
|
||||
}
|
||||
else if (filterMode.contains("Available plugins"))
|
||||
{
|
||||
c.gridy++;
|
||||
contentPanel.add(availablePluginsPanel(), c);
|
||||
}
|
||||
|
||||
return contentPanel;
|
||||
}
|
||||
|
||||
private JPanel repositoriesPanel()
|
||||
{
|
||||
JPanel installedRepositoriesPanel = new JPanel();
|
||||
installedRepositoriesPanel.setLayout(new BorderLayout(0, 5));
|
||||
installedRepositoriesPanel.setBorder(new EmptyBorder(0, 10, 10, 10));
|
||||
installedRepositoriesPanel.add(titleLabel("Repositories"), BorderLayout.NORTH);
|
||||
installedRepositoriesPanel.add(repositoriesPanel, BorderLayout.CENTER);
|
||||
|
||||
repositories();
|
||||
|
||||
return installedRepositoriesPanel;
|
||||
}
|
||||
|
||||
private JPanel installedPluginsPanel()
|
||||
{
|
||||
JPanel installedPluginsContainer = new JPanel();
|
||||
installedPluginsContainer.setLayout(new BorderLayout(0, 5));
|
||||
installedPluginsContainer.setBorder(new EmptyBorder(0, 10, 10, 10));
|
||||
installedPluginsContainer.add(titleLabel(filterMode.replace(" (All)", "")), BorderLayout.NORTH);
|
||||
installedPluginsContainer.add(searchBar, BorderLayout.CENTER);
|
||||
installedPluginsContainer.add(installedPluginsPanel, BorderLayout.SOUTH);
|
||||
|
||||
return installedPluginsContainer;
|
||||
}
|
||||
|
||||
private JPanel availablePluginsPanel()
|
||||
{
|
||||
JPanel availablePluginsContainer = new JPanel();
|
||||
availablePluginsContainer.setLayout(new BorderLayout(0, 5));
|
||||
availablePluginsContainer.setBorder(new EmptyBorder(0, 10, 10, 10));
|
||||
availablePluginsContainer.add(titleLabel(filterMode.replace(" (All)", "")), BorderLayout.NORTH);
|
||||
availablePluginsContainer.add(searchBar, BorderLayout.CENTER);
|
||||
availablePluginsContainer.add(availablePluginsPanel, BorderLayout.SOUTH);
|
||||
|
||||
return availablePluginsContainer;
|
||||
}
|
||||
|
||||
private void repositories()
|
||||
{
|
||||
repositoriesPanel.removeAll();
|
||||
repositoriesPanel.setLayout(new GridBagLayout());
|
||||
GridBagConstraints c = new GridBagConstraints();
|
||||
|
||||
for (UpdateRepository repository : externalPluginManager.getRepositories())
|
||||
{
|
||||
String name = repository.getId();
|
||||
ExternalBox repositoryBox = new ExternalBox(name, repository.getUrl());
|
||||
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
c.weightx = 1.0;
|
||||
c.gridy += 1;
|
||||
c.insets = new Insets(5, 0, 0, 0);
|
||||
|
||||
repositoriesPanel.add(repositoryBox, c);
|
||||
|
||||
if (name.equals("OpenOSRS"))
|
||||
{
|
||||
repositoryBox.install.setVisible(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
repositoryBox.install.setIcon(DELETE_ICON);
|
||||
repositoryBox.install.setToolTipText("Remove");
|
||||
repositoryBox.install.addMouseListener(new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e)
|
||||
{
|
||||
externalPluginManager.removeRepository(name);
|
||||
|
||||
repositories();
|
||||
reloadPlugins();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e)
|
||||
{
|
||||
repositoryBox.install.setIcon(DELETE_HOVER_ICON);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent e)
|
||||
{
|
||||
repositoryBox.install.setIcon(DELETE_ICON);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void fetchPlugins()
|
||||
{
|
||||
List<PluginInfo> availablePlugins = null;
|
||||
List<PluginInfo> plugins = null;
|
||||
List<String> disabledPlugins = externalPluginManager.getDisabledPlugins();
|
||||
|
||||
try
|
||||
{
|
||||
availablePlugins = updateManager.getAvailablePlugins();
|
||||
plugins = updateManager.getPlugins();
|
||||
}
|
||||
catch (JsonSyntaxException ex)
|
||||
{
|
||||
log.error(String.valueOf(ex));
|
||||
}
|
||||
|
||||
if (availablePlugins == null || plugins == null)
|
||||
{
|
||||
JOptionPane.showMessageDialog(null, "The external plugin list could not be loaded.", "Error", JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
availablePluginsList.clear();
|
||||
installedPluginsList.clear();
|
||||
deps = externalPluginManager.getDependencies();
|
||||
|
||||
for (PluginInfo pluginInfo : plugins)
|
||||
{
|
||||
if (availablePlugins.contains(pluginInfo) || disabledPlugins.contains(pluginInfo.id))
|
||||
{
|
||||
availablePluginsList.add(pluginInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
installedPluginsList.add(pluginInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void installedPlugins()
|
||||
{
|
||||
JPanel panel = new JPanel();
|
||||
panel.setLayout(new GridBagLayout());
|
||||
GridBagConstraints c = new GridBagConstraints();
|
||||
|
||||
installedPluginsPanel.removeAll();
|
||||
String search = searchBar.getText();
|
||||
|
||||
for (PluginInfo pluginInfo : installedPluginsList)
|
||||
{
|
||||
|
||||
if ((!search.equals("") && mismatchesSearchTerms(search, pluginInfo)) ||
|
||||
(!filterMode.contains("All") && !filterMode.contains(pluginInfo.getRepositoryId())))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ExternalBox pluginBox = new ExternalBox(pluginInfo);
|
||||
pluginBox.pluginInfo = pluginInfo;
|
||||
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
c.weightx = 1.0;
|
||||
c.gridy += 1;
|
||||
c.insets = new Insets(5, 0, 0, 0);
|
||||
|
||||
pluginInstallButton(pluginBox.install, pluginInfo, true, deps.contains(pluginInfo.id));
|
||||
installedPluginsPanel.add(pluginBox, c);
|
||||
}
|
||||
|
||||
if (installedPluginsPanel.getComponents().length < 1)
|
||||
{
|
||||
installedPluginsPanel.add(titleLabel("No plugins found"));
|
||||
}
|
||||
}
|
||||
|
||||
private void availablePlugins()
|
||||
{
|
||||
JPanel panel = new JPanel();
|
||||
panel.setLayout(new GridBagLayout());
|
||||
GridBagConstraints c = new GridBagConstraints();
|
||||
|
||||
availablePluginsPanel.removeAll();
|
||||
String search = searchBar.getText();
|
||||
|
||||
for (PluginInfo pluginInfo : availablePluginsList)
|
||||
{
|
||||
if ((!search.equals("") && mismatchesSearchTerms(search, pluginInfo)) ||
|
||||
(!filterMode.contains("All") && !filterMode.contains(pluginInfo.getRepositoryId())))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ExternalBox pluginBox = new ExternalBox(pluginInfo);
|
||||
pluginBox.pluginInfo = pluginInfo;
|
||||
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
c.weightx = 1.0;
|
||||
c.gridy += 1;
|
||||
c.insets = new Insets(5, 0, 0, 0);
|
||||
|
||||
pluginInstallButton(pluginBox.install, pluginInfo, false, false);
|
||||
availablePluginsPanel.add(pluginBox, c);
|
||||
}
|
||||
|
||||
if (availablePluginsPanel.getComponents().length < 1)
|
||||
{
|
||||
availablePluginsPanel.add(titleLabel("No plugins found"));
|
||||
}
|
||||
}
|
||||
|
||||
private void pluginInstallButton(JLabel install, PluginInfo pluginInfo, boolean installed, boolean hideAction)
|
||||
{
|
||||
install.setIcon(installed ? hideAction ? DELETE_ICON_GRAY : DELETE_ICON : ADD_ICON);
|
||||
if (!hideAction)
|
||||
{
|
||||
install.setToolTipText(installed ? "Uninstall" : "Install");
|
||||
}
|
||||
install.addMouseListener(new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e)
|
||||
{
|
||||
saveScrollValue();
|
||||
|
||||
if (installed)
|
||||
{
|
||||
if (hideAction)
|
||||
{
|
||||
JOptionPane.showMessageDialog(null, "This plugin can't be uninstalled because one or more other plugins have a dependency on it.", "Error!", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
else
|
||||
{
|
||||
install.setIcon(null);
|
||||
install.setText("Uninstalling");
|
||||
executor.submit(() -> externalPluginManager.uninstall(pluginInfo.id));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
install.setIcon(null);
|
||||
install.setText("Installing");
|
||||
executor.submit(() -> installPlugin(pluginInfo));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e)
|
||||
{
|
||||
if (install.getText().toLowerCase().contains("installing"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
install.setIcon(installed ? hideAction ? DELETE_HOVER_ICON_GRAY : DELETE_HOVER_ICON : ADD_HOVER_ICON);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent e)
|
||||
{
|
||||
if (install.getText().toLowerCase().contains("installing"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
install.setIcon(installed ? hideAction ? DELETE_ICON_GRAY : DELETE_ICON : ADD_ICON);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void installPlugin(PluginInfo pluginInfo)
|
||||
{
|
||||
try
|
||||
{
|
||||
externalPluginManager.install(pluginInfo.id);
|
||||
}
|
||||
catch (VerifyException ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
SwingUtil.syncExec(() ->
|
||||
JOptionPane.showMessageDialog(null, pluginInfo.name + " could not be installed, the hash could not be verified.", "Error!", JOptionPane.ERROR_MESSAGE));
|
||||
}
|
||||
catch (InvocationTargetException | InterruptedException ignored)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveScrollValue()
|
||||
{
|
||||
scrollBarPosition = scrollbar.getValue();
|
||||
}
|
||||
|
||||
private void resetScrollValue()
|
||||
{
|
||||
scrollbar.setValue(scrollBarPosition);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package net.runelite.client.plugins.openosrs.externals;
|
||||
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.GridLayout;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.ButtonGroup;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JRadioButton;
|
||||
|
||||
public class RadioButtonPanel extends JPanel
|
||||
{
|
||||
public static final Orientation VERTICAL = Orientation.VERTICAL;
|
||||
public static final Orientation HORIZONTAL = Orientation.HORIZONTAL;
|
||||
|
||||
private ButtonGroup buttonGroup = new ButtonGroup();
|
||||
|
||||
public RadioButtonPanel(String title, JRadioButton... buttons)
|
||||
{
|
||||
this(VERTICAL, title, buttons);
|
||||
}
|
||||
|
||||
public RadioButtonPanel(Orientation orientation, String title, JRadioButton... buttons)
|
||||
{
|
||||
if (orientation == VERTICAL)
|
||||
{
|
||||
this.setLayout(new GridLayout(buttons.length, 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.setLayout(new FlowLayout(FlowLayout.LEADING));
|
||||
}
|
||||
|
||||
for (JRadioButton button : buttons)
|
||||
{
|
||||
buttonGroup.add(button);
|
||||
this.add(button);
|
||||
}
|
||||
|
||||
if (title != null)
|
||||
{
|
||||
this.setBorder(BorderFactory.createTitledBorder(
|
||||
BorderFactory.createEtchedBorder(), title));
|
||||
}
|
||||
}
|
||||
|
||||
public void clearSelection()
|
||||
{
|
||||
buttonGroup.clearSelection();
|
||||
}
|
||||
|
||||
private enum Orientation
|
||||
{
|
||||
VERTICAL,
|
||||
HORIZONTAL
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package net.runelite.client.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.swing.Timer;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
|
||||
public class DeferredDocumentChangedListener implements DocumentListener
|
||||
{
|
||||
private final Timer timer;
|
||||
private final List<ChangeListener> listeners;
|
||||
|
||||
public DeferredDocumentChangedListener()
|
||||
{
|
||||
listeners = new ArrayList<>(25);
|
||||
timer = new Timer(200, e -> fireStateChanged());
|
||||
timer.setRepeats(false);
|
||||
}
|
||||
|
||||
public void addChangeListener(ChangeListener listener)
|
||||
{
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
private void fireStateChanged()
|
||||
{
|
||||
if (!listeners.isEmpty())
|
||||
{
|
||||
ChangeEvent evt = new ChangeEvent(this);
|
||||
for (ChangeListener listener : listeners)
|
||||
{
|
||||
listener.stateChanged(evt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e)
|
||||
{
|
||||
timer.restart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e)
|
||||
{
|
||||
timer.restart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e)
|
||||
{
|
||||
timer.restart();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -29,6 +29,7 @@ import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.Font;
|
||||
import java.awt.Frame;
|
||||
import java.awt.Image;
|
||||
@@ -40,6 +41,7 @@ import java.awt.event.MouseEvent;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.BiConsumer;
|
||||
@@ -472,4 +474,16 @@ public class SwingUtil
|
||||
{
|
||||
button.addItemListener(l -> button.setToolTipText(button.isSelected() ? on : off));
|
||||
}
|
||||
|
||||
public static void syncExec(final Runnable r) throws InvocationTargetException, InterruptedException
|
||||
{
|
||||
if (EventQueue.isDispatchThread())
|
||||
{
|
||||
r.run();
|
||||
}
|
||||
else
|
||||
{
|
||||
EventQueue.invokeAndWait(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,3 +12,4 @@ runelite.wiki.troubleshooting.link=https://github.com/open-osrs/runelite/wiki/Tr
|
||||
runelite.wiki.building.link=https://github.com/open-osrs/runelite/wiki/Building-with-IntelliJ-IDEA
|
||||
runelite.dnschange.link=https://1.1.1.1/dns/
|
||||
launcher.version=@launcher.version@
|
||||
plugin.path=@plugin.path@
|
||||
|
||||
Reference in New Issue
Block a user