Merge pull request #2463 from Owain94/somethingsomethingyadayadayada
project: Rework external plugins, local instances syncing, hotswapping
This commit is contained in:
2
.github/workflows/pr.yml
vendored
2
.github/workflows/pr.yml
vendored
@@ -69,7 +69,7 @@ jobs:
|
|||||||
- name: Assembling
|
- name: Assembling
|
||||||
run: ./gradlew assemble --console=plain
|
run: ./gradlew assemble --console=plain
|
||||||
- name: Testing
|
- name: Testing
|
||||||
run: ./gradlew test --stacktrace --console=plain
|
run: ./gradlew test -x deobfuscator:test --stacktrace --console=plain
|
||||||
|
|
||||||
checkstyleMain:
|
checkstyleMain:
|
||||||
name: Checkstyle main
|
name: Checkstyle main
|
||||||
|
|||||||
2
.github/workflows/push.yml
vendored
2
.github/workflows/push.yml
vendored
@@ -50,7 +50,7 @@ jobs:
|
|||||||
- name: Assembling
|
- name: Assembling
|
||||||
run: ./gradlew assemble --console=plain
|
run: ./gradlew assemble --console=plain
|
||||||
- name: Testing
|
- name: Testing
|
||||||
run: ./gradlew test --stacktrace --console=plain
|
run: ./gradlew test -x deobfuscator:test --stacktrace --console=plain
|
||||||
|
|
||||||
checkstyleMain:
|
checkstyleMain:
|
||||||
name: Checkstyle main
|
name: Checkstyle main
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ open class BootstrapTask @Inject constructor(@Input val type: String) : DefaultT
|
|||||||
|
|
||||||
private fun getArtifacts(): Array<JsonBuilder> {
|
private fun getArtifacts(): Array<JsonBuilder> {
|
||||||
val artifacts = ArrayList<JsonBuilder>()
|
val artifacts = ArrayList<JsonBuilder>()
|
||||||
|
val artifactsSet = HashSet<String>()
|
||||||
|
|
||||||
project.configurations["runtimeClasspath"].resolvedConfiguration.resolvedArtifacts.forEach {
|
project.configurations["runtimeClasspath"].resolvedConfiguration.resolvedArtifacts.forEach {
|
||||||
val module = it.moduleVersion.id.toString()
|
val module = it.moduleVersion.id.toString()
|
||||||
@@ -44,7 +45,11 @@ open class BootstrapTask @Inject constructor(@Input val type: String) : DefaultT
|
|||||||
val version = splat[2]
|
val version = splat[2]
|
||||||
lateinit var path: String
|
lateinit var path: String
|
||||||
|
|
||||||
if (it.file.name.contains(ProjectVersions.openosrsVersion)) {
|
if (it.file.name.contains("injected-client") ||
|
||||||
|
it.file.name.contains("runelite-client") ||
|
||||||
|
it.file.name.contains("http-api") ||
|
||||||
|
it.file.name.contains("runescape-api") ||
|
||||||
|
it.file.name.contains("runelite-api")) {
|
||||||
path = "https://github.com/open-osrs/hosting/raw/master/${type}/${it.file.name}"
|
path = "https://github.com/open-osrs/hosting/raw/master/${type}/${it.file.name}"
|
||||||
} else if (it.file.name.contains("injection-annotations") || it.file.name.contains("rxrelay")) {
|
} else if (it.file.name.contains("injection-annotations") || it.file.name.contains("rxrelay")) {
|
||||||
path = "https://github.com/open-osrs/hosting/raw/master/" + group.replace(".", "/") + "/${name}/$version/${it.file.name}"
|
path = "https://github.com/open-osrs/hosting/raw/master/" + group.replace(".", "/") + "/${name}/$version/${it.file.name}"
|
||||||
@@ -62,14 +67,18 @@ open class BootstrapTask @Inject constructor(@Input val type: String) : DefaultT
|
|||||||
path += "${name}/$version/${name}-$version.jar"
|
path += "${name}/$version/${name}-$version.jar"
|
||||||
}
|
}
|
||||||
|
|
||||||
val artifactFile = File(it.file.absolutePath)
|
val filePath = it.file.absolutePath
|
||||||
|
val artifactFile = File(filePath)
|
||||||
|
|
||||||
artifacts.add(JsonBuilder(
|
if (!artifactsSet.contains(filePath)) {
|
||||||
"name" to it.file.name,
|
artifactsSet.add(filePath)
|
||||||
"path" to path,
|
artifacts.add(JsonBuilder(
|
||||||
"size" to artifactFile.length(),
|
"name" to it.file.name,
|
||||||
"hash" to hash(artifactFile.readBytes())
|
"path" to path,
|
||||||
))
|
"size" to artifactFile.length(),
|
||||||
|
"hash" to hash(artifactFile.readBytes())
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
artifacts.add(JsonBuilder(
|
artifacts.add(JsonBuilder(
|
||||||
|
|||||||
@@ -25,9 +25,9 @@
|
|||||||
|
|
||||||
object ProjectVersions {
|
object ProjectVersions {
|
||||||
const val launcherVersion = "2.2.0"
|
const val launcherVersion = "2.2.0"
|
||||||
const val rlVersion = "1.6.9"
|
const val rlVersion = "1.6.10"
|
||||||
|
|
||||||
const val openosrsVersion = "3.2.0"
|
const val openosrsVersion = "3.2.1"
|
||||||
|
|
||||||
const val rsversion = 189
|
const val rsversion = 189
|
||||||
const val cacheversion = 165
|
const val cacheversion = 165
|
||||||
@@ -62,6 +62,7 @@ object Libraries {
|
|||||||
const val guice = "4.2.2"
|
const val guice = "4.2.2"
|
||||||
const val h2 = "1.4.200"
|
const val h2 = "1.4.200"
|
||||||
const val hamcrest = "2.2"
|
const val hamcrest = "2.2"
|
||||||
|
const val javagroups = "4.0.0.Final"
|
||||||
const val javax = "1.3.2"
|
const val javax = "1.3.2"
|
||||||
const val javaxInject = "1"
|
const val javaxInject = "1"
|
||||||
const val jna = "5.5.0"
|
const val jna = "5.5.0"
|
||||||
@@ -108,6 +109,7 @@ object Libraries {
|
|||||||
const val guiceTestlib = "com.google.inject.extensions:guice-testlib:${Versions.guice}"
|
const val guiceTestlib = "com.google.inject.extensions:guice-testlib:${Versions.guice}"
|
||||||
const val h2 = "com.h2database:h2:${Versions.h2}"
|
const val h2 = "com.h2database:h2:${Versions.h2}"
|
||||||
const val hamcrest = "org.hamcrest:hamcrest-library:${Versions.hamcrest}"
|
const val hamcrest = "org.hamcrest:hamcrest-library:${Versions.hamcrest}"
|
||||||
|
const val javagroups = "org.jgroups:jgroups:${Versions.javagroups}"
|
||||||
const val javax = "javax.annotation:javax.annotation-api:${Versions.javax}"
|
const val javax = "javax.annotation:javax.annotation-api:${Versions.javax}"
|
||||||
const val javaxInject = "javax.inject:javax.inject:${Versions.javaxInject}"
|
const val javaxInject = "javax.inject:javax.inject:${Versions.javaxInject}"
|
||||||
const val jna = "net.java.dev.jna:jna:${Versions.jna}"
|
const val jna = "net.java.dev.jna:jna:${Versions.jna}"
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ dependencies {
|
|||||||
implementation(Libraries.okhttp3)
|
implementation(Libraries.okhttp3)
|
||||||
implementation(Libraries.rxjava)
|
implementation(Libraries.rxjava)
|
||||||
implementation(Libraries.jna)
|
implementation(Libraries.jna)
|
||||||
|
implementation(Libraries.javagroups)
|
||||||
implementation(Libraries.jnaPlatform)
|
implementation(Libraries.jnaPlatform)
|
||||||
implementation(Libraries.discord)
|
implementation(Libraries.discord)
|
||||||
implementation(Libraries.substance)
|
implementation(Libraries.substance)
|
||||||
@@ -134,11 +135,15 @@ tasks {
|
|||||||
inputs.properties(tokens)
|
inputs.properties(tokens)
|
||||||
|
|
||||||
from("src/main/resources") {
|
from("src/main/resources") {
|
||||||
include("open.osrs.properties")
|
|
||||||
include("sentry.properties")
|
include("sentry.properties")
|
||||||
}
|
}
|
||||||
into("${buildDir}/resources/main")
|
into("${buildDir}/resources/main")
|
||||||
|
|
||||||
|
from("src/main/resources/net/runelite/client") {
|
||||||
|
include("open.osrs.properties")
|
||||||
|
}
|
||||||
|
into("${buildDir}/resources/main/net/runelite/client")
|
||||||
|
|
||||||
filter(ReplaceTokens::class, "tokens" to tokens)
|
filter(ReplaceTokens::class, "tokens" to tokens)
|
||||||
filteringCharset = "UTF-8"
|
filteringCharset = "UTF-8"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import java.net.PasswordAuthentication;
|
|||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
@@ -88,7 +89,7 @@ import net.runelite.client.ui.overlay.arrow.ArrowWorldOverlay;
|
|||||||
import net.runelite.client.ui.overlay.infobox.InfoBoxOverlay;
|
import net.runelite.client.ui.overlay.infobox.InfoBoxOverlay;
|
||||||
import net.runelite.client.ui.overlay.tooltip.TooltipOverlay;
|
import net.runelite.client.ui.overlay.tooltip.TooltipOverlay;
|
||||||
import net.runelite.client.ui.overlay.worldmap.WorldMapOverlay;
|
import net.runelite.client.ui.overlay.worldmap.WorldMapOverlay;
|
||||||
import net.runelite.client.util.AppLock;
|
import net.runelite.client.util.Groups;
|
||||||
import net.runelite.client.util.WorldUtil;
|
import net.runelite.client.util.WorldUtil;
|
||||||
import net.runelite.client.ws.PartyService;
|
import net.runelite.client.ws.PartyService;
|
||||||
import net.runelite.http.api.worlds.World;
|
import net.runelite.http.api.worlds.World;
|
||||||
@@ -112,6 +113,7 @@ public class RuneLite
|
|||||||
public static final File DEFAULT_CONFIG_FILE = new File(RUNELITE_DIR, "runeliteplus.properties");
|
public static final File DEFAULT_CONFIG_FILE = new File(RUNELITE_DIR, "runeliteplus.properties");
|
||||||
public static final Locale SYSTEM_LOCALE = Locale.getDefault();
|
public static final Locale SYSTEM_LOCALE = Locale.getDefault();
|
||||||
public static boolean allowPrivateServer = false;
|
public static boolean allowPrivateServer = false;
|
||||||
|
public static String uuid = UUID.randomUUID().toString();
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private static Injector injector;
|
private static Injector injector;
|
||||||
@@ -191,6 +193,9 @@ public class RuneLite
|
|||||||
@Inject
|
@Inject
|
||||||
private Provider<PartyService> partyService;
|
private Provider<PartyService> partyService;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private Groups groups;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private Hooks hooks;
|
private Hooks hooks;
|
||||||
|
|
||||||
@@ -210,9 +215,6 @@ public class RuneLite
|
|||||||
@Inject
|
@Inject
|
||||||
private Scheduler scheduler;
|
private Scheduler scheduler;
|
||||||
|
|
||||||
@Inject
|
|
||||||
private AppLock appLock;
|
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception
|
public static void main(String[] args) throws Exception
|
||||||
{
|
{
|
||||||
Locale.setDefault(Locale.ENGLISH);
|
Locale.setDefault(Locale.ENGLISH);
|
||||||
@@ -383,15 +385,12 @@ public class RuneLite
|
|||||||
// Tell the plugin manager if client is outdated or not
|
// Tell the plugin manager if client is outdated or not
|
||||||
pluginManager.setOutdated(isOutdated);
|
pluginManager.setOutdated(isOutdated);
|
||||||
|
|
||||||
// Load external plugins
|
// Load external plugin manager
|
||||||
externalPluginManager.startExternalUpdateManager();
|
externalPluginManager.startExternalUpdateManager();
|
||||||
externalPluginManager.startExternalPluginManager();
|
externalPluginManager.startExternalPluginManager();
|
||||||
|
|
||||||
if (appLock.lock(this.getClass().getName()))
|
// Update external plugins
|
||||||
{
|
externalPluginManager.update();
|
||||||
RuneLiteSplashScreen.stage(.59, "Updating external plugins");
|
|
||||||
externalPluginManager.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load the plugins, but does not start them yet.
|
// Load the plugins, but does not start them yet.
|
||||||
// This will initialize configuration
|
// This will initialize configuration
|
||||||
@@ -510,7 +509,7 @@ public class RuneLite
|
|||||||
{
|
{
|
||||||
clientSessionManager.shutdown();
|
clientSessionManager.shutdown();
|
||||||
discordService.close();
|
discordService.close();
|
||||||
appLock.release();
|
groups.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ConfigFileConverter implements ValueConverter<File>
|
private static class ConfigFileConverter implements ValueConverter<File>
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ public class RuneLiteProperties
|
|||||||
|
|
||||||
static
|
static
|
||||||
{
|
{
|
||||||
try (InputStream in = RuneLiteProperties.class.getResourceAsStream("/open.osrs.properties"))
|
try (InputStream in = RuneLiteProperties.class.getResourceAsStream("open.osrs.properties"))
|
||||||
{
|
{
|
||||||
properties.load(in);
|
properties.load(in);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,7 +70,10 @@ import net.runelite.client.events.ConfigChanged;
|
|||||||
import net.runelite.client.plugins.ExternalPluginManager;
|
import net.runelite.client.plugins.ExternalPluginManager;
|
||||||
import net.runelite.client.plugins.Plugin;
|
import net.runelite.client.plugins.Plugin;
|
||||||
import net.runelite.client.util.ColorUtil;
|
import net.runelite.client.util.ColorUtil;
|
||||||
|
import net.runelite.client.util.Groups;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.jgroups.Message;
|
||||||
|
import org.jgroups.util.Util;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@@ -82,15 +85,24 @@ public class ConfigManager
|
|||||||
private final Properties properties = new Properties();
|
private final Properties properties = new Properties();
|
||||||
private final Map<String, String> pendingChanges = new HashMap<>();
|
private final Map<String, String> pendingChanges = new HashMap<>();
|
||||||
private final File settingsFileInput;
|
private final File settingsFileInput;
|
||||||
|
private final Groups groups;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
EventBus eventBus;
|
EventBus eventBus;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ConfigManager(@Named("config") File config, ScheduledExecutorService scheduledExecutorService)
|
public ConfigManager(
|
||||||
|
@Named("config") File config,
|
||||||
|
ScheduledExecutorService scheduledExecutorService,
|
||||||
|
Groups groups)
|
||||||
{
|
{
|
||||||
this.settingsFileInput = config;
|
this.settingsFileInput = config;
|
||||||
|
this.groups = groups;
|
||||||
|
|
||||||
scheduledExecutorService.scheduleWithFixedDelay(this::sendConfig, 30, 30, TimeUnit.SECONDS);
|
scheduledExecutorService.scheduleWithFixedDelay(this::sendConfig, 30, 30, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
groups.getMessageObjectSubject()
|
||||||
|
.subscribe(this::receive);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@@ -450,6 +462,7 @@ public class ConfigManager
|
|||||||
configChanged.setKey(key);
|
configChanged.setKey(key);
|
||||||
configChanged.setOldValue(null);
|
configChanged.setOldValue(null);
|
||||||
configChanged.setNewValue(value);
|
configChanged.setNewValue(value);
|
||||||
|
|
||||||
eventBus.post(ConfigChanged.class, configChanged);
|
eventBus.post(ConfigChanged.class, configChanged);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -525,7 +538,17 @@ public class ConfigManager
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setConfiguration(String groupName, String key, Object value)
|
||||||
|
{
|
||||||
|
setConfiguration(groupName, key, objectToString(value));
|
||||||
|
}
|
||||||
|
|
||||||
public void setConfiguration(String groupName, String key, String value)
|
public void setConfiguration(String groupName, String key, String value)
|
||||||
|
{
|
||||||
|
setConfiguration(groupName, key, value, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConfiguration(String groupName, String key, String value, String origin)
|
||||||
{
|
{
|
||||||
String oldValue = (String) properties.setProperty(groupName + "." + key, value);
|
String oldValue = (String) properties.setProperty(groupName + "." + key, value);
|
||||||
|
|
||||||
@@ -547,16 +570,23 @@ public class ConfigManager
|
|||||||
configChanged.setKey(key);
|
configChanged.setKey(key);
|
||||||
configChanged.setOldValue(oldValue);
|
configChanged.setOldValue(oldValue);
|
||||||
configChanged.setNewValue(value);
|
configChanged.setNewValue(value);
|
||||||
|
configChanged.setOrigin(origin == null ? RuneLite.uuid : origin);
|
||||||
|
configChanged.setPath(settingsFileInput.getAbsolutePath());
|
||||||
|
|
||||||
eventBus.post(ConfigChanged.class, configChanged);
|
eventBus.post(ConfigChanged.class, configChanged);
|
||||||
}
|
|
||||||
|
|
||||||
public void setConfiguration(String groupName, String key, Object value)
|
if (origin == null)
|
||||||
{
|
{
|
||||||
setConfiguration(groupName, key, objectToString(value));
|
broadcast(configChanged);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unsetConfiguration(String groupName, String key)
|
public void unsetConfiguration(String groupName, String key)
|
||||||
|
{
|
||||||
|
unsetConfiguration(groupName, key, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unsetConfiguration(String groupName, String key, String origin)
|
||||||
{
|
{
|
||||||
String oldValue = (String) properties.remove(groupName + "." + key);
|
String oldValue = (String) properties.remove(groupName + "." + key);
|
||||||
|
|
||||||
@@ -577,8 +607,15 @@ public class ConfigManager
|
|||||||
configChanged.setGroup(groupName);
|
configChanged.setGroup(groupName);
|
||||||
configChanged.setKey(key);
|
configChanged.setKey(key);
|
||||||
configChanged.setOldValue(oldValue);
|
configChanged.setOldValue(oldValue);
|
||||||
|
configChanged.setOrigin(origin == null ? RuneLite.uuid : origin);
|
||||||
|
configChanged.setPath(settingsFileInput.getAbsolutePath());
|
||||||
|
|
||||||
eventBus.post(ConfigChanged.class, configChanged);
|
eventBus.post(ConfigChanged.class, configChanged);
|
||||||
|
|
||||||
|
if (origin == null)
|
||||||
|
{
|
||||||
|
broadcast(configChanged);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConfigDescriptor getConfigDescriptor(Object configurationProxy)
|
public ConfigDescriptor getConfigDescriptor(Object configurationProxy)
|
||||||
@@ -842,4 +879,40 @@ public class ConfigManager
|
|||||||
|
|
||||||
syncPropertiesFromFile(newestFile);
|
syncPropertiesFromFile(newestFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void broadcast(ConfigChanged configChanged)
|
||||||
|
{
|
||||||
|
groups.sendConfig(null, configChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void receive(Message message)
|
||||||
|
{
|
||||||
|
if (message.getObject() instanceof String)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ConfigChanged configChanged = Util.objectFromByteBuffer(message.getBuffer());
|
||||||
|
|
||||||
|
if (!configChanged.getPath().equals(settingsFileInput.getAbsolutePath()))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configChanged.getNewValue() == null)
|
||||||
|
{
|
||||||
|
unsetConfiguration(configChanged.getGroup(), configChanged.getKey(), configChanged.getOrigin());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setConfiguration(configChanged.getGroup(), configChanged.getKey(), configChanged.getNewValue(), configChanged.getOrigin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -342,11 +342,23 @@ public interface OpenOSRSConfig extends Config
|
|||||||
return new Title();
|
return new Title();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "localSync",
|
||||||
|
name = "Sync local instances",
|
||||||
|
description = "Enables multiple local instances of OpenOSRS to communicate (this enables syncing plugin state and config options)",
|
||||||
|
position = 21,
|
||||||
|
titleSection = "miscTitle"
|
||||||
|
)
|
||||||
|
default boolean localSync()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@ConfigItem(
|
@ConfigItem(
|
||||||
keyName = "keyboardPin",
|
keyName = "keyboardPin",
|
||||||
name = "Keyboard bank pin",
|
name = "Keyboard bank pin",
|
||||||
description = "Enables you to type your bank pin",
|
description = "Enables you to type your bank pin",
|
||||||
position = 21,
|
position = 22,
|
||||||
titleSection = "miscTitle"
|
titleSection = "miscTitle"
|
||||||
)
|
)
|
||||||
default boolean keyboardPin()
|
default boolean keyboardPin()
|
||||||
@@ -358,7 +370,7 @@ public interface OpenOSRSConfig extends Config
|
|||||||
keyName = "detachHotkey",
|
keyName = "detachHotkey",
|
||||||
name = "Detach Cam",
|
name = "Detach Cam",
|
||||||
description = "Detach Camera hotkey, press this and it will activate detatched camera.",
|
description = "Detach Camera hotkey, press this and it will activate detatched camera.",
|
||||||
position = 22,
|
position = 23,
|
||||||
titleSection = "miscTitle"
|
titleSection = "miscTitle"
|
||||||
)
|
)
|
||||||
default Keybind detachHotkey()
|
default Keybind detachHotkey()
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package net.runelite.client.events;
|
package net.runelite.client.events;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import net.runelite.api.events.Event;
|
import net.runelite.api.events.Event;
|
||||||
|
|
||||||
@@ -31,7 +32,7 @@ import net.runelite.api.events.Event;
|
|||||||
* An event where a configuration entry has been modified.
|
* An event where a configuration entry has been modified.
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public class ConfigChanged implements Event
|
public class ConfigChanged implements Event, Serializable
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The parent group for the key.
|
* The parent group for the key.
|
||||||
@@ -52,4 +53,12 @@ public class ConfigChanged implements Event
|
|||||||
* The new value of the entry, null if the entry has been unset.
|
* The new value of the entry, null if the entry has been unset.
|
||||||
*/
|
*/
|
||||||
private String newValue;
|
private String newValue;
|
||||||
|
/**
|
||||||
|
* The client where the config value was changed from
|
||||||
|
*/
|
||||||
|
private String origin;
|
||||||
|
/**
|
||||||
|
* Path of the current config file
|
||||||
|
*/
|
||||||
|
private String path;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, 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.plugins;
|
package net.runelite.client.plugins;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.graph.GraphBuilder;
|
|
||||||
import com.google.common.graph.MutableGraph;
|
|
||||||
import com.google.inject.Binder;
|
import com.google.inject.Binder;
|
||||||
import com.google.inject.CreationException;
|
import com.google.inject.CreationException;
|
||||||
import com.google.inject.Injector;
|
import com.google.inject.Injector;
|
||||||
import com.google.inject.Key;
|
import com.google.inject.Key;
|
||||||
import com.google.inject.Module;
|
import com.google.inject.Module;
|
||||||
|
import java.io.Closeable;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
@@ -30,13 +53,13 @@ import java.util.concurrent.ExecutorService;
|
|||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import javax.swing.JOptionPane;
|
import javax.swing.JOptionPane;
|
||||||
import javax.swing.SwingUtilities;
|
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -48,12 +71,15 @@ import net.runelite.client.config.Config;
|
|||||||
import net.runelite.client.config.ConfigManager;
|
import net.runelite.client.config.ConfigManager;
|
||||||
import net.runelite.client.config.OpenOSRSConfig;
|
import net.runelite.client.config.OpenOSRSConfig;
|
||||||
import net.runelite.client.eventbus.EventBus;
|
import net.runelite.client.eventbus.EventBus;
|
||||||
|
import net.runelite.client.events.ConfigChanged;
|
||||||
import net.runelite.client.events.ExternalPluginChanged;
|
import net.runelite.client.events.ExternalPluginChanged;
|
||||||
import net.runelite.client.events.ExternalRepositoryChanged;
|
import net.runelite.client.events.ExternalRepositoryChanged;
|
||||||
import net.runelite.client.ui.ClientUI;
|
import net.runelite.client.ui.ClientUI;
|
||||||
import net.runelite.client.ui.RuneLiteSplashScreen;
|
import net.runelite.client.ui.RuneLiteSplashScreen;
|
||||||
|
import net.runelite.client.util.Groups;
|
||||||
import net.runelite.client.util.MiscUtils;
|
import net.runelite.client.util.MiscUtils;
|
||||||
import net.runelite.client.util.SwingUtil;
|
import net.runelite.client.util.SwingUtil;
|
||||||
|
import org.jgroups.Message;
|
||||||
import org.pf4j.DefaultPluginManager;
|
import org.pf4j.DefaultPluginManager;
|
||||||
import org.pf4j.DependencyResolver;
|
import org.pf4j.DependencyResolver;
|
||||||
import org.pf4j.JarPluginLoader;
|
import org.pf4j.JarPluginLoader;
|
||||||
@@ -65,6 +91,8 @@ import org.pf4j.PluginDescriptorFinder;
|
|||||||
import org.pf4j.PluginLoader;
|
import org.pf4j.PluginLoader;
|
||||||
import org.pf4j.PluginRepository;
|
import org.pf4j.PluginRepository;
|
||||||
import org.pf4j.PluginRuntimeException;
|
import org.pf4j.PluginRuntimeException;
|
||||||
|
import org.pf4j.PluginState;
|
||||||
|
import org.pf4j.PluginStateEvent;
|
||||||
import org.pf4j.PluginWrapper;
|
import org.pf4j.PluginWrapper;
|
||||||
import org.pf4j.RuntimeMode;
|
import org.pf4j.RuntimeMode;
|
||||||
import org.pf4j.update.DefaultUpdateRepository;
|
import org.pf4j.update.DefaultUpdateRepository;
|
||||||
@@ -75,8 +103,7 @@ import org.pf4j.update.VerifyException;
|
|||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Singleton
|
@Singleton
|
||||||
public
|
public class ExternalPluginManager
|
||||||
class ExternalPluginManager
|
|
||||||
{
|
{
|
||||||
public static ArrayList<ClassLoader> pluginClassLoaders = new ArrayList<>();
|
public static ArrayList<ClassLoader> pluginClassLoaders = new ArrayList<>();
|
||||||
private final PluginManager runelitePluginManager;
|
private final PluginManager runelitePluginManager;
|
||||||
@@ -86,35 +113,41 @@ class ExternalPluginManager
|
|||||||
private final OpenOSRSConfig openOSRSConfig;
|
private final OpenOSRSConfig openOSRSConfig;
|
||||||
private final EventBus eventBus;
|
private final EventBus eventBus;
|
||||||
private final ConfigManager configManager;
|
private final ConfigManager configManager;
|
||||||
private final List<Plugin> plugins = new CopyOnWriteArrayList<>();
|
|
||||||
private final Map<String, String> pluginsMap = new HashMap<>();
|
private final Map<String, String> pluginsMap = new HashMap<>();
|
||||||
@Getter(AccessLevel.PUBLIC)
|
@Getter(AccessLevel.PUBLIC)
|
||||||
|
private final Map<String, Map<String, String>> pluginsInfoMap = new HashMap<>();
|
||||||
|
private final Groups groups;
|
||||||
|
@Getter(AccessLevel.PUBLIC)
|
||||||
private UpdateManager updateManager;
|
private UpdateManager updateManager;
|
||||||
|
private Map<String, PluginInfo.PluginRelease> lastPluginRelease = new HashMap<>();
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ExternalPluginManager(
|
public ExternalPluginManager(
|
||||||
PluginManager pluginManager,
|
PluginManager pluginManager,
|
||||||
OpenOSRSConfig openOSRSConfig,
|
OpenOSRSConfig openOSRSConfig,
|
||||||
EventBus eventBus,
|
EventBus eventBus,
|
||||||
ConfigManager configManager)
|
ConfigManager configManager,
|
||||||
|
Groups groups)
|
||||||
{
|
{
|
||||||
this.runelitePluginManager = pluginManager;
|
this.runelitePluginManager = pluginManager;
|
||||||
this.openOSRSConfig = openOSRSConfig;
|
this.openOSRSConfig = openOSRSConfig;
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
this.configManager = configManager;
|
this.configManager = configManager;
|
||||||
|
this.groups = groups;
|
||||||
|
|
||||||
//noinspection ResultOfMethodCallIgnored
|
//noinspection ResultOfMethodCallIgnored
|
||||||
EXTERNALPLUGIN_DIR.mkdirs();
|
EXTERNALPLUGIN_DIR.mkdirs();
|
||||||
|
|
||||||
initPluginManager();
|
initPluginManager();
|
||||||
|
|
||||||
|
groups.getMessageStringSubject()
|
||||||
|
.subscribe(this::receive);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initPluginManager()
|
private void initPluginManager()
|
||||||
{
|
{
|
||||||
boolean debug = RuneLiteProperties.getLauncherVersion() == null && RuneLiteProperties.getPluginPath() != null;
|
externalPluginManager = new DefaultPluginManager(
|
||||||
|
RuneLiteProperties.getPluginPath() != null ? Paths.get(RuneLiteProperties.getPluginPath())
|
||||||
this.externalPluginManager = new DefaultPluginManager(
|
|
||||||
debug ? Paths.get(RuneLiteProperties.getPluginPath() + File.separator + "release")
|
|
||||||
: EXTERNALPLUGIN_DIR.toPath())
|
: EXTERNALPLUGIN_DIR.toPath())
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
@@ -158,7 +191,70 @@ class ExternalPluginManager
|
|||||||
@Override
|
@Override
|
||||||
public RuntimeMode getRuntimeMode()
|
public RuntimeMode getRuntimeMode()
|
||||||
{
|
{
|
||||||
return debug ? RuntimeMode.DEVELOPMENT : RuntimeMode.DEPLOYMENT;
|
return RuneLiteProperties.getLauncherVersion() == null ? RuntimeMode.DEVELOPMENT : RuntimeMode.DEPLOYMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void resolvePlugins()
|
||||||
|
{
|
||||||
|
// retrieves the plugins descriptors
|
||||||
|
List<org.pf4j.PluginDescriptor> descriptors = new ArrayList<>();
|
||||||
|
for (PluginWrapper plugin : plugins.values())
|
||||||
|
{
|
||||||
|
descriptors.add(plugin.getDescriptor());
|
||||||
|
}
|
||||||
|
|
||||||
|
// retrieves the plugins descriptors from the resolvedPlugins list. This allows to load plugins that have already loaded dependencies.
|
||||||
|
for (PluginWrapper plugin : resolvedPlugins)
|
||||||
|
{
|
||||||
|
descriptors.add(plugin.getDescriptor());
|
||||||
|
}
|
||||||
|
|
||||||
|
DependencyResolver.Result result = dependencyResolver.resolve(descriptors);
|
||||||
|
|
||||||
|
if (result.hasCyclicDependency())
|
||||||
|
{
|
||||||
|
throw new DependencyResolver.CyclicDependencyException();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> notFoundDependencies = result.getNotFoundDependencies();
|
||||||
|
if (!notFoundDependencies.isEmpty())
|
||||||
|
{
|
||||||
|
throw new DependencyResolver.DependenciesNotFoundException(notFoundDependencies);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<DependencyResolver.WrongDependencyVersion> wrongVersionDependencies = result.getWrongVersionDependencies();
|
||||||
|
if (!wrongVersionDependencies.isEmpty())
|
||||||
|
{
|
||||||
|
throw new DependencyResolver.DependenciesWrongVersionException(wrongVersionDependencies);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> sortedPlugins = result.getSortedPlugins();
|
||||||
|
|
||||||
|
// move plugins from "unresolved" to "resolved"
|
||||||
|
for (String pluginId : sortedPlugins)
|
||||||
|
{
|
||||||
|
PluginWrapper pluginWrapper = plugins.get(pluginId);
|
||||||
|
|
||||||
|
//The plugin is already resolved. Don't put a copy in the resolvedPlugins.
|
||||||
|
if (resolvedPlugins.contains(pluginWrapper))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unresolvedPlugins.remove(pluginWrapper))
|
||||||
|
{
|
||||||
|
PluginState pluginState = pluginWrapper.getPluginState();
|
||||||
|
if (pluginState != PluginState.DISABLED)
|
||||||
|
{
|
||||||
|
pluginWrapper.setPluginState(PluginState.RESOLVED);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolvedPlugins.add(pluginWrapper);
|
||||||
|
|
||||||
|
firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -209,8 +305,129 @@ class ExternalPluginManager
|
|||||||
log.error(e.getMessage(), e);
|
log.error(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PluginState stopPlugin(String pluginId)
|
||||||
|
{
|
||||||
|
if (!plugins.containsKey(pluginId))
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId));
|
||||||
|
}
|
||||||
|
|
||||||
|
PluginWrapper pluginWrapper = getPlugin(pluginId);
|
||||||
|
org.pf4j.PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor();
|
||||||
|
PluginState pluginState = pluginWrapper.getPluginState();
|
||||||
|
if (PluginState.STOPPED == pluginState)
|
||||||
|
{
|
||||||
|
log.debug("Already stopped plugin '{}'", getPluginLabel(pluginDescriptor));
|
||||||
|
return PluginState.STOPPED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// test for disabled plugin
|
||||||
|
if (PluginState.DISABLED == pluginState)
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
return pluginState;
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginWrapper.getPlugin().stop();
|
||||||
|
pluginWrapper.setPluginState(PluginState.STOPPED);
|
||||||
|
startedPlugins.remove(pluginWrapper);
|
||||||
|
|
||||||
|
firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState));
|
||||||
|
|
||||||
|
return pluginWrapper.getPluginState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean unloadPlugin(String pluginId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PluginState pluginState = stopPlugin(pluginId);
|
||||||
|
if (PluginState.STARTED == pluginState)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PluginWrapper pluginWrapper = getPlugin(pluginId);
|
||||||
|
|
||||||
|
// remove the plugin
|
||||||
|
plugins.remove(pluginId);
|
||||||
|
getResolvedPlugins().remove(pluginWrapper);
|
||||||
|
|
||||||
|
firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState));
|
||||||
|
|
||||||
|
// remove the classloader
|
||||||
|
Map<String, ClassLoader> pluginClassLoaders = getPluginClassLoaders();
|
||||||
|
if (pluginClassLoaders.containsKey(pluginId))
|
||||||
|
{
|
||||||
|
ClassLoader classLoader = pluginClassLoaders.remove(pluginId);
|
||||||
|
if (classLoader instanceof Closeable)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
((Closeable) classLoader).close();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
throw new PluginRuntimeException(e, "Cannot close classloader");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e)
|
||||||
|
{
|
||||||
|
// ignore not found exceptions because this method is recursive
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean deletePlugin(String pluginId)
|
||||||
|
{
|
||||||
|
if (!plugins.containsKey(pluginId))
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId));
|
||||||
|
}
|
||||||
|
|
||||||
|
PluginWrapper pluginWrapper = getPlugin(pluginId);
|
||||||
|
// stop the plugin if it's started
|
||||||
|
PluginState pluginState = stopPlugin(pluginId);
|
||||||
|
if (PluginState.STARTED == pluginState)
|
||||||
|
{
|
||||||
|
log.error("Failed to stop plugin '{}' on delete", pluginId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get an instance of plugin before the plugin is unloaded
|
||||||
|
// for reason see https://github.com/pf4j/pf4j/issues/309
|
||||||
|
|
||||||
|
org.pf4j.Plugin plugin = pluginWrapper.getPlugin();
|
||||||
|
|
||||||
|
if (!unloadPlugin(pluginId))
|
||||||
|
{
|
||||||
|
log.error("Failed to unload plugin '{}' on delete", pluginId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// notify the plugin as it's deleted
|
||||||
|
plugin.delete();
|
||||||
|
|
||||||
|
Path pluginPath = pluginWrapper.getPluginPath();
|
||||||
|
|
||||||
|
return pluginRepository.deletePluginPath(pluginPath);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
this.externalPluginManager.setSystemVersion(SYSTEM_VERSION);
|
externalPluginManager.setSystemVersion(SYSTEM_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean developmentMode()
|
||||||
|
{
|
||||||
|
return externalPluginManager.isDevelopment();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean doesGhRepoExist(String owner, String name)
|
public boolean doesGhRepoExist(String owner, String name)
|
||||||
@@ -263,7 +480,7 @@ class ExternalPluginManager
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
this.externalPluginManager.loadPlugins();
|
externalPluginManager.loadPlugins();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -275,7 +492,7 @@ class ExternalPluginManager
|
|||||||
|
|
||||||
for (String dep : deps)
|
for (String dep : deps)
|
||||||
{
|
{
|
||||||
install(dep);
|
updateManager.installPlugin(dep, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
startExternalPluginManager();
|
startExternalPluginManager();
|
||||||
@@ -292,7 +509,7 @@ class ExternalPluginManager
|
|||||||
loadOldFormat();
|
loadOldFormat();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateManager = new UpdateManager(this.externalPluginManager, repositories);
|
updateManager = new UpdateManager(externalPluginManager, repositories);
|
||||||
saveConfig();
|
saveConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,7 +578,7 @@ class ExternalPluginManager
|
|||||||
openOSRSConfig.setExternalRepositories("OpenOSRS:https://raw.githubusercontent.com/open-osrs/plugin-hosting/master/");
|
openOSRSConfig.setExternalRepositories("OpenOSRS:https://raw.githubusercontent.com/open-osrs/plugin-hosting/master/");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateManager = new UpdateManager(this.externalPluginManager, repositories);
|
updateManager = new UpdateManager(externalPluginManager, repositories);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addGHRepository(String owner, String name)
|
public void addGHRepository(String owner, String name)
|
||||||
@@ -407,12 +624,9 @@ class ExternalPluginManager
|
|||||||
openOSRSConfig.setExternalRepositories(config.toString());
|
openOSRSConfig.setExternalRepositories(config.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Plugin> scanAndInstantiate(List<Plugin> plugins, boolean init, boolean initConfig) throws IOException
|
private void scanAndInstantiate(List<Plugin> plugins, boolean init, boolean initConfig)
|
||||||
{
|
{
|
||||||
RuneLiteSplashScreen.stage(.66, "Loading external plugins");
|
RuneLiteSplashScreen.stage(.66, "Loading external plugins");
|
||||||
MutableGraph<Class<? extends Plugin>> graph = GraphBuilder
|
|
||||||
.directed()
|
|
||||||
.build();
|
|
||||||
|
|
||||||
AtomicInteger loaded = new AtomicInteger();
|
AtomicInteger loaded = new AtomicInteger();
|
||||||
List<Plugin> scannedPlugins = new CopyOnWriteArrayList<>();
|
List<Plugin> scannedPlugins = new CopyOnWriteArrayList<>();
|
||||||
@@ -451,6 +665,7 @@ class ExternalPluginManager
|
|||||||
Plugin plugininst;
|
Plugin plugininst;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
//noinspection unchecked
|
||||||
plugininst = instantiate(scannedPlugins, (Class<Plugin>) plugin.getClass(), init, initConfig);
|
plugininst = instantiate(scannedPlugins, (Class<Plugin>) plugin.getClass(), init, initConfig);
|
||||||
scannedPlugins.add(plugininst);
|
scannedPlugins.add(plugininst);
|
||||||
}
|
}
|
||||||
@@ -477,7 +692,6 @@ class ExternalPluginManager
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return scannedPlugins;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@@ -492,7 +706,7 @@ class ExternalPluginManager
|
|||||||
Optional<Plugin> dependency =
|
Optional<Plugin> dependency =
|
||||||
Stream.concat(runelitePluginManager.getPlugins().stream(), scannedPlugins.stream())
|
Stream.concat(runelitePluginManager.getPlugins().stream(), scannedPlugins.stream())
|
||||||
.filter(p -> p.getClass() == pluginDependency.value()).findFirst();
|
.filter(p -> p.getClass() == pluginDependency.value()).findFirst();
|
||||||
if (!dependency.isPresent())
|
if (dependency.isEmpty())
|
||||||
{
|
{
|
||||||
throw new PluginInstantiationException(
|
throw new PluginInstantiationException(
|
||||||
"Unmet dependency for " + clazz.getSimpleName() + ": " + pluginDependency.value().getSimpleName());
|
"Unmet dependency for " + clazz.getSimpleName() + ": " + pluginDependency.value().getSimpleName());
|
||||||
@@ -551,12 +765,12 @@ class ExternalPluginManager
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
SwingUtilities.invokeAndWait(() ->
|
SwingUtil.syncExec(() ->
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
runelitePluginManager.startPlugin(plugin);
|
|
||||||
runelitePluginManager.add(plugin);
|
runelitePluginManager.add(plugin);
|
||||||
|
runelitePluginManager.startPlugin(plugin);
|
||||||
eventBus.post(ExternalPluginChanged.class,
|
eventBus.post(ExternalPluginChanged.class,
|
||||||
new ExternalPluginChanged(pluginsMap.get(plugin.getClass().getSimpleName()), plugin,
|
new ExternalPluginChanged(pluginsMap.get(plugin.getClass().getSimpleName()), plugin,
|
||||||
true));
|
true));
|
||||||
@@ -586,43 +800,53 @@ class ExternalPluginManager
|
|||||||
return plugin;
|
return plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkDepsAndStart(List<PluginWrapper> startedPlugins, List<Plugin> scannedPlugins, PluginWrapper pluginWrapper)
|
||||||
|
{
|
||||||
|
boolean depsLoaded = true;
|
||||||
|
for (PluginDependency dependency : pluginWrapper.getDescriptor().getDependencies())
|
||||||
|
{
|
||||||
|
if (startedPlugins.stream().noneMatch(pl -> pl.getPluginId().equals(dependency.getPluginId())))
|
||||||
|
{
|
||||||
|
depsLoaded = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!depsLoaded)
|
||||||
|
{
|
||||||
|
// This should never happen but can crash the client
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
scannedPlugins.addAll(loadPlugin(pluginWrapper.getPluginId()));
|
||||||
|
}
|
||||||
|
|
||||||
public void loadPlugins()
|
public void loadPlugins()
|
||||||
{
|
{
|
||||||
this.externalPluginManager.startPlugins();
|
externalPluginManager.startPlugins();
|
||||||
List<PluginWrapper> startedPlugins = getStartedPlugins();
|
List<PluginWrapper> startedPlugins = getStartedPlugins();
|
||||||
List<Plugin> scannedPlugins = new ArrayList<>();
|
List<Plugin> scannedPlugins = new ArrayList<>();
|
||||||
|
|
||||||
for (PluginWrapper plugin : startedPlugins)
|
for (PluginWrapper plugin : startedPlugins)
|
||||||
{
|
{
|
||||||
boolean depsLoaded = true;
|
checkDepsAndStart(startedPlugins, scannedPlugins, plugin);
|
||||||
for (PluginDependency dependency : plugin.getDescriptor().getDependencies())
|
|
||||||
{
|
|
||||||
if (startedPlugins.stream().noneMatch(pl -> pl.getPluginId().equals(dependency.getPluginId())))
|
|
||||||
{
|
|
||||||
depsLoaded = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!depsLoaded)
|
|
||||||
{
|
|
||||||
// This should never happen but can crash the client
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
scannedPlugins.addAll(loadPlugin(plugin.getPluginId()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
startPlugins(scannedPlugins, false, false);
|
scanAndInstantiate(scannedPlugins, false, false);
|
||||||
}
|
|
||||||
|
|
||||||
private void startPlugins(List<Plugin> scannedPlugins, boolean init, boolean initConfig)
|
if (groups.getInstanceCount() > 1)
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
plugins.addAll(scanAndInstantiate(scannedPlugins, init, initConfig));
|
for (String pluginId : getDisabledPlugins())
|
||||||
|
{
|
||||||
|
groups.sendString("STOPEXTERNAL;" + pluginId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (IOException ignored)
|
else
|
||||||
{
|
{
|
||||||
|
for (String pluginId : getDisabledPlugins())
|
||||||
|
{
|
||||||
|
externalPluginManager.enablePlugin(pluginId);
|
||||||
|
externalPluginManager.deletePlugin(pluginId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -633,55 +857,57 @@ class ExternalPluginManager
|
|||||||
for (Plugin plugin : extensions)
|
for (Plugin plugin : extensions)
|
||||||
{
|
{
|
||||||
pluginClassLoaders.add(plugin.getClass().getClassLoader());
|
pluginClassLoaders.add(plugin.getClass().getClassLoader());
|
||||||
|
|
||||||
|
pluginsMap.remove(plugin.getClass().getSimpleName());
|
||||||
pluginsMap.put(plugin.getClass().getSimpleName(), pluginId);
|
pluginsMap.put(plugin.getClass().getSimpleName(), pluginId);
|
||||||
|
|
||||||
|
pluginsInfoMap.remove(plugin.getClass().getSimpleName());
|
||||||
|
|
||||||
|
AtomicReference<String> support = new AtomicReference<>("");
|
||||||
|
AtomicReference<String> version = new AtomicReference<>("");
|
||||||
|
|
||||||
|
updateManager.getRepositories().forEach(repository ->
|
||||||
|
repository.getPlugins().forEach((key, value) -> {
|
||||||
|
if (key.equals(pluginId))
|
||||||
|
{
|
||||||
|
support.set(value.projectUrl);
|
||||||
|
|
||||||
|
for (PluginInfo.PluginRelease release : value.releases)
|
||||||
|
{
|
||||||
|
if (externalPluginManager.getSystemVersion().equals("0.0.0") || externalPluginManager.getVersionManager().checkVersionConstraint(externalPluginManager.getSystemVersion(), release.requires))
|
||||||
|
{
|
||||||
|
if (lastPluginRelease.get(pluginId) == null)
|
||||||
|
{
|
||||||
|
lastPluginRelease.put(pluginId, release);
|
||||||
|
}
|
||||||
|
else if (externalPluginManager.getVersionManager().compareVersions(release.version, lastPluginRelease.get(pluginId).version) > 0)
|
||||||
|
{
|
||||||
|
lastPluginRelease.put(pluginId, release);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
version.set(lastPluginRelease.get(pluginId).version);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
pluginsInfoMap.put(
|
||||||
|
plugin.getClass().getSimpleName(),
|
||||||
|
new HashMap<>()
|
||||||
|
{{
|
||||||
|
put("version", version.get());
|
||||||
|
put("id", externalPluginManager.getPlugin(pluginId).getDescriptor().getPluginId());
|
||||||
|
put("provider", externalPluginManager.getPlugin(pluginId).getDescriptor().getProvider());
|
||||||
|
put("support", support.get());
|
||||||
|
}}
|
||||||
|
);
|
||||||
|
|
||||||
scannedPlugins.add(plugin);
|
scannedPlugins.add(plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
return scannedPlugins;
|
return scannedPlugins;
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
|
||||||
{
|
|
||||||
SwingUtilities.invokeAndWait(() ->
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
runelitePluginManager.stopPlugin(plugin);
|
|
||||||
}
|
|
||||||
catch (Exception e2)
|
|
||||||
{
|
|
||||||
throw new RuntimeException(e2);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
runelitePluginManager.remove(plugin);
|
|
||||||
|
|
||||||
eventBus.post(ExternalPluginChanged.class, new ExternalPluginChanged(pluginId, plugin, false));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
log.warn("unable to stop plugin", ex);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Path stopPlugin(String pluginId)
|
private Path stopPlugin(String pluginId)
|
||||||
{
|
{
|
||||||
List<PluginWrapper> startedPlugins = ImmutableList.copyOf(getStartedPlugins());
|
List<PluginWrapper> startedPlugins = ImmutableList.copyOf(getStartedPlugins());
|
||||||
@@ -704,7 +930,7 @@ class ExternalPluginManager
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
SwingUtilities.invokeAndWait(() ->
|
SwingUtil.syncExec(() ->
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -716,6 +942,7 @@ class ExternalPluginManager
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
runelitePluginManager.remove(plugin);
|
runelitePluginManager.remove(plugin);
|
||||||
|
pluginClassLoaders.remove(plugin.getClass().getClassLoader());
|
||||||
|
|
||||||
eventBus.post(ExternalPluginChanged.class, new ExternalPluginChanged(pluginId, plugin, false));
|
eventBus.post(ExternalPluginChanged.class, new ExternalPluginChanged(pluginId, plugin, false));
|
||||||
|
|
||||||
@@ -734,13 +961,13 @@ class ExternalPluginManager
|
|||||||
|
|
||||||
public boolean install(String pluginId) throws VerifyException
|
public boolean install(String pluginId) throws VerifyException
|
||||||
{
|
{
|
||||||
|
|
||||||
if (getDisabledPlugins().contains(pluginId))
|
if (getDisabledPlugins().contains(pluginId))
|
||||||
{
|
{
|
||||||
this.externalPluginManager.enablePlugin(pluginId);
|
externalPluginManager.enablePlugin(pluginId);
|
||||||
this.externalPluginManager.startPlugin(pluginId);
|
externalPluginManager.startPlugin(pluginId);
|
||||||
|
|
||||||
startPlugins(loadPlugin(pluginId), true, false);
|
groups.broadcastSring("STARTEXTERNAL;" + pluginId);
|
||||||
|
scanAndInstantiate(loadPlugin(pluginId), true, false);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -775,7 +1002,9 @@ class ExternalPluginManager
|
|||||||
|
|
||||||
updateManager.installPlugin(pluginId, null);
|
updateManager.installPlugin(pluginId, null);
|
||||||
|
|
||||||
startPlugins(loadPlugin(pluginId), true, true);
|
scanAndInstantiate(loadPlugin(pluginId), true, true);
|
||||||
|
|
||||||
|
groups.broadcastSring("STARTEXTERNAL;" + pluginId);
|
||||||
}
|
}
|
||||||
catch (DependencyResolver.DependenciesNotFoundException ex)
|
catch (DependencyResolver.DependenciesNotFoundException ex)
|
||||||
{
|
{
|
||||||
@@ -792,6 +1021,11 @@ class ExternalPluginManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean uninstall(String pluginId)
|
public boolean uninstall(String pluginId)
|
||||||
|
{
|
||||||
|
return uninstall(pluginId, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean uninstall(String pluginId, boolean skip)
|
||||||
{
|
{
|
||||||
Path pluginPath = stopPlugin(pluginId);
|
Path pluginPath = stopPlugin(pluginId);
|
||||||
|
|
||||||
@@ -801,13 +1035,35 @@ class ExternalPluginManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
externalPluginManager.stopPlugin(pluginId);
|
externalPluginManager.stopPlugin(pluginId);
|
||||||
externalPluginManager.disablePlugin(pluginId);
|
|
||||||
|
if (skip)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groups.getInstanceCount() > 1)
|
||||||
|
{
|
||||||
|
groups.sendString("STOPEXTERNAL;" + pluginId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
externalPluginManager.deletePlugin(pluginId);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update()
|
public void update()
|
||||||
{
|
{
|
||||||
|
if (groups.getInstanceCount() > 1)
|
||||||
|
{
|
||||||
|
// Do not update when there is more than one client open -> api might contain changes
|
||||||
|
log.info("Not updating external plugins since there is more than 1 client open");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RuneLiteSplashScreen.stage(.59, "Updating external plugins");
|
||||||
|
|
||||||
boolean error = false;
|
boolean error = false;
|
||||||
if (updateManager.hasUpdates())
|
if (updateManager.hasUpdates())
|
||||||
{
|
{
|
||||||
@@ -828,6 +1084,7 @@ class ExternalPluginManager
|
|||||||
}
|
}
|
||||||
catch (PluginRuntimeException ex)
|
catch (PluginRuntimeException ex)
|
||||||
{
|
{
|
||||||
|
// This should never happen but can crash the client
|
||||||
log.warn("Cannot update plugin '{}', the user probably has another client open", plugin.id);
|
log.warn("Cannot update plugin '{}', the user probably has another client open", plugin.id);
|
||||||
error = true;
|
error = true;
|
||||||
break;
|
break;
|
||||||
@@ -861,15 +1118,99 @@ class ExternalPluginManager
|
|||||||
|
|
||||||
public List<String> getDisabledPlugins()
|
public List<String> getDisabledPlugins()
|
||||||
{
|
{
|
||||||
return this.externalPluginManager.getResolvedPlugins()
|
return externalPluginManager.getResolvedPlugins()
|
||||||
.stream()
|
.stream()
|
||||||
.filter(not(this.externalPluginManager.getStartedPlugins()::contains))
|
.filter(not(externalPluginManager.getStartedPlugins()::contains))
|
||||||
.map(PluginWrapper::getPluginId)
|
.map(PluginWrapper::getPluginId)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<PluginWrapper> getStartedPlugins()
|
public List<PluginWrapper> getStartedPlugins()
|
||||||
{
|
{
|
||||||
return this.externalPluginManager.getStartedPlugins();
|
return externalPluginManager.getStartedPlugins();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean reloadStart(String pluginId)
|
||||||
|
{
|
||||||
|
externalPluginManager.loadPlugins();
|
||||||
|
externalPluginManager.startPlugin(pluginId);
|
||||||
|
|
||||||
|
List<PluginWrapper> startedPlugins = ImmutableList.copyOf(getStartedPlugins());
|
||||||
|
List<Plugin> scannedPlugins = new ArrayList<>();
|
||||||
|
|
||||||
|
for (PluginWrapper pluginWrapper : startedPlugins)
|
||||||
|
{
|
||||||
|
if (!pluginId.equals(pluginWrapper.getDescriptor().getPluginId()))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkDepsAndStart(startedPlugins, scannedPlugins, pluginWrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
scanAndInstantiate(scannedPlugins, true, false);
|
||||||
|
|
||||||
|
groups.broadcastSring("STARTEXTERNAL;" + pluginId);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void receive(Message message)
|
||||||
|
{
|
||||||
|
if (message.getObject() instanceof ConfigChanged)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] messageObject = ((String) message.getObject()).split(";");
|
||||||
|
|
||||||
|
if (messageObject.length < 2)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String command = messageObject[0];
|
||||||
|
String pluginId = messageObject[1];
|
||||||
|
|
||||||
|
switch (command)
|
||||||
|
{
|
||||||
|
case "STARTEXTERNAL":
|
||||||
|
externalPluginManager.loadPlugins();
|
||||||
|
externalPluginManager.startPlugin(pluginId);
|
||||||
|
|
||||||
|
List<PluginWrapper> startedPlugins = ImmutableList.copyOf(getStartedPlugins());
|
||||||
|
List<Plugin> scannedPlugins = new ArrayList<>();
|
||||||
|
|
||||||
|
for (PluginWrapper pluginWrapper : startedPlugins)
|
||||||
|
{
|
||||||
|
if (!pluginId.equals(pluginWrapper.getDescriptor().getPluginId()))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkDepsAndStart(startedPlugins, scannedPlugins, pluginWrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
scanAndInstantiate(scannedPlugins, true, false);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "STOPEXTERNAL":
|
||||||
|
uninstall(pluginId, true);
|
||||||
|
externalPluginManager.unloadPlugin(pluginId);
|
||||||
|
groups.send(message.getSrc(), "STOPPEDEXTERNAL;" + pluginId);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "STOPPEDEXTERNAL":
|
||||||
|
groups.getMessageMap().get(pluginId).remove(message.getSrc());
|
||||||
|
|
||||||
|
if (groups.getMessageMap().get(pluginId).size() == 0)
|
||||||
|
{
|
||||||
|
groups.getMessageMap().remove(pluginId);
|
||||||
|
externalPluginManager.deletePlugin(pluginId);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,5 +63,9 @@ public class PluginClassLoader extends URLClassLoader
|
|||||||
// fall back to main class loader
|
// fall back to main class loader
|
||||||
return parent.loadClass(name);
|
return parent.loadClass(name);
|
||||||
}
|
}
|
||||||
|
catch (NoClassDefFoundError ex)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ import com.google.inject.CreationException;
|
|||||||
import com.google.inject.Injector;
|
import com.google.inject.Injector;
|
||||||
import com.google.inject.Key;
|
import com.google.inject.Key;
|
||||||
import com.google.inject.Module;
|
import com.google.inject.Module;
|
||||||
|
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -56,6 +58,7 @@ import java.util.concurrent.Future;
|
|||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
@@ -68,6 +71,7 @@ import net.runelite.client.config.ConfigGroup;
|
|||||||
import net.runelite.client.config.ConfigManager;
|
import net.runelite.client.config.ConfigManager;
|
||||||
import net.runelite.client.config.RuneLiteConfig;
|
import net.runelite.client.config.RuneLiteConfig;
|
||||||
import net.runelite.client.eventbus.EventBus;
|
import net.runelite.client.eventbus.EventBus;
|
||||||
|
import net.runelite.client.events.ConfigChanged;
|
||||||
import net.runelite.client.events.PluginChanged;
|
import net.runelite.client.events.PluginChanged;
|
||||||
import net.runelite.client.events.SessionClose;
|
import net.runelite.client.events.SessionClose;
|
||||||
import net.runelite.client.events.SessionOpen;
|
import net.runelite.client.events.SessionOpen;
|
||||||
@@ -76,6 +80,8 @@ import net.runelite.client.task.ScheduledMethod;
|
|||||||
import net.runelite.client.task.Scheduler;
|
import net.runelite.client.task.Scheduler;
|
||||||
import net.runelite.client.ui.RuneLiteSplashScreen;
|
import net.runelite.client.ui.RuneLiteSplashScreen;
|
||||||
import net.runelite.client.util.GameEventManager;
|
import net.runelite.client.util.GameEventManager;
|
||||||
|
import net.runelite.client.util.Groups;
|
||||||
|
import org.jgroups.Message;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@@ -96,6 +102,8 @@ public class PluginManager
|
|||||||
private final List<PluginConfigurationDescriptor> fakePlugins = new ArrayList<>();
|
private final List<PluginConfigurationDescriptor> fakePlugins = new ArrayList<>();
|
||||||
private final String runeliteGroupName = RuneLiteConfig.class
|
private final String runeliteGroupName = RuneLiteConfig.class
|
||||||
.getAnnotation(ConfigGroup.class).value();
|
.getAnnotation(ConfigGroup.class).value();
|
||||||
|
private final Groups groups;
|
||||||
|
private final File settingsFileInput;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ExternalPluginLoader externalPluginLoader;
|
ExternalPluginLoader externalPluginLoader;
|
||||||
@@ -109,18 +117,29 @@ public class PluginManager
|
|||||||
final EventBus eventBus,
|
final EventBus eventBus,
|
||||||
final Scheduler scheduler,
|
final Scheduler scheduler,
|
||||||
final ConfigManager configManager,
|
final ConfigManager configManager,
|
||||||
final Provider<GameEventManager> sceneTileManager)
|
final Provider<GameEventManager> sceneTileManager,
|
||||||
|
final Groups groups,
|
||||||
|
final @Named("config") File config)
|
||||||
{
|
{
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
this.scheduler = scheduler;
|
this.scheduler = scheduler;
|
||||||
this.configManager = configManager;
|
this.configManager = configManager;
|
||||||
this.sceneTileManager = sceneTileManager;
|
this.sceneTileManager = sceneTileManager;
|
||||||
|
this.groups = groups;
|
||||||
|
this.settingsFileInput = config;
|
||||||
|
|
||||||
if (eventBus != null)
|
if (eventBus != null)
|
||||||
{
|
{
|
||||||
eventBus.subscribe(SessionOpen.class, this, this::onSessionOpen);
|
eventBus.subscribe(SessionOpen.class, this, this::onSessionOpen);
|
||||||
eventBus.subscribe(SessionClose.class, this, this::onSessionClose);
|
eventBus.subscribe(SessionClose.class, this, this::onSessionClose);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (groups != null)
|
||||||
|
{
|
||||||
|
groups.getMessageStringSubject()
|
||||||
|
.subscribeOn(Schedulers.from(SwingUtilities::invokeLater))
|
||||||
|
.subscribe(this::receive);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onSessionOpen(SessionOpen event)
|
private void onSessionOpen(SessionOpen event)
|
||||||
@@ -321,6 +340,12 @@ public class PluginManager
|
|||||||
for (ClassInfo classInfo : classes)
|
for (ClassInfo classInfo : classes)
|
||||||
{
|
{
|
||||||
Class<?> clazz = classInfo.load();
|
Class<?> clazz = classInfo.load();
|
||||||
|
|
||||||
|
if (clazz == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
PluginDescriptor pluginDescriptor = clazz.getAnnotation(PluginDescriptor.class);
|
PluginDescriptor pluginDescriptor = clazz.getAnnotation(PluginDescriptor.class);
|
||||||
|
|
||||||
if (pluginDescriptor == null)
|
if (pluginDescriptor == null)
|
||||||
@@ -462,6 +487,8 @@ public class PluginManager
|
|||||||
throw new PluginInstantiationException(ex);
|
throw new PluginInstantiationException(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
groups.broadcastSring("STARTPLUGIN;" + plugin.getClass().getSimpleName() + ";" + settingsFileInput.getAbsolutePath());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -491,6 +518,8 @@ public class PluginManager
|
|||||||
throw new PluginInstantiationException(ex);
|
throw new PluginInstantiationException(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
groups.broadcastSring("STOPPLUGIN;" + plugin.getClass().getSimpleName() + ";" + settingsFileInput.getAbsolutePath());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -661,4 +690,77 @@ public class PluginManager
|
|||||||
incrementChildren(graph, dependencyCount, m, val + 1));
|
incrementChildren(graph, dependencyCount, m, val + 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void receive(Message message)
|
||||||
|
{
|
||||||
|
if (message.getObject() instanceof ConfigChanged)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] messageObject = ((String) message.getObject()).split(";");
|
||||||
|
|
||||||
|
if (messageObject.length < 3)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String command = messageObject[0];
|
||||||
|
String pluginName = messageObject[1];
|
||||||
|
String path = messageObject[2];
|
||||||
|
Plugin plugin = null;
|
||||||
|
|
||||||
|
if (!path.equals(settingsFileInput.getAbsolutePath()))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Plugin pl : getPlugins())
|
||||||
|
{
|
||||||
|
if (pl.getClass().getSimpleName().equals(pluginName))
|
||||||
|
{
|
||||||
|
plugin = pl;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (plugin == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Plugin finalPlugin = plugin;
|
||||||
|
|
||||||
|
switch (command)
|
||||||
|
{
|
||||||
|
case "STARTPLUGIN":
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
startPlugin(finalPlugin);
|
||||||
|
}
|
||||||
|
catch (PluginInstantiationException e)
|
||||||
|
{
|
||||||
|
log.warn("unable to start plugin", e);
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "STOPPLUGIN":
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
stopPlugin(finalPlugin);
|
||||||
|
}
|
||||||
|
catch (PluginInstantiationException e)
|
||||||
|
{
|
||||||
|
log.warn("unable to stop plugin", e);
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import java.awt.BorderLayout;
|
|||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Font;
|
||||||
import java.awt.GridLayout;
|
import java.awt.GridLayout;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
@@ -78,6 +79,7 @@ import javax.swing.plaf.basic.BasicSpinnerUI;
|
|||||||
import javax.swing.text.JTextComponent;
|
import javax.swing.text.JTextComponent;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.runelite.api.util.Text;
|
import net.runelite.api.util.Text;
|
||||||
|
import net.runelite.client.RuneLite;
|
||||||
import net.runelite.client.config.Button;
|
import net.runelite.client.config.Button;
|
||||||
import net.runelite.client.config.ConfigDescriptor;
|
import net.runelite.client.config.ConfigDescriptor;
|
||||||
import net.runelite.client.config.ConfigItem;
|
import net.runelite.client.config.ConfigItem;
|
||||||
@@ -90,7 +92,9 @@ import net.runelite.client.config.ModifierlessKeybind;
|
|||||||
import net.runelite.client.config.Range;
|
import net.runelite.client.config.Range;
|
||||||
import net.runelite.client.config.Units;
|
import net.runelite.client.config.Units;
|
||||||
import net.runelite.client.eventbus.EventBus;
|
import net.runelite.client.eventbus.EventBus;
|
||||||
|
import net.runelite.client.events.ConfigChanged;
|
||||||
import net.runelite.client.events.PluginChanged;
|
import net.runelite.client.events.PluginChanged;
|
||||||
|
import net.runelite.client.plugins.ExternalPluginManager;
|
||||||
import net.runelite.client.plugins.PluginManager;
|
import net.runelite.client.plugins.PluginManager;
|
||||||
import net.runelite.client.ui.ColorScheme;
|
import net.runelite.client.ui.ColorScheme;
|
||||||
import net.runelite.client.ui.DynamicGridLayout;
|
import net.runelite.client.ui.DynamicGridLayout;
|
||||||
@@ -102,6 +106,7 @@ import net.runelite.client.ui.components.colorpicker.ColorPickerManager;
|
|||||||
import net.runelite.client.ui.components.colorpicker.RuneliteColorPicker;
|
import net.runelite.client.ui.components.colorpicker.RuneliteColorPicker;
|
||||||
import net.runelite.client.util.ColorUtil;
|
import net.runelite.client.util.ColorUtil;
|
||||||
import net.runelite.client.util.ImageUtil;
|
import net.runelite.client.util.ImageUtil;
|
||||||
|
import net.runelite.client.util.LinkBrowser;
|
||||||
import net.runelite.client.util.MiscUtils;
|
import net.runelite.client.util.MiscUtils;
|
||||||
import net.runelite.client.util.SwingUtil;
|
import net.runelite.client.util.SwingUtil;
|
||||||
|
|
||||||
@@ -132,6 +137,9 @@ class ConfigPanel extends PluginPanel
|
|||||||
@Inject
|
@Inject
|
||||||
private PluginManager pluginManager;
|
private PluginManager pluginManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ExternalPluginManager externalPluginManager;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private ColorPickerManager colorPickerManager;
|
private ColorPickerManager colorPickerManager;
|
||||||
|
|
||||||
@@ -233,6 +241,7 @@ class ConfigPanel extends PluginPanel
|
|||||||
|
|
||||||
rebuild(false);
|
rebuild(false);
|
||||||
eventBus.subscribe(PluginChanged.class, this, this::onPluginChanged);
|
eventBus.subscribe(PluginChanged.class, this, this::onPluginChanged);
|
||||||
|
eventBus.subscribe(ConfigChanged.class, this, this::onConfigChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void getSections(ConfigDescriptor cd)
|
private void getSections(ConfigDescriptor cd)
|
||||||
@@ -425,11 +434,44 @@ class ConfigPanel extends PluginPanel
|
|||||||
ConfigDescriptor cd = pluginConfig.getConfigDescriptor();
|
ConfigDescriptor cd = pluginConfig.getConfigDescriptor();
|
||||||
assert cd != null;
|
assert cd != null;
|
||||||
|
|
||||||
|
List<JButton> buttons = new ArrayList<>();
|
||||||
|
|
||||||
|
Map<String, Map<String, String>> pluginsInfoMap = externalPluginManager.getPluginsInfoMap();
|
||||||
|
|
||||||
|
if (pluginConfig.getPlugin() != null && pluginsInfoMap.containsKey(pluginConfig.getPlugin().getClass().getSimpleName()))
|
||||||
|
{
|
||||||
|
|
||||||
|
JPanel infoPanel = new JPanel();
|
||||||
|
infoPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||||
|
infoPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
|
||||||
|
infoPanel.setLayout(new GridLayout(0, 1));
|
||||||
|
|
||||||
|
final Font smallFont = FontManager.getRunescapeSmallFont();
|
||||||
|
|
||||||
|
Map<String, String> pluginInfo = pluginsInfoMap.get(pluginConfig.getPlugin().getClass().getSimpleName());
|
||||||
|
|
||||||
|
JLabel idLabel = new JLabel(htmlLabel("id", pluginInfo.get("id")));
|
||||||
|
idLabel.setFont(smallFont);
|
||||||
|
infoPanel.add(idLabel);
|
||||||
|
|
||||||
|
JLabel versionLabel = new JLabel(htmlLabel("version", pluginInfo.get("version")));
|
||||||
|
versionLabel.setFont(smallFont);
|
||||||
|
infoPanel.add(versionLabel);
|
||||||
|
|
||||||
|
JLabel providerLabel = new JLabel(htmlLabel("provider", pluginInfo.get("provider")));
|
||||||
|
providerLabel.setFont(smallFont);
|
||||||
|
infoPanel.add(providerLabel);
|
||||||
|
|
||||||
|
JButton button = new JButton("Support");
|
||||||
|
button.addActionListener(e -> LinkBrowser.browse(pluginInfo.get("support")));
|
||||||
|
buttons.add(button);
|
||||||
|
|
||||||
|
mainPanel.add(infoPanel);
|
||||||
|
}
|
||||||
|
|
||||||
getSections(cd);
|
getSections(cd);
|
||||||
getTitleSections(cd);
|
getTitleSections(cd);
|
||||||
|
|
||||||
List<JButton> buttons = new ArrayList<>();
|
|
||||||
|
|
||||||
for (ConfigItemDescriptor cid : cd.getItems())
|
for (ConfigItemDescriptor cid : cd.getItems())
|
||||||
{
|
{
|
||||||
if (cid == null)
|
if (cid == null)
|
||||||
@@ -1168,4 +1210,25 @@ class ConfigPanel extends PluginPanel
|
|||||||
pluginToggle.setSelected(event.isLoaded()));
|
pluginToggle.setSelected(event.isLoaded()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onConfigChanged(ConfigChanged event)
|
||||||
|
{
|
||||||
|
if (event.getOrigin().equals(RuneLite.uuid))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SwingUtilities.invokeAndWait(() -> rebuild(true));
|
||||||
|
}
|
||||||
|
catch (InterruptedException | InvocationTargetException e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String htmlLabel(String key, String value)
|
||||||
|
{
|
||||||
|
return "<html><body style = 'color:#a5a5a5'>" + key + ": <span style = 'color:white'>" + value + "</span></body></html>";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -34,13 +34,19 @@ import java.awt.image.BufferedImage;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JOptionPane;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.JToggleButton;
|
import javax.swing.JToggleButton;
|
||||||
|
import javax.swing.SwingWorker;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import net.runelite.client.RuneLiteProperties;
|
||||||
|
import net.runelite.client.plugins.ExternalPluginManager;
|
||||||
import net.runelite.client.plugins.PluginType;
|
import net.runelite.client.plugins.PluginType;
|
||||||
|
import net.runelite.client.ui.ClientUI;
|
||||||
import net.runelite.client.ui.ColorScheme;
|
import net.runelite.client.ui.ColorScheme;
|
||||||
import net.runelite.client.ui.PluginPanel;
|
import net.runelite.client.ui.PluginPanel;
|
||||||
import net.runelite.client.util.ImageUtil;
|
import net.runelite.client.util.ImageUtil;
|
||||||
@@ -50,6 +56,8 @@ public class PluginListItem extends JPanel
|
|||||||
{
|
{
|
||||||
private static final ImageIcon CONFIG_ICON;
|
private static final ImageIcon CONFIG_ICON;
|
||||||
private static final ImageIcon CONFIG_ICON_HOVER;
|
private static final ImageIcon CONFIG_ICON_HOVER;
|
||||||
|
private static final ImageIcon REFRESH_ICON;
|
||||||
|
private static final ImageIcon REFRESH_ICON_HOVER;
|
||||||
private static final ImageIcon ON_STAR;
|
private static final ImageIcon ON_STAR;
|
||||||
private static final ImageIcon OFF_STAR;
|
private static final ImageIcon OFF_STAR;
|
||||||
|
|
||||||
@@ -70,10 +78,13 @@ public class PluginListItem extends JPanel
|
|||||||
static
|
static
|
||||||
{
|
{
|
||||||
BufferedImage configIcon = ImageUtil.getResourceStreamFromClass(ConfigPanel.class, "config_edit_icon.png");
|
BufferedImage configIcon = ImageUtil.getResourceStreamFromClass(ConfigPanel.class, "config_edit_icon.png");
|
||||||
|
BufferedImage refreshIcon = ImageUtil.getResourceStreamFromClass(ConfigPanel.class, "refresh.png");
|
||||||
BufferedImage onStar = ImageUtil.getResourceStreamFromClass(ConfigPanel.class, "star_on.png");
|
BufferedImage onStar = ImageUtil.getResourceStreamFromClass(ConfigPanel.class, "star_on.png");
|
||||||
CONFIG_ICON = new ImageIcon(configIcon);
|
CONFIG_ICON = new ImageIcon(configIcon);
|
||||||
|
REFRESH_ICON = new ImageIcon(refreshIcon);
|
||||||
ON_STAR = new ImageIcon(ImageUtil.recolorImage(onStar, ColorScheme.BRAND_BLUE));
|
ON_STAR = new ImageIcon(ImageUtil.recolorImage(onStar, ColorScheme.BRAND_BLUE));
|
||||||
CONFIG_ICON_HOVER = new ImageIcon(ImageUtil.luminanceOffset(configIcon, -100));
|
CONFIG_ICON_HOVER = new ImageIcon(ImageUtil.luminanceOffset(configIcon, -100));
|
||||||
|
REFRESH_ICON_HOVER = new ImageIcon(ImageUtil.luminanceOffset(refreshIcon, -100));
|
||||||
|
|
||||||
BufferedImage offStar = ImageUtil.luminanceScale(
|
BufferedImage offStar = ImageUtil.luminanceScale(
|
||||||
ImageUtil.grayscaleImage(onStar),
|
ImageUtil.grayscaleImage(onStar),
|
||||||
@@ -82,7 +93,7 @@ public class PluginListItem extends JPanel
|
|||||||
OFF_STAR = new ImageIcon(offStar);
|
OFF_STAR = new ImageIcon(offStar);
|
||||||
}
|
}
|
||||||
|
|
||||||
PluginListItem(PluginListPanel pluginListPanel, PluginConfigurationDescriptor pluginConfig)
|
PluginListItem(PluginListPanel pluginListPanel, PluginConfigurationDescriptor pluginConfig, ExternalPluginManager externalPluginManager)
|
||||||
{
|
{
|
||||||
this.pluginListPanel = pluginListPanel;
|
this.pluginListPanel = pluginListPanel;
|
||||||
this.pluginConfig = pluginConfig;
|
this.pluginConfig = pluginConfig;
|
||||||
@@ -119,6 +130,55 @@ public class PluginListItem extends JPanel
|
|||||||
buttonPanel.setLayout(new GridLayout(1, 2));
|
buttonPanel.setLayout(new GridLayout(1, 2));
|
||||||
add(buttonPanel, BorderLayout.LINE_END);
|
add(buttonPanel, BorderLayout.LINE_END);
|
||||||
|
|
||||||
|
Map<String, Map<String, String>> pluginsInfoMap = externalPluginManager.getPluginsInfoMap();
|
||||||
|
|
||||||
|
if (RuneLiteProperties.getLauncherVersion() == null && pluginConfig.getPlugin() != null && pluginsInfoMap.containsKey(pluginConfig.getPlugin().getClass().getSimpleName()))
|
||||||
|
{
|
||||||
|
JButton hotSwapButton = new JButton(REFRESH_ICON);
|
||||||
|
hotSwapButton.setRolloverIcon(REFRESH_ICON_HOVER);
|
||||||
|
SwingUtil.removeButtonDecorations(hotSwapButton);
|
||||||
|
hotSwapButton.setPreferredSize(new Dimension(25, 0));
|
||||||
|
hotSwapButton.setVisible(false);
|
||||||
|
buttonPanel.add(hotSwapButton);
|
||||||
|
|
||||||
|
hotSwapButton.addActionListener(e ->
|
||||||
|
{
|
||||||
|
Map<String, String> pluginInfo = pluginsInfoMap.get(pluginConfig.getPlugin().getClass().getSimpleName());
|
||||||
|
String pluginId = pluginInfo.get("id");
|
||||||
|
|
||||||
|
hotSwapButton.setIcon(REFRESH_ICON);
|
||||||
|
externalPluginManager.uninstall(pluginId);
|
||||||
|
|
||||||
|
SwingWorker<Boolean, Void> worker = new SwingWorker<>()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
protected Boolean doInBackground()
|
||||||
|
{
|
||||||
|
return externalPluginManager.uninstall(pluginId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
worker.execute();
|
||||||
|
|
||||||
|
JOptionPane.showMessageDialog(ClientUI.getFrame(),
|
||||||
|
pluginId + " is unloaded, put the new jar file in the externalmanager folder and click `ok`",
|
||||||
|
"Hotswap " + pluginId,
|
||||||
|
JOptionPane.INFORMATION_MESSAGE);
|
||||||
|
|
||||||
|
worker = new SwingWorker<>()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
protected Boolean doInBackground()
|
||||||
|
{
|
||||||
|
return externalPluginManager.reloadStart(pluginId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
worker.execute();
|
||||||
|
});
|
||||||
|
|
||||||
|
hotSwapButton.setVisible(true);
|
||||||
|
hotSwapButton.setToolTipText("Hotswap plugin");
|
||||||
|
}
|
||||||
|
|
||||||
if (pluginConfig.hasConfigurables())
|
if (pluginConfig.hasConfigurables())
|
||||||
{
|
{
|
||||||
JButton configButton = new JButton(CONFIG_ICON);
|
JButton configButton = new JButton(CONFIG_ICON);
|
||||||
@@ -211,8 +271,6 @@ public class PluginListItem extends JPanel
|
|||||||
*/
|
*/
|
||||||
static void addLabelMouseOver(final JLabel label)
|
static void addLabelMouseOver(final JLabel label)
|
||||||
{
|
{
|
||||||
final Color labelForeground = label.getForeground();
|
|
||||||
|
|
||||||
label.addMouseListener(new MouseAdapter()
|
label.addMouseListener(new MouseAdapter()
|
||||||
{
|
{
|
||||||
private Color lastForeground;
|
private Color lastForeground;
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ import javax.swing.event.DocumentListener;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.runelite.api.util.Text;
|
import net.runelite.api.util.Text;
|
||||||
|
import net.runelite.client.RuneLite;
|
||||||
import net.runelite.client.config.Config;
|
import net.runelite.client.config.Config;
|
||||||
import net.runelite.client.config.ConfigDescriptor;
|
import net.runelite.client.config.ConfigDescriptor;
|
||||||
import net.runelite.client.config.ConfigGroup;
|
import net.runelite.client.config.ConfigGroup;
|
||||||
@@ -73,6 +74,7 @@ import net.runelite.client.events.ConfigChanged;
|
|||||||
import net.runelite.client.events.ExternalPluginChanged;
|
import net.runelite.client.events.ExternalPluginChanged;
|
||||||
import net.runelite.client.events.ExternalPluginsLoaded;
|
import net.runelite.client.events.ExternalPluginsLoaded;
|
||||||
import net.runelite.client.events.PluginChanged;
|
import net.runelite.client.events.PluginChanged;
|
||||||
|
import net.runelite.client.plugins.ExternalPluginManager;
|
||||||
import net.runelite.client.plugins.Plugin;
|
import net.runelite.client.plugins.Plugin;
|
||||||
import net.runelite.client.plugins.PluginDescriptor;
|
import net.runelite.client.plugins.PluginDescriptor;
|
||||||
import net.runelite.client.plugins.PluginInstantiationException;
|
import net.runelite.client.plugins.PluginInstantiationException;
|
||||||
@@ -110,6 +112,7 @@ public class PluginListPanel extends PluginPanel
|
|||||||
private final PluginManager pluginManager;
|
private final PluginManager pluginManager;
|
||||||
private final Provider<ConfigPanel> configPanelProvider;
|
private final Provider<ConfigPanel> configPanelProvider;
|
||||||
private final OpenOSRSConfig openOSRSConfig;
|
private final OpenOSRSConfig openOSRSConfig;
|
||||||
|
private final ExternalPluginManager externalPluginManager;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private final MultiplexingPluginPanel muxer;
|
private final MultiplexingPluginPanel muxer;
|
||||||
@@ -140,6 +143,7 @@ public class PluginListPanel extends PluginPanel
|
|||||||
PluginManager pluginManager,
|
PluginManager pluginManager,
|
||||||
Provider<ConfigPanel> configPanelProvider,
|
Provider<ConfigPanel> configPanelProvider,
|
||||||
OpenOSRSConfig openOSRSConfig,
|
OpenOSRSConfig openOSRSConfig,
|
||||||
|
ExternalPluginManager externalPluginManager,
|
||||||
EventBus eventBus)
|
EventBus eventBus)
|
||||||
{
|
{
|
||||||
super(false);
|
super(false);
|
||||||
@@ -148,8 +152,14 @@ public class PluginListPanel extends PluginPanel
|
|||||||
this.pluginManager = pluginManager;
|
this.pluginManager = pluginManager;
|
||||||
this.configPanelProvider = configPanelProvider;
|
this.configPanelProvider = configPanelProvider;
|
||||||
this.openOSRSConfig = openOSRSConfig;
|
this.openOSRSConfig = openOSRSConfig;
|
||||||
|
this.externalPluginManager = externalPluginManager;
|
||||||
|
|
||||||
eventBus.subscribe(ConfigChanged.class, this, ev -> {
|
eventBus.subscribe(ConfigChanged.class, this, ev -> {
|
||||||
|
if (ev.getGroup().equals("runelite") && ev.getKey().equals("pinnedPlugins") && !ev.getOrigin().equals(RuneLite.uuid))
|
||||||
|
{
|
||||||
|
SwingUtilities.invokeLater(this::rebuildPluginList);
|
||||||
|
}
|
||||||
|
|
||||||
if (!ev.getGroup().equals("openosrs"))
|
if (!ev.getGroup().equals("openosrs"))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -157,7 +167,7 @@ public class PluginListPanel extends PluginPanel
|
|||||||
|
|
||||||
if (ev.getKey().equals("enableCategories") || ev.getKey().equals("pluginSortMode"))
|
if (ev.getKey().equals("enableCategories") || ev.getKey().equals("pluginSortMode"))
|
||||||
{
|
{
|
||||||
rebuildPluginList();
|
SwingUtilities.invokeLater(this::rebuildPluginList);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ev.getKey().equals("pluginSortMode"))
|
if (ev.getKey().equals("pluginSortMode"))
|
||||||
@@ -264,7 +274,7 @@ public class PluginListPanel extends PluginPanel
|
|||||||
})
|
})
|
||||||
).map(desc ->
|
).map(desc ->
|
||||||
{
|
{
|
||||||
PluginListItem listItem = new PluginListItem(this, desc);
|
PluginListItem listItem = new PluginListItem(this, desc, externalPluginManager);
|
||||||
listItem.setPinned(pinnedPlugins.contains(desc.getName()));
|
listItem.setPinned(pinnedPlugins.contains(desc.getName()));
|
||||||
listItem.setColor(getColorByCategory(listItem.getPluginType()));
|
listItem.setColor(getColorByCategory(listItem.getPluginType()));
|
||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,11 @@ public class RuneLiteSplashScreen extends JFrame
|
|||||||
this.setVisible(true);
|
this.setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean showing()
|
||||||
|
{
|
||||||
|
return INSTANCE != null;
|
||||||
|
}
|
||||||
|
|
||||||
public static void setError(String title, String content)
|
public static void setError(String title, String content)
|
||||||
{
|
{
|
||||||
if (INSTANCE != null)
|
if (INSTANCE != null)
|
||||||
|
|||||||
@@ -34,9 +34,6 @@ import java.awt.event.MouseAdapter;
|
|||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.Properties;
|
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
@@ -54,9 +51,6 @@ import net.runelite.client.util.LinkBrowser;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class InfoPanel extends JPanel
|
public class InfoPanel extends JPanel
|
||||||
{
|
{
|
||||||
private static final String RUNELITE_VERSION = "runelite.version";
|
|
||||||
private static final String RUNELITE_PLUS_VERSION = "open.osrs.version";
|
|
||||||
private static final String RUNELITE_PLUS_DATE = "open.osrs.builddate";
|
|
||||||
private static final Color DARK_GREY = new Color(10, 10, 10, 255);
|
private static final Color DARK_GREY = new Color(10, 10, 10, 255);
|
||||||
|
|
||||||
private static final BufferedImage TRANSPARENT_LOGO = ImageUtil.getResourceStreamFromClass(InfoPanel.class, "/openosrs.png");
|
private static final BufferedImage TRANSPARENT_LOGO = ImageUtil.getResourceStreamFromClass(InfoPanel.class, "/openosrs.png");
|
||||||
@@ -67,16 +61,6 @@ public class InfoPanel extends JPanel
|
|||||||
|
|
||||||
public InfoPanel()
|
public InfoPanel()
|
||||||
{
|
{
|
||||||
Properties properties = new Properties();
|
|
||||||
try (InputStream in = getClass().getResourceAsStream("/open.osrs.properties"))
|
|
||||||
{
|
|
||||||
properties.load(in);
|
|
||||||
}
|
|
||||||
catch (IOException ex)
|
|
||||||
{
|
|
||||||
log.warn("unable to load propertries", ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setLayout(new GridBagLayout());
|
this.setLayout(new GridBagLayout());
|
||||||
this.setPreferredSize(PANEL_SIZE);
|
this.setPreferredSize(PANEL_SIZE);
|
||||||
this.setBackground(new Color(38, 38, 38));
|
this.setBackground(new Color(38, 38, 38));
|
||||||
@@ -104,11 +88,11 @@ public class InfoPanel extends JPanel
|
|||||||
c.weighty = 0;
|
c.weighty = 0;
|
||||||
|
|
||||||
// OpenOSRS version
|
// OpenOSRS version
|
||||||
this.add(createPanelTextButton("OpenOSRS Version: " + properties.getProperty(RUNELITE_PLUS_VERSION)), c);
|
this.add(createPanelTextButton("OpenOSRS Version: " + RuneLiteProperties.getPlusVersion()), c);
|
||||||
c.gridy++;
|
c.gridy++;
|
||||||
|
|
||||||
// Build date
|
// Build date
|
||||||
this.add(createPanelTextButton("Build date: " + properties.getProperty(RUNELITE_PLUS_DATE)), c);
|
this.add(createPanelTextButton("Build date: " + RuneLiteProperties.getPlusDate()), c);
|
||||||
c.gridy++;
|
c.gridy++;
|
||||||
|
|
||||||
final JLabel logsFolder = createPanelButton("Open logs folder", null, () -> LinkBrowser.openLocalFile(LOGS_DIR));
|
final JLabel logsFolder = createPanelButton("Open logs folder", null, () -> LinkBrowser.openLocalFile(LOGS_DIR));
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
/*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2018 by rumatoest at github.com
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package net.runelite.client.util;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Class AppLock.
|
|
||||||
*
|
|
||||||
* @author Vladislav Zablotsky
|
|
||||||
*/
|
|
||||||
public class AppLock
|
|
||||||
{
|
|
||||||
|
|
||||||
private static CrossLock lockInstance;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set lock for application instance.
|
|
||||||
* Method must be run only one time at application start.
|
|
||||||
*
|
|
||||||
* @param lockId Unique lock identifiers
|
|
||||||
* @return true if succeeded
|
|
||||||
*/
|
|
||||||
public synchronized boolean lock(String lockId)
|
|
||||||
{
|
|
||||||
if (lockInstance == null)
|
|
||||||
{
|
|
||||||
lockInstance = new CrossLock("application_" + lockId);
|
|
||||||
}
|
|
||||||
return lockInstance.lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Trying to release application lock.
|
|
||||||
* Thus another application instances will be able to use lock with current ID.
|
|
||||||
*/
|
|
||||||
public synchronized void release()
|
|
||||||
{
|
|
||||||
if (lockInstance != null)
|
|
||||||
{
|
|
||||||
lockInstance.clear();
|
|
||||||
}
|
|
||||||
lockInstance = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,228 +0,0 @@
|
|||||||
/*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2018 by rumatoest at github.com
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
package net.runelite.client.util;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.channels.FileChannel;
|
|
||||||
import java.nio.channels.FileLock;
|
|
||||||
import java.nio.channels.OverlappingFileLockException;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
import static net.runelite.client.RuneLite.RUNELITE_DIR;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Universal cross application instances locker.
|
|
||||||
* Allow you to create simple lock like object which can be used for
|
|
||||||
* different application instances. Basic idea is simple - just a simple file lock.
|
|
||||||
* <br />
|
|
||||||
* All you need is to define unique key for each lock type.
|
|
||||||
*
|
|
||||||
* @author Vladislav Zablotsky
|
|
||||||
*/
|
|
||||||
public class CrossLock
|
|
||||||
{
|
|
||||||
|
|
||||||
private static final HashMap<String, CrossLock> locks = new HashMap<>();
|
|
||||||
|
|
||||||
private final String id;
|
|
||||||
|
|
||||||
private final File fileToLock;
|
|
||||||
|
|
||||||
private FileOutputStream fileStream;
|
|
||||||
|
|
||||||
private FileChannel fileStreamChannel;
|
|
||||||
|
|
||||||
private FileLock lockOnFile;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Will create or retrieve lock instance.
|
|
||||||
* Each lock id is unique among all you instances,
|
|
||||||
* thus only one instance can acquire lock for this id.
|
|
||||||
*
|
|
||||||
* @param lockId Unique lock identifier
|
|
||||||
* @return Not null
|
|
||||||
*/
|
|
||||||
public static CrossLock get(String lockId)
|
|
||||||
{
|
|
||||||
if (locks.containsKey(lockId))
|
|
||||||
{
|
|
||||||
return locks.get(lockId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
synchronized (CrossLock.class)
|
|
||||||
{
|
|
||||||
if (locks.containsKey(lockId))
|
|
||||||
{
|
|
||||||
return locks.get(lockId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CrossLock cl = new CrossLock(lockId);
|
|
||||||
locks.put(lockId, cl);
|
|
||||||
return cl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Will remove lock object for specific id and release lock if any.
|
|
||||||
*
|
|
||||||
* @param lockId Unique lock identifier
|
|
||||||
*/
|
|
||||||
public static void remove(String lockId)
|
|
||||||
{
|
|
||||||
if (locks.containsKey(lockId))
|
|
||||||
{
|
|
||||||
CrossLock lock = null;
|
|
||||||
synchronized (CrossLock.class)
|
|
||||||
{
|
|
||||||
if (locks.containsKey(lockId))
|
|
||||||
{
|
|
||||||
lock = locks.remove(lockId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (lock != null)
|
|
||||||
{
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CrossLock(String lockId)
|
|
||||||
{
|
|
||||||
this.id = lockId;
|
|
||||||
fileToLock = new File(RUNELITE_DIR, lockId + ".app_lock");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return lock instance identifier.
|
|
||||||
*/
|
|
||||||
public String id()
|
|
||||||
{
|
|
||||||
return this.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Activate lock.
|
|
||||||
* Note! This is only cross application (cross instances) lock. It will not work
|
|
||||||
* as lock inside single application instance.
|
|
||||||
*
|
|
||||||
* @return true if lock was acquire or false
|
|
||||||
*/
|
|
||||||
public synchronized boolean lock()
|
|
||||||
{
|
|
||||||
if (lockOnFile != null && lockOnFile.isValid())
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
release();
|
|
||||||
}
|
|
||||||
|
|
||||||
String lockContent = "#Java AppLock Object\n#Locked by key: " + id() + "\r\n";
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (fileToLock.exists())
|
|
||||||
{
|
|
||||||
fileToLock.createNewFile();
|
|
||||||
}
|
|
||||||
fileStream = new FileOutputStream(fileToLock);
|
|
||||||
fileStreamChannel = fileStream.getChannel();
|
|
||||||
lockOnFile = fileStreamChannel.tryLock();
|
|
||||||
if (lockOnFile != null)
|
|
||||||
{
|
|
||||||
fileStream.write(lockContent.getBytes());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
if (!(ex instanceof OverlappingFileLockException))
|
|
||||||
{
|
|
||||||
Logger.getLogger(AppLock.class.getName()).log(Level.WARNING,
|
|
||||||
"Can not get application lock for id=" + id() + "\n" + ex.getMessage(), ex);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return lockOnFile != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Release lock associated with this object.
|
|
||||||
*/
|
|
||||||
public synchronized void release()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (lockOnFile != null && lockOnFile.isValid())
|
|
||||||
{
|
|
||||||
lockOnFile.release();
|
|
||||||
}
|
|
||||||
lockOnFile = null;
|
|
||||||
|
|
||||||
if (fileStream != null)
|
|
||||||
{
|
|
||||||
fileStream.close();
|
|
||||||
fileStream = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fileStreamChannel != null && fileStreamChannel.isOpen())
|
|
||||||
{
|
|
||||||
fileStreamChannel.close();
|
|
||||||
}
|
|
||||||
fileStreamChannel = null;
|
|
||||||
}
|
|
||||||
catch (IOException ex)
|
|
||||||
{
|
|
||||||
Logger.getLogger(AppLock.class.getName()).log(Level.WARNING,
|
|
||||||
"Can not get application lock for id=" + id() + "\n" + ex.getMessage(), ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Release lock and remove lock file.
|
|
||||||
*/
|
|
||||||
public synchronized void clear()
|
|
||||||
{
|
|
||||||
release();
|
|
||||||
if (fileToLock.exists())
|
|
||||||
{
|
|
||||||
fileToLock.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void finalize() throws Throwable
|
|
||||||
{
|
|
||||||
this.clear();
|
|
||||||
super.finalize();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
package net.runelite.client.util;
|
||||||
|
|
||||||
|
import io.reactivex.rxjava3.subjects.PublishSubject;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.runelite.client.RuneLite;
|
||||||
|
import net.runelite.client.config.OpenOSRSConfig;
|
||||||
|
import net.runelite.client.events.ConfigChanged;
|
||||||
|
import net.runelite.client.ui.RuneLiteSplashScreen;
|
||||||
|
import org.jgroups.Address;
|
||||||
|
import org.jgroups.JChannel;
|
||||||
|
import org.jgroups.Message;
|
||||||
|
import org.jgroups.ReceiverAdapter;
|
||||||
|
import org.jgroups.View;
|
||||||
|
import org.jgroups.util.Util;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Singleton
|
||||||
|
public class Groups extends ReceiverAdapter
|
||||||
|
{
|
||||||
|
private final OpenOSRSConfig openOSRSConfig;
|
||||||
|
private final JChannel channel;
|
||||||
|
|
||||||
|
@Getter(AccessLevel.PUBLIC)
|
||||||
|
private int instanceCount;
|
||||||
|
@Getter(AccessLevel.PUBLIC)
|
||||||
|
private Map<String, List<Address>> messageMap = new HashMap<>();
|
||||||
|
@Getter(AccessLevel.PUBLIC)
|
||||||
|
private final PublishSubject<Message> messageStringSubject = PublishSubject.create();
|
||||||
|
@Getter(AccessLevel.PUBLIC)
|
||||||
|
private final PublishSubject<Message> messageObjectSubject = PublishSubject.create();
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public Groups(OpenOSRSConfig openOSRSConfig) throws Exception
|
||||||
|
{
|
||||||
|
this.openOSRSConfig = openOSRSConfig;
|
||||||
|
|
||||||
|
this.channel = new JChannel(RuneLite.class.getResourceAsStream("/udp.xml"))
|
||||||
|
.setName(RuneLite.uuid)
|
||||||
|
.setReceiver(this)
|
||||||
|
.setDiscardOwnMessages(true)
|
||||||
|
.connect("openosrs");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void broadcastSring(String command)
|
||||||
|
{
|
||||||
|
send(null, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendConfig(Address destination, ConfigChanged configChanged)
|
||||||
|
{
|
||||||
|
if (!openOSRSConfig.localSync() || RuneLiteSplashScreen.showing() || instanceCount < 2)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
byte[] buffer = Util.objectToByteBuffer(configChanged);
|
||||||
|
Message message = new Message()
|
||||||
|
.setDest(destination)
|
||||||
|
.setBuffer(buffer);
|
||||||
|
|
||||||
|
channel.send(message);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendString(String command)
|
||||||
|
{
|
||||||
|
String[] messageObject = command.split(";");
|
||||||
|
String pluginId = messageObject[1];
|
||||||
|
|
||||||
|
messageMap.put(pluginId, new ArrayList<>());
|
||||||
|
|
||||||
|
for (Address member : channel.getView().getMembers())
|
||||||
|
{
|
||||||
|
if (member.toString().equals(RuneLite.uuid))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
messageMap.get(pluginId).add(member);
|
||||||
|
send(member, command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void send(Address destination, String command)
|
||||||
|
{
|
||||||
|
if (!openOSRSConfig.localSync() || RuneLiteSplashScreen.showing() || instanceCount < 2)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
channel.send(new Message(destination, command));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void viewAccepted(View view)
|
||||||
|
{
|
||||||
|
instanceCount = view.getMembers().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void receive(Message message)
|
||||||
|
{
|
||||||
|
if (RuneLiteSplashScreen.showing())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.getObject() instanceof String)
|
||||||
|
{
|
||||||
|
messageStringSubject.onNext(message);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
messageObjectSubject.onNext(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close()
|
||||||
|
{
|
||||||
|
channel.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -69,4 +69,5 @@
|
|||||||
</root>
|
</root>
|
||||||
|
|
||||||
<logger name="org.pf4j.AbstractPluginManager" level="OFF"/>
|
<logger name="org.pf4j.AbstractPluginManager" level="OFF"/>
|
||||||
|
<logger name="org.jgroups" level="ERROR"/>
|
||||||
</configuration>
|
</configuration>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 211 B |
54
runelite-client/src/main/resources/udp.xml
Normal file
54
runelite-client/src/main/resources/udp.xml
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<!--
|
||||||
|
Default stack using IP multicasting. It is similar to the "udp"
|
||||||
|
stack in stacks.xml, but doesn't use streaming state transfer and flushing
|
||||||
|
author: Bela Ban
|
||||||
|
-->
|
||||||
|
|
||||||
|
<config xmlns="urn:org:jgroups"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="urn:org:jgroups http://www.jgroups.org/schema/jgroups.xsd">
|
||||||
|
<UDP
|
||||||
|
mcast_port="${jgroups.udp.mcast_port:45588}"
|
||||||
|
ip_ttl="4"
|
||||||
|
tos="8"
|
||||||
|
ucast_recv_buf_size="5M"
|
||||||
|
ucast_send_buf_size="5M"
|
||||||
|
mcast_recv_buf_size="5M"
|
||||||
|
mcast_send_buf_size="5M"
|
||||||
|
max_bundle_size="64K"
|
||||||
|
enable_diagnostics="true"
|
||||||
|
thread_naming_pattern="cl"
|
||||||
|
|
||||||
|
thread_pool.min_threads="0"
|
||||||
|
thread_pool.max_threads="20"
|
||||||
|
thread_pool.keep_alive_time="30000"/>
|
||||||
|
|
||||||
|
<PING/>
|
||||||
|
<MERGE3 max_interval="30000"
|
||||||
|
min_interval="10000"/>
|
||||||
|
<FD_SOCK/>
|
||||||
|
<FD_ALL/>
|
||||||
|
<VERIFY_SUSPECT timeout="1500"/>
|
||||||
|
<BARRIER/>
|
||||||
|
<pbcast.NAKACK2 xmit_interval="500"
|
||||||
|
xmit_table_num_rows="100"
|
||||||
|
xmit_table_msgs_per_row="2000"
|
||||||
|
xmit_table_max_compaction_time="30000"
|
||||||
|
use_mcast_xmit="false"
|
||||||
|
discard_delivered_msgs="true"/>
|
||||||
|
<UNICAST3 xmit_interval="500"
|
||||||
|
xmit_table_num_rows="100"
|
||||||
|
xmit_table_msgs_per_row="2000"
|
||||||
|
xmit_table_max_compaction_time="60000"
|
||||||
|
conn_expiry_timeout="0"/>
|
||||||
|
<pbcast.STABLE desired_avg_gossip="50000"
|
||||||
|
max_bytes="4M"/>
|
||||||
|
<pbcast.GMS print_local_addr="false" join_timeout="2000"/>
|
||||||
|
<UFC max_credits="2M"
|
||||||
|
min_threshold="0.4"/>
|
||||||
|
<MFC max_credits="2M"
|
||||||
|
min_threshold="0.4"/>
|
||||||
|
<FRAG2 frag_size="60K"/>
|
||||||
|
<RSVP resend_interval="2000" timeout="10000"/>
|
||||||
|
<pbcast.STATE_TRANSFER/>
|
||||||
|
</config>
|
||||||
@@ -116,7 +116,7 @@ public class PluginManagerTest
|
|||||||
@Test
|
@Test
|
||||||
public void testLoadPlugins() throws Exception
|
public void testLoadPlugins() throws Exception
|
||||||
{
|
{
|
||||||
PluginManager pluginManager = new PluginManager(null, null, null, null);
|
PluginManager pluginManager = new PluginManager(null, null, null, null, null, null);
|
||||||
pluginManager.setOutdated(true);
|
pluginManager.setOutdated(true);
|
||||||
pluginManager.loadCorePlugins();
|
pluginManager.loadCorePlugins();
|
||||||
Collection<Plugin> plugins = pluginManager.getPlugins();
|
Collection<Plugin> plugins = pluginManager.getPlugins();
|
||||||
@@ -127,7 +127,7 @@ public class PluginManagerTest
|
|||||||
.count();
|
.count();
|
||||||
assertEquals(expected, plugins.size());
|
assertEquals(expected, plugins.size());
|
||||||
|
|
||||||
pluginManager = new PluginManager(null, null, null, null);
|
pluginManager = new PluginManager(null, null, null, null, null, null);
|
||||||
pluginManager.loadCorePlugins();
|
pluginManager.loadCorePlugins();
|
||||||
plugins = pluginManager.getPlugins();
|
plugins = pluginManager.getPlugins();
|
||||||
|
|
||||||
@@ -145,7 +145,7 @@ public class PluginManagerTest
|
|||||||
modules.add(new GraphvizModule());
|
modules.add(new GraphvizModule());
|
||||||
modules.add(new RuneLiteModule(() -> null, RuneLite.DEFAULT_CONFIG_FILE));
|
modules.add(new RuneLiteModule(() -> null, RuneLite.DEFAULT_CONFIG_FILE));
|
||||||
|
|
||||||
PluginManager pluginManager = new PluginManager(null, null, null, null);
|
PluginManager pluginManager = new PluginManager(null, null, null, null, null, null);
|
||||||
pluginManager.loadCorePlugins();
|
pluginManager.loadCorePlugins();
|
||||||
modules.addAll(pluginManager.getPlugins());
|
modules.addAll(pluginManager.getPlugins());
|
||||||
|
|
||||||
@@ -194,10 +194,10 @@ public class PluginManagerTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEventbusAnnotations() throws PluginInstantiationException
|
public void testEventbusAnnotations() throws Exception
|
||||||
{
|
{
|
||||||
EventBus eventbus = new EventBus();
|
EventBus eventbus = new EventBus();
|
||||||
PluginManager pluginManager = new PluginManager(eventbus, null, null, null)
|
PluginManager pluginManager = new PluginManager(eventbus, null, null, null, null, null)
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public boolean isPluginEnabled(Plugin plugin)
|
public boolean isPluginEnabled(Plugin plugin)
|
||||||
|
|||||||
Reference in New Issue
Block a user