rl-client: implement our pf4j plugins, extract pf4j out of net.runelite package. (#2883)
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
package com.openosrs.client;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.UUID;
|
||||
|
||||
public class OpenOSRS
|
||||
{
|
||||
public static final File OPENOSRS_DIR = new File(System.getProperty("user.home"), ".openosrs");
|
||||
public static final File EXTERNALPLUGIN_DIR = new File(OPENOSRS_DIR, "plugins");
|
||||
public static final String SYSTEM_VERSION = "0.0.1";
|
||||
|
||||
public static String uuid = UUID.randomUUID().toString();
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2019, Zeruth <TheRealNull@gmail.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
package com.openosrs.client.config;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
import net.runelite.client.config.Keybind;
|
||||
import net.runelite.client.config.Range;
|
||||
import net.runelite.client.config.Units;
|
||||
import com.openosrs.client.plugins.ExternalPluginManager;
|
||||
|
||||
@ConfigGroup("openosrs")
|
||||
public interface OpenOSRSConfig extends Config
|
||||
{
|
||||
@Getter(AccessLevel.PUBLIC)
|
||||
@AllArgsConstructor
|
||||
enum SortStyle
|
||||
{
|
||||
CATEGORY("Category"),
|
||||
ALPHABETICALLY("Alphabetically"),
|
||||
REPOSITORY("Repository");
|
||||
|
||||
private String name;
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return getName();
|
||||
}
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 3,
|
||||
keyName = "shareLogs",
|
||||
name = "Share anonymous error data",
|
||||
description = "Share anonymous error data with the OpenOSRS developers"
|
||||
)
|
||||
default boolean shareLogs()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "enableOpacity",
|
||||
name = "Enable opacity",
|
||||
description = "Enables opacity for the whole window.<br>NOTE: This only stays enabled if your pc supports this!",
|
||||
position = 18
|
||||
)
|
||||
default boolean enableOpacity()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Range(
|
||||
min = 15,
|
||||
max = 100
|
||||
)
|
||||
@ConfigItem(
|
||||
keyName = "opacityPercentage",
|
||||
name = "Opacity percentage",
|
||||
description = "Changes the opacity of the window if opacity is enabled",
|
||||
position = 19
|
||||
)
|
||||
@Units(Units.PERCENT)
|
||||
default int opacityPercentage()
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
|
||||
@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
|
||||
)
|
||||
default boolean localSync()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "keyboardPin",
|
||||
name = "Keyboard bank pin",
|
||||
description = "Enables you to type your bank pin",
|
||||
position = 22
|
||||
)
|
||||
default boolean keyboardPin()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "detachHotkey",
|
||||
name = "Detach Cam",
|
||||
description = "Detach Camera hotkey, press this and it will activate detatched camera.",
|
||||
position = 23
|
||||
)
|
||||
default Keybind detachHotkey()
|
||||
{
|
||||
return Keybind.NOT_SET;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "externalRepositories",
|
||||
name = "",
|
||||
description = "",
|
||||
hidden = true
|
||||
)
|
||||
default String getExternalRepositories()
|
||||
{
|
||||
return ExternalPluginManager.DEFAULT_PLUGIN_REPOS;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "externalRepositories",
|
||||
name = "",
|
||||
description = "",
|
||||
hidden = true
|
||||
)
|
||||
void setExternalRepositories(String val);
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "warning",
|
||||
name = "",
|
||||
description = "",
|
||||
hidden = true
|
||||
)
|
||||
default boolean warning()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, OpenOSRS <https://github.com/open-osrs>
|
||||
* Copyright (c) 2019 Owain van Brakel <https://github.com/Owain94>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -22,38 +22,16 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.openosrs.client.events;
|
||||
|
||||
package com.openosrs.client.plugins.neverlog;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.events.GameTick;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import lombok.Data;
|
||||
import net.runelite.api.events.Event;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "Never Logout",
|
||||
enabledByDefault = false,
|
||||
description = "Overrides the 5 minute AFK logout timer.",
|
||||
tags = {"openosrs", "never log", "idle", "logout", "log", "never"}
|
||||
)
|
||||
@SuppressWarnings("unchecked")
|
||||
public class NeverLogoutPlugin extends Plugin
|
||||
@Data
|
||||
public class ExternalPluginChanged implements Event
|
||||
{
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Subscribe
|
||||
private void onGameTick(GameTick gameTick)
|
||||
{
|
||||
if (client.getKeyboardIdleTicks() > 14900)
|
||||
{
|
||||
client.setKeyboardIdleTicks(0);
|
||||
}
|
||||
if (client.getMouseIdleTicks() > 14900)
|
||||
{
|
||||
client.setMouseIdleTicks(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
private final String pluginId;
|
||||
private final Plugin plugin;
|
||||
private final boolean added;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Owain van Brakel <https://github.com/Owain94>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.openosrs.client.events;
|
||||
|
||||
import lombok.Data;
|
||||
import net.runelite.api.events.Event;
|
||||
|
||||
@Data
|
||||
public class ExternalPluginsLoaded implements Event
|
||||
{}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Owain van Brakel <https://github.com/Owain94>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.openosrs.client.events;
|
||||
|
||||
import lombok.Data;
|
||||
import net.runelite.api.events.Event;
|
||||
|
||||
@Data
|
||||
public class ExternalRepositoryChanged implements Event
|
||||
{
|
||||
private final String owner;
|
||||
private final boolean added;
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
package com.openosrs.client;
|
||||
package com.openosrs.client.plugins;
|
||||
|
||||
import com.openosrs.client.plugins.neverlog.NeverLogoutPlugin;
|
||||
import com.openosrs.client.plugins.openosrs.OpenOSRSPlugin;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import net.runelite.client.externalplugins.ExternalPluginManager;
|
||||
import net.runelite.client.plugins.PluginInstantiationException;
|
||||
|
||||
public class PluginManager
|
||||
public class BuiltInPluginManager
|
||||
{
|
||||
public static List<Class<?>> oprsPlugins = new ArrayList<>();
|
||||
|
||||
@@ -24,6 +24,6 @@ public class PluginManager
|
||||
|
||||
static
|
||||
{
|
||||
oprsPlugins.add(NeverLogoutPlugin.class);
|
||||
oprsPlugins.add(OpenOSRSPlugin.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
package com.openosrs.client.plugins;
|
||||
|
||||
import com.openosrs.client.OpenOSRS;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.pf4j.BasePluginLoader;
|
||||
import org.pf4j.CompoundPluginLoader;
|
||||
import org.pf4j.CompoundPluginRepository;
|
||||
import org.pf4j.DefaultPluginManager;
|
||||
import org.pf4j.DependencyResolver;
|
||||
import org.pf4j.JarPluginLoader;
|
||||
import org.pf4j.JarPluginRepository;
|
||||
import org.pf4j.ManifestPluginDescriptorFinder;
|
||||
import org.pf4j.PluginAlreadyLoadedException;
|
||||
import org.pf4j.PluginDescriptorFinder;
|
||||
import org.pf4j.PluginLoader;
|
||||
import org.pf4j.PluginRepository;
|
||||
import org.pf4j.PluginRuntimeException;
|
||||
import org.pf4j.PluginState;
|
||||
import org.pf4j.PluginStateEvent;
|
||||
import org.pf4j.PluginWrapper;
|
||||
import org.pf4j.RuntimeMode;
|
||||
|
||||
@Slf4j
|
||||
class ExternalPf4jPluginManager extends DefaultPluginManager
|
||||
{
|
||||
private final ExternalPluginManager externalPluginManager;
|
||||
|
||||
public ExternalPf4jPluginManager(ExternalPluginManager externalPluginManager)
|
||||
{
|
||||
super(OpenOSRS.EXTERNALPLUGIN_DIR.toPath());
|
||||
this.externalPluginManager = externalPluginManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PluginDescriptorFinder createPluginDescriptorFinder()
|
||||
{
|
||||
return new ManifestPluginDescriptorFinder()
|
||||
{
|
||||
protected Path getManifestPath(Path pluginPath)
|
||||
{
|
||||
if (isDevelopment())
|
||||
{
|
||||
// The superclass performs a find, which is slow in development mode since we're pointing
|
||||
// at a sources directory, which can have a lot of files. The external plugin template
|
||||
// will always output the manifest at the following location, so we can hardcode this path.
|
||||
return pluginPath.resolve(ExternalPluginManager.DEVELOPMENT_MANIFEST_PATH);
|
||||
}
|
||||
|
||||
return super.getManifestPath(pluginPath);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PluginRepository createPluginRepository()
|
||||
{
|
||||
CompoundPluginRepository compoundPluginRepository = new CompoundPluginRepository();
|
||||
|
||||
JarPluginRepository jarPluginRepository = new JarPluginRepository(getPluginsRoot());
|
||||
compoundPluginRepository.add(jarPluginRepository);
|
||||
|
||||
return compoundPluginRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PluginLoader createPluginLoader()
|
||||
{
|
||||
return new CompoundPluginLoader()
|
||||
.add(new BasePluginLoader(this, new ExternalPluginClasspath()), this::isDevelopment)
|
||||
.add(new JarPluginLoader(this), this::isNotDevelopment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadPlugins()
|
||||
{
|
||||
for (Path path : pluginsRoots)
|
||||
{
|
||||
if (Files.notExists(path) || !Files.isDirectory(path))
|
||||
{
|
||||
log.warn("No '{}' root", path);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
List<Path> pluginPaths = pluginRepository.getPluginPaths();
|
||||
Collections.reverse(pluginPaths);
|
||||
|
||||
if (pluginPaths.isEmpty())
|
||||
{
|
||||
log.warn("No plugins");
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug("Found {} possible plugins: {}", pluginPaths.size(), pluginPaths);
|
||||
|
||||
Set<String> duplicatePlugins = new HashSet<>();
|
||||
for (Path pluginPath : pluginPaths)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!isPluginEligibleForLoading(pluginPath) && isNotDevelopment())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
loadPluginFromPath(pluginPath);
|
||||
}
|
||||
catch (PluginRuntimeException e)
|
||||
{
|
||||
if (!(e instanceof PluginAlreadyLoadedException))
|
||||
{
|
||||
log.error("Could not load plugin {}", pluginPath, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!duplicatePlugins.isEmpty())
|
||||
{
|
||||
log.error("Duplicate plugins detected: {}", String.join(", ", duplicatePlugins));
|
||||
|
||||
String formatted = String.join("\n", duplicatePlugins);
|
||||
|
||||
SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(null, "You have duplicate plugins in your externalmanager.\n" +
|
||||
"Having duplicate plugins will result in an unstable\n" +
|
||||
"experience, It is highly recommended to delete any\n" +
|
||||
"duplicates, here is a list of the plugins.\n\n" +
|
||||
formatted, "Duplicate Plugins Detected", JOptionPane.WARNING_MESSAGE));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
resolvePlugins();
|
||||
}
|
||||
catch (PluginRuntimeException e)
|
||||
{
|
||||
if (e instanceof DependencyResolver.DependenciesNotFoundException)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
|
||||
log.error("Could not resolve plugins", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resolvePlugins()
|
||||
{
|
||||
// retrieves the plugins descriptors
|
||||
List<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
|
||||
public RuntimeMode getRuntimeMode()
|
||||
{
|
||||
return RuntimeMode.DEPLOYMENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginState stopPlugin(String pluginId)
|
||||
{
|
||||
if (!plugins.containsKey(pluginId))
|
||||
{
|
||||
throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId));
|
||||
}
|
||||
|
||||
PluginWrapper pluginWrapper = getPlugin(pluginId);
|
||||
org.pf4j.PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor();
|
||||
PluginState pluginState = pluginWrapper.getPluginState();
|
||||
if (PluginState.STOPPED == pluginState)
|
||||
{
|
||||
log.debug("Already stopped plugin '{}'", getPluginLabel(pluginDescriptor));
|
||||
return PluginState.STOPPED;
|
||||
}
|
||||
|
||||
// test for disabled plugin
|
||||
if (PluginState.DISABLED == pluginState)
|
||||
{
|
||||
// do nothing
|
||||
return pluginState;
|
||||
}
|
||||
|
||||
pluginWrapper.getPlugin().stop();
|
||||
pluginWrapper.setPluginState(PluginState.STOPPED);
|
||||
startedPlugins.remove(pluginWrapper);
|
||||
|
||||
firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState));
|
||||
|
||||
return pluginWrapper.getPluginState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unloadPlugin(String pluginId)
|
||||
{
|
||||
try
|
||||
{
|
||||
PluginState pluginState = stopPlugin(pluginId);
|
||||
if (PluginState.STARTED == pluginState)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
PluginWrapper pluginWrapper = getPlugin(pluginId);
|
||||
|
||||
// remove the plugin
|
||||
plugins.remove(pluginId);
|
||||
getResolvedPlugins().remove(pluginWrapper);
|
||||
|
||||
firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState));
|
||||
|
||||
// remove the classloader
|
||||
Map<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);
|
||||
}
|
||||
|
||||
private boolean isPluginEligibleForLoading(Path path)
|
||||
{
|
||||
return path.toFile().getName().endsWith(".jar");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.openosrs.client.plugins;
|
||||
|
||||
import org.pf4j.DevelopmentPluginClasspath;
|
||||
|
||||
class ExternalPluginClasspath extends DevelopmentPluginClasspath
|
||||
{
|
||||
static final String GRADLE_DEPS_PATH = "build/deps";
|
||||
|
||||
ExternalPluginClasspath()
|
||||
{
|
||||
addJarsDirectories(GRADLE_DEPS_PATH);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.openosrs.client.plugins;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Determines whether a {@link File} is an external plugin folder. To be considered a plugin a folder must:
|
||||
* <p>
|
||||
* * Must not be a blacklisted name
|
||||
* * Have a {@code .gradle.kts} file in the root named after the folder
|
||||
* * Have a {@code MANIFEST.MF} located at {@code build/tmp/jar/MANIFEST.MF}
|
||||
*/
|
||||
public class ExternalPluginFileFilter implements FileFilter
|
||||
{
|
||||
private static final List<String> blacklist = Arrays.asList(
|
||||
".git",
|
||||
"build",
|
||||
"target",
|
||||
"release"
|
||||
);
|
||||
|
||||
private static final List<String> buildFiles = Arrays.asList(
|
||||
"%s.gradle.kts",
|
||||
"%s.gradle"
|
||||
);
|
||||
|
||||
@Override
|
||||
public boolean accept(File pathName)
|
||||
{
|
||||
// Check if this path looks like a plugin development directory
|
||||
if (!pathName.isDirectory())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
String dirName = pathName.getName();
|
||||
if (blacklist.contains(dirName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the plugin directory has a MANIFEST.MF which si required for loading
|
||||
if (!new File(pathName, ExternalPluginManager.DEVELOPMENT_MANIFEST_PATH).exists())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// By convention plugins their directory is $name and they have a $name.gradle.kts or $name.gradle file in their root
|
||||
for (String buildFile : buildFiles)
|
||||
{
|
||||
if (new File(pathName, String.format(buildFile, dirName)).exists())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,9 @@
|
||||
package com.openosrs.client.plugins;
|
||||
|
||||
import com.google.inject.Injector;
|
||||
import org.pf4j.ExtensionPoint;
|
||||
|
||||
public class Plugin extends net.runelite.client.plugins.Plugin implements ExtensionPoint
|
||||
{
|
||||
public Injector injector;
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2019, Zeruth <TheRealNull@gmail.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
package com.openosrs.client.plugins.openosrs;
|
||||
|
||||
import ch.qos.logback.classic.Logger;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.api.Client;
|
||||
import static net.runelite.api.ScriptID.BANK_PIN_OP;
|
||||
import net.runelite.api.events.ScriptCallbackEvent;
|
||||
import net.runelite.api.widgets.WidgetID;
|
||||
import static net.runelite.api.widgets.WidgetInfo.BANK_PIN_1;
|
||||
import static net.runelite.api.widgets.WidgetInfo.BANK_PIN_10;
|
||||
import static net.runelite.api.widgets.WidgetInfo.BANK_PIN_2;
|
||||
import static net.runelite.api.widgets.WidgetInfo.BANK_PIN_3;
|
||||
import static net.runelite.api.widgets.WidgetInfo.BANK_PIN_4;
|
||||
import static net.runelite.api.widgets.WidgetInfo.BANK_PIN_5;
|
||||
import static net.runelite.api.widgets.WidgetInfo.BANK_PIN_6;
|
||||
import static net.runelite.api.widgets.WidgetInfo.BANK_PIN_7;
|
||||
import static net.runelite.api.widgets.WidgetInfo.BANK_PIN_8;
|
||||
import static net.runelite.api.widgets.WidgetInfo.BANK_PIN_9;
|
||||
import static net.runelite.api.widgets.WidgetInfo.BANK_PIN_EXIT_BUTTON;
|
||||
import static net.runelite.api.widgets.WidgetInfo.BANK_PIN_FIRST_ENTERED;
|
||||
import static net.runelite.api.widgets.WidgetInfo.BANK_PIN_FORGOT_BUTTON;
|
||||
import static net.runelite.api.widgets.WidgetInfo.BANK_PIN_FOURTH_ENTERED;
|
||||
import static net.runelite.api.widgets.WidgetInfo.BANK_PIN_INSTRUCTION_TEXT;
|
||||
import static net.runelite.api.widgets.WidgetInfo.BANK_PIN_SECOND_ENTERED;
|
||||
import static net.runelite.api.widgets.WidgetInfo.BANK_PIN_THIRD_ENTERED;
|
||||
import net.runelite.client.callback.ClientThread;
|
||||
import net.runelite.client.config.Keybind;
|
||||
import com.openosrs.client.config.OpenOSRSConfig;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.events.ConfigChanged;
|
||||
import net.runelite.client.input.KeyListener;
|
||||
import net.runelite.client.input.KeyManager;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import com.openosrs.client.plugins.openosrs.externals.ExternalPluginManagerPanel;
|
||||
import net.runelite.client.ui.ClientToolbar;
|
||||
import net.runelite.client.ui.NavigationButton;
|
||||
import net.runelite.client.util.HotkeyListener;
|
||||
import net.runelite.client.util.ImageUtil;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@PluginDescriptor(
|
||||
loadWhenOutdated = true, // prevent users from disabling
|
||||
hidden = true, // prevent users from disabling
|
||||
name = "OpenOSRS"
|
||||
)
|
||||
@Singleton
|
||||
@Slf4j
|
||||
public class OpenOSRSPlugin extends Plugin
|
||||
{
|
||||
private final openosrsKeyListener keyListener = new openosrsKeyListener();
|
||||
|
||||
@Inject
|
||||
private OpenOSRSConfig config;
|
||||
|
||||
@Inject
|
||||
private KeyManager keyManager;
|
||||
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Inject
|
||||
private ClientThread clientThread;
|
||||
|
||||
@Inject
|
||||
private ClientToolbar clientToolbar;
|
||||
|
||||
private NavigationButton navButton;
|
||||
|
||||
private final HotkeyListener hotkeyListener = new HotkeyListener(() -> this.keybind)
|
||||
{
|
||||
@Override
|
||||
public void hotkeyPressed()
|
||||
{
|
||||
detach = !detach;
|
||||
client.setOculusOrbState(detach ? 1 : 0);
|
||||
client.setOculusOrbNormalSpeed(detach ? 36 : 12);
|
||||
}
|
||||
};
|
||||
private int entered = -1;
|
||||
private int enterIdx;
|
||||
private boolean expectInput;
|
||||
private boolean detach;
|
||||
private Keybind keybind;
|
||||
|
||||
@Override
|
||||
protected void startUp()
|
||||
{
|
||||
ExternalPluginManagerPanel panel = injector.getInstance(ExternalPluginManagerPanel.class);
|
||||
|
||||
final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "externalmanager_icon.png");
|
||||
|
||||
navButton = NavigationButton.builder()
|
||||
.tooltip("External Plugin Manager")
|
||||
.icon(icon)
|
||||
.priority(1)
|
||||
.panel(panel)
|
||||
.build();
|
||||
clientToolbar.addNavigation(navButton);
|
||||
|
||||
entered = -1;
|
||||
enterIdx = 0;
|
||||
expectInput = false;
|
||||
this.keybind = config.detachHotkey();
|
||||
keyManager.registerKeyListener(hotkeyListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutDown()
|
||||
{
|
||||
clientToolbar.removeNavigation(navButton);
|
||||
|
||||
entered = 0;
|
||||
enterIdx = 0;
|
||||
expectInput = false;
|
||||
keyManager.unregisterKeyListener(keyListener);
|
||||
keyManager.unregisterKeyListener(hotkeyListener);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
private void onConfigChanged(ConfigChanged event)
|
||||
{
|
||||
if (!event.getGroup().equals("openosrs"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.keybind = config.detachHotkey();
|
||||
|
||||
if (!config.keyboardPin())
|
||||
{
|
||||
entered = 0;
|
||||
enterIdx = 0;
|
||||
expectInput = false;
|
||||
keyManager.unregisterKeyListener(keyListener);
|
||||
}
|
||||
|
||||
if (event.getKey().equals("shareLogs") && !config.shareLogs())
|
||||
{
|
||||
final Logger logger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
|
||||
logger.detachAppender("Sentry");
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
private void onScriptCallbackEvent(ScriptCallbackEvent e)
|
||||
{
|
||||
if (!config.keyboardPin())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.getEventName().equals("bankpin"))
|
||||
{
|
||||
int[] intStack = client.getIntStack();
|
||||
int intStackSize = client.getIntStackSize();
|
||||
|
||||
// This'll be anywhere from -1 to 3
|
||||
// 0 = first number, 1 second, etc
|
||||
// Anything other than 0123 means the bankpin interface closes
|
||||
int enterIdx = intStack[intStackSize - 1];
|
||||
|
||||
if (enterIdx < 0 || enterIdx > 3)
|
||||
{
|
||||
keyManager.unregisterKeyListener(keyListener);
|
||||
this.enterIdx = 0;
|
||||
this.entered = 0;
|
||||
expectInput = false;
|
||||
return;
|
||||
}
|
||||
else if (enterIdx == 0)
|
||||
{
|
||||
keyManager.registerKeyListener(keyListener);
|
||||
}
|
||||
|
||||
this.enterIdx = enterIdx;
|
||||
expectInput = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleKey(char c)
|
||||
{
|
||||
if (client.getWidget(WidgetID.BANK_PIN_GROUP_ID, BANK_PIN_INSTRUCTION_TEXT.getChildId()) == null
|
||||
|| !client.getWidget(BANK_PIN_INSTRUCTION_TEXT).getText().equals("First click the FIRST digit.")
|
||||
&& !client.getWidget(BANK_PIN_INSTRUCTION_TEXT).getText().equals("Now click the SECOND digit.")
|
||||
&& !client.getWidget(BANK_PIN_INSTRUCTION_TEXT).getText().equals("Time for the THIRD digit.")
|
||||
&& !client.getWidget(BANK_PIN_INSTRUCTION_TEXT).getText().equals("Finally, the FOURTH digit."))
|
||||
|
||||
{
|
||||
entered = 0;
|
||||
enterIdx = 0;
|
||||
expectInput = false;
|
||||
keyManager.unregisterKeyListener(keyListener);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!expectInput)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int num = Character.getNumericValue(c);
|
||||
|
||||
// We gotta copy this cause enteridx changes while the script is executing
|
||||
int oldEnterIdx = enterIdx;
|
||||
|
||||
// Script 685 will call 653, which in turn will set expectInput to true
|
||||
expectInput = false;
|
||||
client.runScript(BANK_PIN_OP, num, enterIdx, entered, BANK_PIN_EXIT_BUTTON.getId(), BANK_PIN_FORGOT_BUTTON.getId(), BANK_PIN_1.getId(), BANK_PIN_2.getId(), BANK_PIN_3.getId(), BANK_PIN_4.getId(), BANK_PIN_5.getId(), BANK_PIN_6.getId(), BANK_PIN_7.getId(), BANK_PIN_8.getId(), BANK_PIN_9.getId(), BANK_PIN_10.getId(), BANK_PIN_FIRST_ENTERED.getId(), BANK_PIN_SECOND_ENTERED.getId(), BANK_PIN_THIRD_ENTERED.getId(), BANK_PIN_FOURTH_ENTERED.getId(), BANK_PIN_INSTRUCTION_TEXT.getId());
|
||||
|
||||
if (oldEnterIdx == 0)
|
||||
{
|
||||
entered = num * 1000;
|
||||
}
|
||||
else if (oldEnterIdx == 1)
|
||||
{
|
||||
entered += num * 100;
|
||||
}
|
||||
else if (oldEnterIdx == 2)
|
||||
{
|
||||
entered += num * 10;
|
||||
}
|
||||
}
|
||||
|
||||
private class openosrsKeyListener implements KeyListener
|
||||
{
|
||||
private int lastKeyCycle;
|
||||
|
||||
@Override
|
||||
public void keyTyped(KeyEvent keyEvent)
|
||||
{
|
||||
if (!Character.isDigit(keyEvent.getKeyChar()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (client.getGameCycle() - lastKeyCycle <= 5)
|
||||
{
|
||||
keyEvent.consume();
|
||||
return;
|
||||
}
|
||||
|
||||
lastKeyCycle = client.getGameCycle();
|
||||
|
||||
clientThread.invoke(() -> handleKey(keyEvent.getKeyChar()));
|
||||
keyEvent.consume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(KeyEvent keyEvent)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyReleased(KeyEvent keyEvent)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
97
runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/ExternalBox.java
vendored
Normal file
97
runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/ExternalBox.java
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
package com.openosrs.client.plugins.openosrs.externals;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.net.URL;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.border.CompoundBorder;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.text.DefaultCaret;
|
||||
import net.runelite.client.ui.ColorScheme;
|
||||
import net.runelite.client.ui.FontManager;
|
||||
import org.pf4j.update.PluginInfo;
|
||||
|
||||
public class ExternalBox extends JPanel
|
||||
{
|
||||
private static final Font normalFont = FontManager.getRunescapeFont();
|
||||
private static final Font smallFont = FontManager.getRunescapeSmallFont();
|
||||
|
||||
PluginInfo pluginInfo;
|
||||
JLabel install = new JLabel();
|
||||
JMultilineLabel description = new JMultilineLabel();
|
||||
|
||||
ExternalBox(String name, URL url)
|
||||
{
|
||||
this(name, url.toString().replace("https://raw.githubusercontent.com/", "").replace("/master/", ""));
|
||||
}
|
||||
|
||||
ExternalBox(PluginInfo pluginInfo)
|
||||
{
|
||||
this(pluginInfo.name, pluginInfo.description);
|
||||
}
|
||||
|
||||
ExternalBox(String name, String desc)
|
||||
{
|
||||
setLayout(new BorderLayout());
|
||||
setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
|
||||
JPanel titleWrapper = new JPanel(new BorderLayout());
|
||||
titleWrapper.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
titleWrapper.setBorder(new CompoundBorder(
|
||||
BorderFactory.createMatteBorder(0, 0, 1, 0, ColorScheme.DARK_GRAY_COLOR),
|
||||
BorderFactory.createLineBorder(ColorScheme.DARKER_GRAY_COLOR)
|
||||
));
|
||||
|
||||
JLabel title = new JLabel();
|
||||
title.setText(name);
|
||||
title.setFont(normalFont);
|
||||
title.setBorder(null);
|
||||
title.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
title.setPreferredSize(new Dimension(0, 24));
|
||||
title.setForeground(Color.WHITE);
|
||||
title.setBorder(new EmptyBorder(0, 8, 0, 0));
|
||||
|
||||
JPanel titleActions = new JPanel(new BorderLayout(3, 0));
|
||||
titleActions.setBorder(new EmptyBorder(0, 0, 0, 8));
|
||||
titleActions.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
|
||||
titleActions.add(install, BorderLayout.EAST);
|
||||
|
||||
titleWrapper.add(title, BorderLayout.CENTER);
|
||||
titleWrapper.add(titleActions, BorderLayout.EAST);
|
||||
|
||||
description.setText(desc);
|
||||
description.setFont(smallFont);
|
||||
description.setDisabledTextColor(Color.WHITE);
|
||||
description.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
|
||||
add(titleWrapper, BorderLayout.NORTH);
|
||||
add(description, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
public static class JMultilineLabel extends JTextArea
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public JMultilineLabel()
|
||||
{
|
||||
super();
|
||||
setEditable(false);
|
||||
setCursor(null);
|
||||
setOpaque(false);
|
||||
setFocusable(false);
|
||||
setWrapStyleWord(true);
|
||||
setLineWrap(true);
|
||||
setBorder(new EmptyBorder(0, 8, 0, 8));
|
||||
setAlignmentY(JLabel.CENTER_ALIGNMENT);
|
||||
|
||||
DefaultCaret caret = (DefaultCaret) getCaret();
|
||||
caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
package com.openosrs.client.plugins.openosrs.externals;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import javax.inject.Inject;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTabbedPane;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import net.runelite.client.eventbus.EventBus;
|
||||
import com.openosrs.client.plugins.ExternalPluginManager;
|
||||
import net.runelite.client.ui.ClientUI;
|
||||
import net.runelite.client.ui.ColorScheme;
|
||||
import net.runelite.client.ui.PluginPanel;
|
||||
import net.runelite.client.util.ImageUtil;
|
||||
|
||||
public class ExternalPluginManagerPanel extends PluginPanel
|
||||
{
|
||||
private static final ImageIcon ADD_ICON_RAW;
|
||||
private static final ImageIcon ADD_HOVER_ICON_RAW;
|
||||
private static final ImageIcon ADD_ICON_GH;
|
||||
private static final ImageIcon ADD_HOVER_ICON_GH;
|
||||
|
||||
static
|
||||
{
|
||||
final BufferedImage addIconRaw =
|
||||
ImageUtil.getResourceStreamFromClass(ExternalPluginManagerPanel.class, "add_raw_icon.png");
|
||||
final BufferedImage addIconGh = ImageUtil
|
||||
.resizeImage(ImageUtil.getResourceStreamFromClass(ExternalPluginManagerPanel.class, "gh_icon.png"), 14, 14);
|
||||
ADD_ICON_RAW = new ImageIcon(addIconRaw);
|
||||
ADD_HOVER_ICON_RAW = new ImageIcon(ImageUtil.alphaOffset(addIconRaw, 0.53f));
|
||||
ADD_ICON_GH = new ImageIcon(addIconGh);
|
||||
ADD_HOVER_ICON_GH = new ImageIcon(ImageUtil.alphaOffset(addIconGh, 0.53f));
|
||||
}
|
||||
|
||||
private final ExternalPluginManager externalPluginManager;
|
||||
private final ScheduledExecutorService executor;
|
||||
private final EventBus eventBus;
|
||||
|
||||
@Inject
|
||||
private ExternalPluginManagerPanel(ExternalPluginManager externalPluginManager, ScheduledExecutorService executor, EventBus eventBus)
|
||||
{
|
||||
super(false);
|
||||
|
||||
this.externalPluginManager = externalPluginManager;
|
||||
this.executor = executor;
|
||||
this.eventBus = eventBus;
|
||||
|
||||
buildPanel();
|
||||
}
|
||||
|
||||
private void buildPanel()
|
||||
{
|
||||
removeAll();
|
||||
|
||||
setLayout(new BorderLayout(0, 10));
|
||||
setBackground(ColorScheme.DARK_GRAY_COLOR);
|
||||
|
||||
add(titleBar(), BorderLayout.NORTH);
|
||||
add(tabbedPane(), BorderLayout.CENTER);
|
||||
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
|
||||
private JPanel titleBar()
|
||||
{
|
||||
JPanel titlePanel = new JPanel(new BorderLayout());
|
||||
titlePanel.setBorder(new EmptyBorder(10, 10, 10, 10));
|
||||
|
||||
JLabel title = new JLabel();
|
||||
JLabel addGHRepo = new JLabel(ADD_ICON_GH);
|
||||
JLabel addRawRepo = new JLabel(ADD_ICON_RAW);
|
||||
|
||||
JPanel buttonHolder = new JPanel(new BorderLayout());
|
||||
buttonHolder.setBorder(new EmptyBorder(0, 0, 0, 0));
|
||||
|
||||
title.setText("External Plugin Manager");
|
||||
title.setForeground(Color.WHITE);
|
||||
|
||||
addGHRepo.setToolTipText("Add new GitHub repository");
|
||||
addGHRepo.addMouseListener(new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mousePressed(MouseEvent mouseEvent)
|
||||
{
|
||||
if (externalPluginManager.getWarning())
|
||||
{
|
||||
JCheckBox checkbox = new JCheckBox("Don't show again.");
|
||||
int answer = showWarningDialog(checkbox);
|
||||
|
||||
if (answer == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (checkbox.isSelected())
|
||||
{
|
||||
externalPluginManager.setWarning(false);
|
||||
}
|
||||
}
|
||||
|
||||
JTextField owner = new JTextField();
|
||||
JTextField name = new JTextField();
|
||||
Object[] message = {
|
||||
"Github Repository owner:", owner,
|
||||
"Github Repository name:", name
|
||||
};
|
||||
|
||||
int option =
|
||||
JOptionPane.showConfirmDialog(ClientUI.getFrame(), message, "Add repository", JOptionPane.OK_CANCEL_OPTION);
|
||||
if (option != JOptionPane.OK_OPTION || owner.getText().equals("") || name.getText().equals(""))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (externalPluginManager.doesGhRepoExist(owner.getText(), name.getText()))
|
||||
{
|
||||
JOptionPane.showMessageDialog(ClientUI.getFrame(), "This repository already exists.", "Error!",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ExternalPluginManager.testGHRepository(owner.getText(), name.getText()))
|
||||
{
|
||||
JOptionPane.showMessageDialog(ClientUI.getFrame(), "This doesn't appear to be a valid repository.", "Error!",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
externalPluginManager.addGHRepository(owner.getText(), name.getText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent mouseEvent)
|
||||
{
|
||||
addGHRepo.setIcon(ADD_HOVER_ICON_GH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent mouseEvent)
|
||||
{
|
||||
addGHRepo.setIcon(ADD_ICON_GH);
|
||||
}
|
||||
});
|
||||
addGHRepo.setBorder(new EmptyBorder(0, 3, 0, 0));
|
||||
|
||||
addRawRepo.setToolTipText("Add new raw repository");
|
||||
addRawRepo.addMouseListener(new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mousePressed(MouseEvent mouseEvent)
|
||||
{
|
||||
if (externalPluginManager.getWarning())
|
||||
{
|
||||
JCheckBox checkbox = new JCheckBox("Don't show again.");
|
||||
int answer = showWarningDialog(checkbox);
|
||||
|
||||
if (answer == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (checkbox.isSelected())
|
||||
{
|
||||
externalPluginManager.setWarning(false);
|
||||
}
|
||||
}
|
||||
|
||||
JTextField id = new JTextField();
|
||||
JTextField url = new JTextField();
|
||||
Object[] message = {
|
||||
"Repository ID:", id,
|
||||
"Repository URL:", url
|
||||
};
|
||||
|
||||
int option =
|
||||
JOptionPane.showConfirmDialog(ClientUI.getFrame(), message, "Add repository", JOptionPane.OK_CANCEL_OPTION);
|
||||
if (option != JOptionPane.OK_OPTION || id.getText().equals("") || url.getText().equals(""))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (id.getText().startsWith("gh:") || id.getText().contains("|"))
|
||||
{
|
||||
JOptionPane.showMessageDialog(ClientUI.getFrame(),
|
||||
"Repository id cannot begin with \"gh:\"\nor contain the pipe character '|'.", "Error!",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (externalPluginManager.doesRepoExist(id.getText()))
|
||||
{
|
||||
JOptionPane.showMessageDialog(ClientUI.getFrame(),
|
||||
String.format("The repository with id %s already exists.", id.getText()), "Error!",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
URL urlActual;
|
||||
try
|
||||
{
|
||||
urlActual = new URL(url.getText());
|
||||
}
|
||||
catch (MalformedURLException e)
|
||||
{
|
||||
JOptionPane.showMessageDialog(ClientUI.getFrame(), "This doesn't appear to be a valid repository.", "Error!",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ExternalPluginManager.testRepository(urlActual))
|
||||
{
|
||||
JOptionPane.showMessageDialog(ClientUI.getFrame(), "This doesn't appear to be a valid repository.", "Error!",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
externalPluginManager.addRepository(id.getText(), urlActual);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent mouseEvent)
|
||||
{
|
||||
addRawRepo.setIcon(ADD_HOVER_ICON_RAW);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent mouseEvent)
|
||||
{
|
||||
addRawRepo.setIcon(ADD_ICON_RAW);
|
||||
}
|
||||
});
|
||||
addRawRepo.setBorder(new EmptyBorder(0, 0, 0, 3));
|
||||
|
||||
titlePanel.add(title, BorderLayout.WEST);
|
||||
buttonHolder.add(addRawRepo, BorderLayout.WEST);
|
||||
buttonHolder.add(addGHRepo, BorderLayout.EAST);
|
||||
titlePanel.add(buttonHolder, BorderLayout.EAST);
|
||||
|
||||
return titlePanel;
|
||||
}
|
||||
|
||||
private JTabbedPane tabbedPane()
|
||||
{
|
||||
JTabbedPane mainTabPane = new JTabbedPane();
|
||||
|
||||
PluginsPanel pluginPanel = new PluginsPanel(this.externalPluginManager, this.executor, this.eventBus);
|
||||
JScrollPane repositoryPanel = wrapContainer(new RepositoryPanel(this.externalPluginManager, this.eventBus));
|
||||
|
||||
mainTabPane.add("Plugins", pluginPanel);
|
||||
mainTabPane.add("Repositories", repositoryPanel);
|
||||
|
||||
return mainTabPane;
|
||||
}
|
||||
|
||||
private int showWarningDialog(JCheckBox checkbox)
|
||||
{
|
||||
Object[] options = {"Okay, I accept the risk", "Never mind, turn back", checkbox};
|
||||
return JOptionPane.showOptionDialog(new JFrame(),
|
||||
"Adding plugins from unverified sources may put your account, or personal information at risk! \n",
|
||||
"Account security warning",
|
||||
JOptionPane.YES_NO_OPTION,
|
||||
JOptionPane.WARNING_MESSAGE,
|
||||
null,
|
||||
options,
|
||||
options[0]);
|
||||
}
|
||||
|
||||
static JScrollPane wrapContainer(final JPanel container)
|
||||
{
|
||||
final JPanel wrapped = new JPanel(new BorderLayout());
|
||||
wrapped.add(container, BorderLayout.NORTH);
|
||||
|
||||
final JScrollPane scroller = new JScrollPane(wrapped);
|
||||
scroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
scroller.getVerticalScrollBar().setPreferredSize(new Dimension(8, 0));
|
||||
|
||||
return scroller;
|
||||
}
|
||||
}
|
||||
592
runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/PluginsPanel.java
vendored
Normal file
592
runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/PluginsPanel.java
vendored
Normal file
@@ -0,0 +1,592 @@
|
||||
package com.openosrs.client.plugins.openosrs.externals;
|
||||
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.Insets;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTabbedPane;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.SwingWorker;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import static net.runelite.api.util.Text.DISTANCE;
|
||||
import net.runelite.client.eventbus.EventBus;
|
||||
import com.openosrs.client.events.ExternalPluginChanged;
|
||||
import com.openosrs.client.events.ExternalRepositoryChanged;
|
||||
import com.openosrs.client.plugins.ExternalPluginManager;
|
||||
import static com.openosrs.client.plugins.openosrs.externals.ExternalPluginManagerPanel.wrapContainer;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.ui.ClientUI;
|
||||
import net.runelite.client.ui.ColorScheme;
|
||||
import net.runelite.client.ui.FontManager;
|
||||
import net.runelite.client.ui.PluginPanel;
|
||||
import net.runelite.client.ui.components.IconTextField;
|
||||
import net.runelite.client.ui.components.shadowlabel.JShadowedLabel;
|
||||
import com.openosrs.client.util.DeferredDocumentChangedListener;
|
||||
import com.openosrs.client.util.ImageUtil;
|
||||
import com.openosrs.client.util.SwingUtil;
|
||||
import org.pf4j.update.PluginInfo;
|
||||
import org.pf4j.update.UpdateManager;
|
||||
import org.pf4j.update.UpdateRepository;
|
||||
import org.pf4j.update.VerifyException;
|
||||
|
||||
@Slf4j
|
||||
public class PluginsPanel extends JPanel
|
||||
{
|
||||
private static final ImageIcon ADD_ICON;
|
||||
private static final ImageIcon ADD_HOVER_ICON;
|
||||
private static final ImageIcon DELETE_ICON;
|
||||
private static final ImageIcon DELETE_HOVER_ICON;
|
||||
private static final ImageIcon DELETE_ICON_GRAY;
|
||||
private static final ImageIcon DELETE_HOVER_ICON_GRAY;
|
||||
|
||||
static
|
||||
{
|
||||
final BufferedImage addIcon =
|
||||
ImageUtil.recolorImage(
|
||||
ImageUtil.getResourceStreamFromClass(PluginsPanel.class, "add_icon.png"), ColorScheme.BRAND_BLUE
|
||||
);
|
||||
ADD_ICON = new ImageIcon(addIcon);
|
||||
ADD_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(addIcon, 0.53f));
|
||||
|
||||
final BufferedImage deleteImg =
|
||||
ImageUtil.recolorImage(
|
||||
ImageUtil.resizeCanvas(
|
||||
ImageUtil.getResourceStreamFromClass(PluginsPanel.class, "delete_icon.png"), 14, 14
|
||||
), ColorScheme.BRAND_BLUE
|
||||
);
|
||||
DELETE_ICON = new ImageIcon(deleteImg);
|
||||
DELETE_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(deleteImg, 0.53f));
|
||||
|
||||
DELETE_ICON_GRAY = new ImageIcon(ImageUtil.grayscaleImage(deleteImg));
|
||||
DELETE_HOVER_ICON_GRAY = new ImageIcon(ImageUtil.alphaOffset(ImageUtil.grayscaleImage(deleteImg), 0.53f));
|
||||
}
|
||||
|
||||
private final ExternalPluginManager externalPluginManager;
|
||||
private final UpdateManager updateManager;
|
||||
private final ScheduledExecutorService executor;
|
||||
private final EventBus eventBus;
|
||||
|
||||
private final IconTextField searchBar = new IconTextField();
|
||||
private final JPanel filterwrapper = new JPanel(new BorderLayout(0, 10));
|
||||
private final List<PluginInfo> installedPluginsList = new ArrayList<>();
|
||||
private final List<PluginInfo> availablePluginsList = new ArrayList<>();
|
||||
private final JPanel installedPluginsPanel = new JPanel(new GridBagLayout());
|
||||
private final JPanel availablePluginsPanel = new JPanel(new GridBagLayout());
|
||||
|
||||
private JComboBox<String> filterComboBox;
|
||||
private Set<String> deps;
|
||||
|
||||
PluginsPanel(ExternalPluginManager externalPluginManager, ScheduledExecutorService executor, EventBus eventBus)
|
||||
{
|
||||
this.externalPluginManager = externalPluginManager;
|
||||
this.updateManager = externalPluginManager.getUpdateManager();
|
||||
this.executor = executor;
|
||||
this.eventBus = eventBus;
|
||||
|
||||
setLayout(new BorderLayout(0, 10));
|
||||
setBackground(ColorScheme.DARK_GRAY_COLOR);
|
||||
|
||||
buildFilter();
|
||||
|
||||
JTabbedPane mainTabPane = new JTabbedPane();
|
||||
|
||||
mainTabPane.add("Installed", wrapContainer(installedPluginsPanel()));
|
||||
mainTabPane.add("Available", wrapContainer(availablePluginsPanel()));
|
||||
|
||||
add(filterwrapper, BorderLayout.NORTH);
|
||||
add(mainTabPane, BorderLayout.CENTER);
|
||||
|
||||
eventBus.register(this);
|
||||
|
||||
reloadPlugins();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onExternalRepositoryChanged(ExternalRepositoryChanged event)
|
||||
{
|
||||
buildFilter();
|
||||
reloadPlugins();
|
||||
repaint();
|
||||
}
|
||||
|
||||
private void buildFilter()
|
||||
{
|
||||
filterwrapper.removeAll();
|
||||
|
||||
DeferredDocumentChangedListener listener = new DeferredDocumentChangedListener();
|
||||
listener.addChangeListener(e ->
|
||||
{
|
||||
installedPlugins();
|
||||
availablePlugins();
|
||||
});
|
||||
|
||||
filterwrapper.setBorder(new EmptyBorder(10, 10, 0, 10));
|
||||
|
||||
List<String> repositories = getRepositories();
|
||||
filterComboBox = new JComboBox<>(repositories.toArray(new String[0]));
|
||||
filterComboBox.setPreferredSize(new Dimension(PluginPanel.PANEL_WIDTH - 20, 30));
|
||||
filterComboBox.addActionListener(e -> {
|
||||
installedPlugins();
|
||||
availablePlugins();
|
||||
});
|
||||
|
||||
if (repositories.size() > 2)
|
||||
{
|
||||
filterwrapper.add(filterComboBox, BorderLayout.NORTH);
|
||||
}
|
||||
|
||||
searchBar.setIcon(IconTextField.Icon.SEARCH);
|
||||
searchBar.setPreferredSize(new Dimension(PluginPanel.PANEL_WIDTH - 20, 30));
|
||||
searchBar.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
searchBar.setHoverBackgroundColor(ColorScheme.DARK_GRAY_HOVER_COLOR);
|
||||
searchBar.getDocument().addDocumentListener(listener);
|
||||
|
||||
filterwrapper.add(searchBar, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
private List<String> getRepositories()
|
||||
{
|
||||
List<String> repositories = new ArrayList<>();
|
||||
repositories.add("All");
|
||||
for (UpdateRepository updateRepository : this.updateManager.getRepositories())
|
||||
{
|
||||
repositories.add(updateRepository.getUrl().toString().replace("https://raw.githubusercontent.com/", "").replace("/master/", ""));
|
||||
}
|
||||
|
||||
return repositories;
|
||||
}
|
||||
|
||||
private JLabel titleLabel(String text)
|
||||
{
|
||||
JLabel title = new JShadowedLabel();
|
||||
|
||||
title.setFont(FontManager.getRunescapeSmallFont());
|
||||
title.setForeground(Color.WHITE);
|
||||
title.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
title.setText("<html><body style = 'text-align:center'>" + text + "</body></html>");
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
private JPanel installedPluginsPanel()
|
||||
{
|
||||
JPanel installedPluginsContainer = new JPanel();
|
||||
installedPluginsContainer.setLayout(new BorderLayout(0, 5));
|
||||
installedPluginsContainer.setBorder(new EmptyBorder(0, 10, 10, 10));
|
||||
installedPluginsContainer.add(installedPluginsPanel, BorderLayout.CENTER);
|
||||
|
||||
return installedPluginsContainer;
|
||||
}
|
||||
|
||||
private JPanel availablePluginsPanel()
|
||||
{
|
||||
JPanel availablePluginsContainer = new JPanel();
|
||||
availablePluginsContainer.setLayout(new BorderLayout(0, 5));
|
||||
availablePluginsContainer.setBorder(new EmptyBorder(0, 10, 10, 10));
|
||||
availablePluginsContainer.add(availablePluginsPanel, BorderLayout.CENTER);
|
||||
|
||||
return availablePluginsContainer;
|
||||
}
|
||||
|
||||
static boolean mismatchesSearchTerms(String search, PluginInfo pluginInfo)
|
||||
{
|
||||
final String[] searchTerms = search.toLowerCase().split(" ");
|
||||
final String[] pluginTerms = (pluginInfo.name + " " + pluginInfo.description).toLowerCase().split("[/\\s]");
|
||||
for (String term : searchTerms)
|
||||
{
|
||||
if (Arrays.stream(pluginTerms).noneMatch((t) -> t.contains(term) ||
|
||||
DISTANCE.apply(t, term) > 0.9))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void reloadPlugins()
|
||||
{
|
||||
fetchPlugins();
|
||||
|
||||
try
|
||||
{
|
||||
SwingUtil.syncExec(() -> {
|
||||
this.installedPlugins();
|
||||
this.availablePlugins();
|
||||
});
|
||||
|
||||
}
|
||||
catch (InvocationTargetException | InterruptedException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void fetchPlugins()
|
||||
{
|
||||
List<PluginInfo> availablePlugins = null;
|
||||
List<PluginInfo> plugins = null;
|
||||
List<String> disabledPlugins = externalPluginManager.getDisabledPlugins();
|
||||
|
||||
try
|
||||
{
|
||||
availablePlugins = updateManager.getAvailablePlugins();
|
||||
plugins = updateManager.getPlugins();
|
||||
}
|
||||
catch (JsonSyntaxException ex)
|
||||
{
|
||||
log.error(String.valueOf(ex));
|
||||
}
|
||||
|
||||
if (availablePlugins == null || plugins == null)
|
||||
{
|
||||
JOptionPane.showMessageDialog(ClientUI.getFrame(), "The external plugin list could not be loaded.", "Error", JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
availablePluginsList.clear();
|
||||
installedPluginsList.clear();
|
||||
|
||||
deps = externalPluginManager.getDependencies();
|
||||
|
||||
for (PluginInfo pluginInfo : plugins)
|
||||
{
|
||||
if (availablePlugins.contains(pluginInfo) || disabledPlugins.contains(pluginInfo.id))
|
||||
{
|
||||
availablePluginsList.add(pluginInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
installedPluginsList.add(pluginInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
private void onExternalPluginChanged(ExternalPluginChanged externalPluginChanged)
|
||||
{
|
||||
String pluginId = externalPluginChanged.getPluginId();
|
||||
Optional<Component> externalBox;
|
||||
|
||||
if (externalPluginChanged.isAdded())
|
||||
{
|
||||
externalBox = Arrays.stream(
|
||||
availablePluginsPanel.getComponents()
|
||||
).filter(extBox ->
|
||||
extBox instanceof ExternalBox && ((ExternalBox) extBox).pluginInfo.id.equals(pluginId)
|
||||
).findFirst();
|
||||
}
|
||||
else
|
||||
{
|
||||
externalBox = Arrays.stream(
|
||||
installedPluginsPanel.getComponents()
|
||||
).filter(extBox ->
|
||||
extBox instanceof ExternalBox && ((ExternalBox) extBox).pluginInfo.id.equals(pluginId)
|
||||
).findFirst();
|
||||
}
|
||||
|
||||
if (externalBox.isEmpty())
|
||||
{
|
||||
log.info("EXTERNALBOX IS EMPTY: {}", pluginId);
|
||||
return;
|
||||
}
|
||||
|
||||
ExternalBox extBox = (ExternalBox) externalBox.get();
|
||||
deps = externalPluginManager.getDependencies();
|
||||
|
||||
try
|
||||
{
|
||||
SwingUtil.syncExec(() ->
|
||||
{
|
||||
if (externalPluginChanged.isAdded())
|
||||
{
|
||||
availablePluginsPanel.remove(externalBox.get());
|
||||
availablePluginsList.remove(extBox.pluginInfo);
|
||||
|
||||
installedPluginsList.add(extBox.pluginInfo);
|
||||
installedPluginsList.sort(Comparator.naturalOrder());
|
||||
|
||||
installedPlugins();
|
||||
|
||||
pluginInstallButton(extBox.install, extBox.pluginInfo, true, deps.contains(extBox.pluginInfo.id));
|
||||
}
|
||||
else
|
||||
{
|
||||
installedPluginsPanel.remove(externalBox.get());
|
||||
installedPluginsList.remove(extBox.pluginInfo);
|
||||
|
||||
availablePluginsList.add(extBox.pluginInfo);
|
||||
availablePluginsList.sort(Comparator.naturalOrder());
|
||||
|
||||
availablePlugins();
|
||||
|
||||
pluginInstallButton(extBox.install, extBox.pluginInfo, false, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (InvocationTargetException | InterruptedException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void installedPlugins()
|
||||
{
|
||||
GridBagConstraints c = new GridBagConstraints();
|
||||
|
||||
installedPluginsPanel.removeAll();
|
||||
String search = searchBar.getText();
|
||||
|
||||
for (PluginInfo pluginInfo : installedPluginsList)
|
||||
{
|
||||
if (!search.equals("") && mismatchesSearchTerms(search, pluginInfo))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (filterComboBox.getSelectedIndex() != 0)
|
||||
{
|
||||
boolean filtered = true;
|
||||
String filter = String.valueOf(filterComboBox.getSelectedItem());
|
||||
for (UpdateRepository updateRepository : updateManager.getRepositories())
|
||||
{
|
||||
if (filter.equals(updateRepository.getUrl().toString().replace("https://raw.githubusercontent.com/", "").replace("/master/", "")) &&
|
||||
pluginInfo.getRepositoryId().equals(updateRepository.getId()))
|
||||
{
|
||||
filtered = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (filtered)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
ExternalBox pluginBox = new ExternalBox(pluginInfo);
|
||||
pluginBox.pluginInfo = pluginInfo;
|
||||
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
c.weightx = 1.0;
|
||||
c.gridy += 1;
|
||||
c.insets = new Insets(5, 0, 0, 0);
|
||||
|
||||
pluginInstallButton(pluginBox.install, pluginInfo, true, deps.contains(pluginInfo.id));
|
||||
installedPluginsPanel.add(pluginBox, c);
|
||||
}
|
||||
|
||||
if (installedPluginsPanel.getComponents().length < 1)
|
||||
{
|
||||
installedPluginsPanel.add(titleLabel("No plugins found"));
|
||||
}
|
||||
}
|
||||
|
||||
private void availablePlugins()
|
||||
{
|
||||
GridBagConstraints c = new GridBagConstraints();
|
||||
|
||||
availablePluginsPanel.removeAll();
|
||||
String search = searchBar.getText();
|
||||
|
||||
for (PluginInfo pluginInfo : availablePluginsList)
|
||||
{
|
||||
if (!search.equals("") && mismatchesSearchTerms(search, pluginInfo))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (filterComboBox.getSelectedIndex() != 0)
|
||||
{
|
||||
boolean filtered = true;
|
||||
String filter = String.valueOf(filterComboBox.getSelectedItem());
|
||||
for (UpdateRepository updateRepository : updateManager.getRepositories())
|
||||
{
|
||||
if (filter.equals(updateRepository.getUrl().toString().replace("https://raw.githubusercontent.com/", "").replace("/master/", "")) &&
|
||||
pluginInfo.getRepositoryId().equals(updateRepository.getId()))
|
||||
{
|
||||
filtered = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (filtered)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
ExternalBox pluginBox = new ExternalBox(pluginInfo);
|
||||
pluginBox.pluginInfo = pluginInfo;
|
||||
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
c.weightx = 1.0;
|
||||
c.gridy += 1;
|
||||
c.insets = new Insets(5, 0, 0, 0);
|
||||
|
||||
pluginInstallButton(pluginBox.install, pluginInfo, false, false);
|
||||
availablePluginsPanel.add(pluginBox, c);
|
||||
}
|
||||
|
||||
if (availablePluginsPanel.getComponents().length < 1)
|
||||
{
|
||||
availablePluginsPanel.add(titleLabel("No plugins found"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void pluginInstallButton(JLabel install, PluginInfo pluginInfo, boolean installed, boolean hideAction)
|
||||
{
|
||||
install.setIcon(installed ? hideAction ? DELETE_ICON_GRAY : DELETE_ICON : ADD_ICON);
|
||||
install.setText("");
|
||||
|
||||
if (!hideAction)
|
||||
{
|
||||
install.setToolTipText(installed ? "Uninstall" : "Install");
|
||||
}
|
||||
install.addMouseListener(new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e)
|
||||
{
|
||||
if (installed)
|
||||
{
|
||||
if (hideAction)
|
||||
{
|
||||
JOptionPane.showMessageDialog(ClientUI.getFrame(), "This plugin can't be uninstalled because one or more other plugins have a dependency on it.", "Error!", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
else
|
||||
{
|
||||
install.setIcon(null);
|
||||
install.setText("Uninstalling");
|
||||
|
||||
SwingWorker<Boolean, Void> worker = new SwingWorker<>()
|
||||
{
|
||||
@Override
|
||||
protected Boolean doInBackground()
|
||||
{
|
||||
return externalPluginManager.uninstall(pluginInfo.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done()
|
||||
{
|
||||
|
||||
boolean status = false;
|
||||
try
|
||||
{
|
||||
status = get();
|
||||
}
|
||||
catch (InterruptedException | ExecutionException e)
|
||||
{
|
||||
}
|
||||
|
||||
if (!status)
|
||||
{
|
||||
pluginInstallButton(install, pluginInfo, installed, hideAction);
|
||||
}
|
||||
}
|
||||
};
|
||||
worker.execute();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
install.setIcon(null);
|
||||
install.setText("Installing");
|
||||
|
||||
SwingWorker<Boolean, Void> worker = new SwingWorker<>()
|
||||
{
|
||||
@Override
|
||||
protected Boolean doInBackground()
|
||||
{
|
||||
return installPlugin(pluginInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done()
|
||||
{
|
||||
|
||||
boolean status = false;
|
||||
try
|
||||
{
|
||||
status = get();
|
||||
}
|
||||
catch (InterruptedException | ExecutionException e)
|
||||
{
|
||||
}
|
||||
|
||||
if (!status)
|
||||
{
|
||||
pluginInstallButton(install, pluginInfo, installed, hideAction);
|
||||
}
|
||||
}
|
||||
};
|
||||
worker.execute();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e)
|
||||
{
|
||||
if (install.getText().toLowerCase().contains("installing"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
install.setIcon(installed ? hideAction ? DELETE_HOVER_ICON_GRAY : DELETE_HOVER_ICON : ADD_HOVER_ICON);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent e)
|
||||
{
|
||||
if (install.getText().toLowerCase().contains("installing"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
install.setIcon(installed ? hideAction ? DELETE_ICON_GRAY : DELETE_ICON : ADD_ICON);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean installPlugin(PluginInfo pluginInfo)
|
||||
{
|
||||
try
|
||||
{
|
||||
return externalPluginManager.install(pluginInfo.id);
|
||||
}
|
||||
catch (VerifyException ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
SwingUtil.syncExec(() ->
|
||||
JOptionPane.showMessageDialog(ClientUI.getFrame(), pluginInfo.name + " could not be installed, the hash could not be verified.", "Error!", JOptionPane.ERROR_MESSAGE));
|
||||
}
|
||||
catch (InvocationTargetException | InterruptedException ignored)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
179
runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/RepositoryBox.java
vendored
Normal file
179
runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/RepositoryBox.java
vendored
Normal file
@@ -0,0 +1,179 @@
|
||||
package com.openosrs.client.plugins.openosrs.externals;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.Optional;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.border.CompoundBorder;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import com.openosrs.client.plugins.ExternalPluginManager;
|
||||
import net.runelite.client.ui.ColorScheme;
|
||||
import net.runelite.client.ui.FontManager;
|
||||
import com.openosrs.client.ui.JMultilineLabel;
|
||||
import com.openosrs.client.util.ImageUtil;
|
||||
import net.runelite.client.util.LinkBrowser;
|
||||
import org.pf4j.update.PluginInfo;
|
||||
import org.pf4j.update.UpdateRepository;
|
||||
|
||||
public class RepositoryBox extends JPanel
|
||||
{
|
||||
private static final Font normalFont = FontManager.getRunescapeFont();
|
||||
private static final Font smallFont = FontManager.getRunescapeSmallFont();
|
||||
private static final ImageIcon DELETE_ICON;
|
||||
private static final ImageIcon DELETE_HOVER_ICON;
|
||||
private static final ImageIcon DISCORD_ICON;
|
||||
private static final ImageIcon DISCORD_HOVER_ICON;
|
||||
|
||||
static
|
||||
{
|
||||
final BufferedImage deleteImg =
|
||||
ImageUtil.recolorImage(
|
||||
ImageUtil.resizeCanvas(
|
||||
ImageUtil.getResourceStreamFromClass(ExternalPluginManagerPanel.class, "delete_icon.png"), 14, 14
|
||||
), ColorScheme.BRAND_BLUE
|
||||
);
|
||||
DELETE_ICON = new ImageIcon(deleteImg);
|
||||
DELETE_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(deleteImg, 0.53f));
|
||||
|
||||
final BufferedImage discordImg =
|
||||
ImageUtil.recolorImage(
|
||||
ImageUtil.resizeCanvas(
|
||||
ImageUtil.getResourceStreamFromClass(ExternalPluginManagerPanel.class, "discord_icon.png"), 14, 14
|
||||
), Color.WHITE
|
||||
);
|
||||
DISCORD_ICON = new ImageIcon(discordImg);
|
||||
DISCORD_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(discordImg, 0.53f));
|
||||
}
|
||||
|
||||
RepositoryBox(ExternalPluginManager externalPluginManager, UpdateRepository updateRepository)
|
||||
{
|
||||
setLayout(new BorderLayout());
|
||||
setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
|
||||
String name = updateRepository.getId();
|
||||
String urlString = updateRepository.getUrl().toString();
|
||||
if (urlString.startsWith("/"))
|
||||
{
|
||||
urlString = urlString.substring(1);
|
||||
}
|
||||
|
||||
JPanel titleWrapper = new JPanel(new BorderLayout());
|
||||
titleWrapper.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
titleWrapper.setBorder(new CompoundBorder(
|
||||
BorderFactory.createMatteBorder(0, 0, 1, 0, ColorScheme.DARK_GRAY_COLOR),
|
||||
BorderFactory.createLineBorder(ColorScheme.DARKER_GRAY_COLOR)
|
||||
));
|
||||
|
||||
JLabel title = new JLabel();
|
||||
title.setText(name);
|
||||
title.setFont(normalFont);
|
||||
title.setBorder(null);
|
||||
title.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
title.setPreferredSize(new Dimension(0, 24));
|
||||
title.setForeground(Color.WHITE);
|
||||
title.setBorder(new EmptyBorder(0, 8, 0, 0));
|
||||
|
||||
JPanel titleActions = new JPanel(new BorderLayout(3, 0));
|
||||
titleActions.setBorder(new EmptyBorder(0, 0, 0, 8));
|
||||
titleActions.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
|
||||
Optional<PluginInfo> firstPlugin = updateRepository.getPlugins().values().stream().findFirst();
|
||||
|
||||
if (firstPlugin.isPresent() && !firstPlugin.get().projectUrl.equals(""))
|
||||
{
|
||||
JLabel support = new JLabel();
|
||||
support.setIcon(DISCORD_ICON);
|
||||
support.setToolTipText("Support");
|
||||
support.addMouseListener(new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e)
|
||||
{
|
||||
LinkBrowser.browse(firstPlugin.get().projectUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e)
|
||||
{
|
||||
support.setIcon(DISCORD_HOVER_ICON);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent e)
|
||||
{
|
||||
support.setIcon(DISCORD_ICON);
|
||||
}
|
||||
});
|
||||
|
||||
titleActions.add(support, BorderLayout.WEST);
|
||||
}
|
||||
|
||||
if (!name.equals("OpenOSRS") && !name.equals("Plugin-Hub"))
|
||||
{
|
||||
JLabel install = new JLabel();
|
||||
install.setIcon(DELETE_ICON);
|
||||
install.setToolTipText("Remove");
|
||||
install.addMouseListener(new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e)
|
||||
{
|
||||
externalPluginManager.removeRepository(updateRepository.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e)
|
||||
{
|
||||
install.setIcon(DELETE_HOVER_ICON);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent e)
|
||||
{
|
||||
install.setIcon(DELETE_ICON);
|
||||
}
|
||||
});
|
||||
|
||||
titleActions.add(install, BorderLayout.EAST);
|
||||
}
|
||||
|
||||
titleWrapper.add(title, BorderLayout.CENTER);
|
||||
titleWrapper.add(titleActions, BorderLayout.EAST);
|
||||
|
||||
JMultilineLabel repository = new JMultilineLabel();
|
||||
repository.setText(formatURL(urlString));
|
||||
repository.setFont(smallFont);
|
||||
repository.setDisabledTextColor(Color.WHITE);
|
||||
|
||||
String finalUrlString = urlString;
|
||||
repository.addMouseListener(new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e)
|
||||
{
|
||||
LinkBrowser.browse(formatURL(finalUrlString));
|
||||
}
|
||||
});
|
||||
|
||||
add(titleWrapper, BorderLayout.NORTH);
|
||||
add(repository, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
private String formatURL(String url)
|
||||
{
|
||||
if (url.contains("githubusercontent"))
|
||||
{
|
||||
url = url.replace("raw.githubusercontent", "github").replace("/master/", "");
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.openosrs.client.plugins.openosrs.externals;
|
||||
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.Insets;
|
||||
import javax.inject.Inject;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import net.runelite.client.eventbus.EventBus;
|
||||
import com.openosrs.client.events.ExternalRepositoryChanged;
|
||||
import com.openosrs.client.plugins.ExternalPluginManager;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.ui.ColorScheme;
|
||||
import org.pf4j.update.UpdateRepository;
|
||||
|
||||
public class RepositoryPanel extends JPanel
|
||||
{
|
||||
@Inject
|
||||
public EventBus eventBus;
|
||||
|
||||
private final ExternalPluginManager externalPluginManager;
|
||||
|
||||
private final GridBagConstraints c = new GridBagConstraints();
|
||||
|
||||
RepositoryPanel(ExternalPluginManager externalPluginManager, EventBus eventBus)
|
||||
{
|
||||
this.externalPluginManager = externalPluginManager;
|
||||
|
||||
setLayout(new GridBagLayout());
|
||||
setBackground(ColorScheme.DARK_GRAY_COLOR);
|
||||
setBorder(new EmptyBorder(0, 10, 0, 10));
|
||||
|
||||
onExternalRepositoryChanged(null);
|
||||
|
||||
eventBus.register(this);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
private void onExternalRepositoryChanged(ExternalRepositoryChanged event)
|
||||
{
|
||||
removeAll();
|
||||
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
c.weightx = 1;
|
||||
c.gridy = 0;
|
||||
c.insets = new Insets(5, 0, 0, 0);
|
||||
|
||||
for (UpdateRepository repository : externalPluginManager.getRepositories())
|
||||
{
|
||||
final RepositoryBox p = new RepositoryBox(externalPluginManager, repository);
|
||||
add(p, c);
|
||||
c.gridy++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.openosrs.client.ui;
|
||||
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.text.DefaultCaret;
|
||||
|
||||
public class JMultilineLabel extends JTextArea
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public JMultilineLabel()
|
||||
{
|
||||
super();
|
||||
setEditable(false);
|
||||
setCursor(null);
|
||||
setOpaque(false);
|
||||
setFocusable(false);
|
||||
setWrapStyleWord(true);
|
||||
setLineWrap(true);
|
||||
setBorder(new EmptyBorder(0, 8, 0, 8));
|
||||
setAlignmentY(JLabel.CENTER_ALIGNMENT);
|
||||
|
||||
DefaultCaret caret = (DefaultCaret) getCaret();
|
||||
caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.openosrs.client.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.swing.Timer;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
|
||||
public class DeferredDocumentChangedListener implements DocumentListener
|
||||
{
|
||||
private final Timer timer;
|
||||
private final List<ChangeListener> listeners;
|
||||
|
||||
public DeferredDocumentChangedListener()
|
||||
{
|
||||
listeners = new ArrayList<>(25);
|
||||
timer = new Timer(200, e -> fireStateChanged());
|
||||
timer.setRepeats(false);
|
||||
}
|
||||
|
||||
public void addChangeListener(ChangeListener listener)
|
||||
{
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
private void fireStateChanged()
|
||||
{
|
||||
if (!listeners.isEmpty())
|
||||
{
|
||||
ChangeEvent evt = new ChangeEvent(this);
|
||||
for (ChangeListener listener : listeners)
|
||||
{
|
||||
listener.stateChanged(evt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e)
|
||||
{
|
||||
timer.restart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e)
|
||||
{
|
||||
timer.restart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e)
|
||||
{
|
||||
timer.restart();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
package com.openosrs.client.util;
|
||||
|
||||
import com.openosrs.client.OpenOSRS;
|
||||
import io.reactivex.rxjava3.subjects.PublishSubject;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Future;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.client.RuneLite;
|
||||
import com.openosrs.client.config.OpenOSRSConfig;
|
||||
import net.runelite.client.eventbus.EventBus;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.events.ClientShutdown;
|
||||
import net.runelite.client.events.ConfigChanged;
|
||||
import com.openosrs.client.ui.OpenOSRSSplashScreen;
|
||||
import org.jgroups.Address;
|
||||
import org.jgroups.JChannel;
|
||||
import org.jgroups.Message;
|
||||
import org.jgroups.ObjectMessage;
|
||||
import org.jgroups.Receiver;
|
||||
import org.jgroups.View;
|
||||
import org.jgroups.util.Util;
|
||||
|
||||
@Slf4j
|
||||
@Singleton
|
||||
public class Groups implements Receiver
|
||||
{
|
||||
private final OpenOSRSConfig openOSRSConfig;
|
||||
private final JChannel channel;
|
||||
|
||||
@Getter(AccessLevel.PUBLIC)
|
||||
private int instanceCount;
|
||||
@Getter(AccessLevel.PUBLIC)
|
||||
private List<Address> members;
|
||||
@Getter(AccessLevel.PUBLIC)
|
||||
private final 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, EventBus eventBus) throws Exception
|
||||
{
|
||||
this.openOSRSConfig = openOSRSConfig;
|
||||
|
||||
try (final InputStream is = RuneLite.class.getResourceAsStream("/udp-openosrs.xml"))
|
||||
{
|
||||
this.channel = new JChannel(is)
|
||||
.setName(OpenOSRS.uuid)
|
||||
.setReceiver(this)
|
||||
.setDiscardOwnMessages(true)
|
||||
.connect("openosrs");
|
||||
}
|
||||
|
||||
eventBus.register(this);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onClientShutdown(ClientShutdown event)
|
||||
{
|
||||
Future<Void> f = close();
|
||||
event.waitFor(f);
|
||||
}
|
||||
|
||||
public void broadcastSring(String command)
|
||||
{
|
||||
send(null, command);
|
||||
}
|
||||
|
||||
public void sendConfig(Address destination, ConfigChanged configChanged)
|
||||
{
|
||||
if (!openOSRSConfig.localSync() || OpenOSRSSplashScreen.showing() || instanceCount < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
byte[] buffer = Util.objectToByteBuffer(configChanged);
|
||||
Message message = new ObjectMessage()
|
||||
.setDest(destination)
|
||||
.setObject(buffer);
|
||||
|
||||
channel.send(message);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void sendString(String command)
|
||||
{
|
||||
String[] messageObject = command.split(";");
|
||||
String pluginId = messageObject[1];
|
||||
|
||||
messageMap.put(pluginId, new ArrayList<>());
|
||||
|
||||
for (Address member : channel.getView().getMembers())
|
||||
{
|
||||
if (member.toString().equals(OpenOSRS.uuid))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
messageMap.get(pluginId).add(member);
|
||||
send(member, command);
|
||||
}
|
||||
}
|
||||
|
||||
public void send(Address destination, String command)
|
||||
{
|
||||
if (!openOSRSConfig.localSync() || OpenOSRSSplashScreen.showing() || instanceCount < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
channel.send(new ObjectMessage(destination, command));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void viewAccepted(View view)
|
||||
{
|
||||
members = view.getMembers();
|
||||
instanceCount = members.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(Message message)
|
||||
{
|
||||
if (OpenOSRSSplashScreen.showing())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.getObject() instanceof String)
|
||||
{
|
||||
messageStringSubject.onNext(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
messageObjectSubject.onNext(message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private CompletableFuture<Void> close()
|
||||
{
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
try
|
||||
{
|
||||
channel.close();
|
||||
future.complete(null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
future.completeExceptionally(ex);
|
||||
}
|
||||
|
||||
return future;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.openosrs.client.util;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.WritableRaster;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class ImageUtil extends net.runelite.client.util.ImageUtil
|
||||
{
|
||||
/**
|
||||
* Recolors pixels of the given image with the given color based on a given recolor condition
|
||||
* predicate.
|
||||
*
|
||||
* @param image The image which should have its non-transparent pixels recolored.
|
||||
* @param color The color with which to recolor pixels.
|
||||
* @param recolorCondition The condition on which to recolor pixels with the given color.
|
||||
* @return The given image with all pixels fulfilling the recolor condition predicate
|
||||
* set to the given color.
|
||||
*/
|
||||
public static BufferedImage recolorImage(final BufferedImage image, final Color color, final Predicate<Color> recolorCondition)
|
||||
{
|
||||
final BufferedImage recoloredImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);
|
||||
for (int x = 0; x < recoloredImage.getWidth(); x++)
|
||||
{
|
||||
for (int y = 0; y < recoloredImage.getHeight(); y++)
|
||||
{
|
||||
final Color pixelColor = new Color(image.getRGB(x, y), true);
|
||||
if (!recolorCondition.test(pixelColor))
|
||||
{
|
||||
recoloredImage.setRGB(x, y, image.getRGB(x, y));
|
||||
continue;
|
||||
}
|
||||
|
||||
recoloredImage.setRGB(x, y, color.getRGB());
|
||||
}
|
||||
}
|
||||
return recoloredImage;
|
||||
}
|
||||
|
||||
public static BufferedImage recolorImage(BufferedImage image, final Color color)
|
||||
{
|
||||
int width = image.getWidth();
|
||||
int height = image.getHeight();
|
||||
WritableRaster raster = image.getRaster();
|
||||
|
||||
for (int xx = 0; xx < width; xx++)
|
||||
{
|
||||
for (int yy = 0; yy < height; yy++)
|
||||
{
|
||||
int[] pixels = raster.getPixel(xx, yy, (int[]) null);
|
||||
pixels[0] = color.getRed();
|
||||
pixels[1] = color.getGreen();
|
||||
pixels[2] = color.getBlue();
|
||||
raster.setPixel(xx, yy, pixels);
|
||||
}
|
||||
}
|
||||
return image;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
package com.openosrs.client.util;
|
||||
|
||||
import java.awt.Polygon;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.Player;
|
||||
import net.runelite.api.WorldType;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
|
||||
public class MiscUtils
|
||||
{
|
||||
private static final int[] abovePointsX = {2944, 3392, 3392, 2944};
|
||||
private static final int[] abovePointsY = {3523, 3523, 3971, 3971};
|
||||
private static final int[] belowPointsX = {2944, 2944, 3264, 3264};
|
||||
private static final int[] belowPointsY = {9918, 10360, 10360, 9918};
|
||||
|
||||
private static final Polygon abovePoly = new Polygon(abovePointsX, abovePointsY, abovePointsX.length);
|
||||
private static final Polygon belowPoly = new Polygon(belowPointsX, belowPointsY, belowPointsX.length);
|
||||
|
||||
private static final ChronoUnit[] ORDERED_CHRONOS = new ChronoUnit[]
|
||||
{
|
||||
ChronoUnit.YEARS,
|
||||
ChronoUnit.MONTHS,
|
||||
ChronoUnit.WEEKS,
|
||||
ChronoUnit.DAYS,
|
||||
ChronoUnit.HOURS,
|
||||
ChronoUnit.MINUTES,
|
||||
ChronoUnit.SECONDS
|
||||
};
|
||||
|
||||
//test replacement so private for now
|
||||
private static boolean inWildy(WorldPoint point)
|
||||
{
|
||||
if (point == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return abovePoly.contains(point.getX(), point.getY()) || belowPoly.contains(point.getX(), point.getY());
|
||||
}
|
||||
|
||||
public static int getWildernessLevelFrom(Client client, WorldPoint point)
|
||||
{
|
||||
if (client == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (point == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int x = point.getX();
|
||||
|
||||
if (point.getPlane() == 0 && (x < 2940 || x > 3391))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int y = point.getY();
|
||||
//v underground //v above ground
|
||||
int wildernessLevel = clamp(y > 6400 ? ((y - 9920) / 8) + 1 : ((y - 3520) / 8) + 1, 0, 56);
|
||||
|
||||
if (point.getPlane() > 0 && y < 9920)
|
||||
{
|
||||
wildernessLevel = 0;
|
||||
}
|
||||
|
||||
if (client.getWorldType().stream().anyMatch(worldType -> worldType == WorldType.PVP || worldType == WorldType.HIGH_RISK))
|
||||
{
|
||||
wildernessLevel += 15;
|
||||
}
|
||||
|
||||
return Math.max(0, wildernessLevel);
|
||||
}
|
||||
|
||||
public static int clamp(int val, int min, int max)
|
||||
{
|
||||
return Math.max(min, Math.min(max, val));
|
||||
}
|
||||
|
||||
public static float clamp(float val, float min, float max)
|
||||
{
|
||||
return Math.max(min, Math.min(max, val));
|
||||
}
|
||||
|
||||
public static boolean inWilderness(Client client)
|
||||
{
|
||||
Player localPlayer = client.getLocalPlayer();
|
||||
|
||||
if (localPlayer == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return inWildy(localPlayer.getWorldLocation());
|
||||
|
||||
//return getWildernessLevelFrom(client, localPlayer.getWorldLocation()) > 0;
|
||||
}
|
||||
|
||||
public static String formatTimeAgo(Duration dur)
|
||||
{
|
||||
long dA = 0, dB = 0, rm;
|
||||
ChronoUnit cA = null, cB = null;
|
||||
for (int i = 0; i < ORDERED_CHRONOS.length; i++)
|
||||
{
|
||||
cA = ORDERED_CHRONOS[i];
|
||||
dA = dur.getSeconds() / cA.getDuration().getSeconds();
|
||||
rm = dur.getSeconds() % cA.getDuration().getSeconds();
|
||||
if (dA <= 0)
|
||||
{
|
||||
cA = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i + 1 < ORDERED_CHRONOS.length)
|
||||
{
|
||||
cB = ORDERED_CHRONOS[i + 1];
|
||||
dB = rm / cB.getDuration().getSeconds();
|
||||
|
||||
if (dB <= 0)
|
||||
{
|
||||
cB = null;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (cA == null)
|
||||
{
|
||||
return "just now.";
|
||||
}
|
||||
|
||||
String str = formatUnit(cA, dA);
|
||||
|
||||
if (cB != null)
|
||||
{
|
||||
str += " and " + formatUnit(cB, dB);
|
||||
}
|
||||
|
||||
return str + " ago.";
|
||||
}
|
||||
|
||||
private static String formatUnit(ChronoUnit chrono, long val)
|
||||
{
|
||||
boolean multiple = val != 1;
|
||||
String str;
|
||||
if (multiple)
|
||||
{
|
||||
str = val + " ";
|
||||
}
|
||||
else
|
||||
{
|
||||
str = "a" + (chrono == ChronoUnit.HOURS ? "n " : " ");
|
||||
}
|
||||
str += chrono.name().toLowerCase();
|
||||
if (!multiple)
|
||||
{
|
||||
if (str.charAt(str.length() - 1) == 's')
|
||||
{
|
||||
str = str.substring(0, str.length() - 1);
|
||||
}
|
||||
}
|
||||
else if (str.charAt(str.length() - 1) != 's')
|
||||
{
|
||||
str += "s";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mostly stolen from {@link java.net.URLStreamHandler#toExternalForm(URL)}
|
||||
*
|
||||
* @param url URL to encode
|
||||
* @return URL, with path, query and ref encoded
|
||||
*/
|
||||
public static String urlToStringEncoded(URL url)
|
||||
{
|
||||
String s;
|
||||
String path = url.getPath() != null ? Stream.of(url.getPath().split("/"))
|
||||
.map(s2 -> URLEncoder.encode(s2, StandardCharsets.UTF_8)).collect(Collectors.joining("/")) : "";
|
||||
return url.getProtocol()
|
||||
+ ':'
|
||||
+ (((s = url.getAuthority()) != null && s.length() > 0) ? "//" + s : "")
|
||||
+ (path)
|
||||
+ (((s = url.getQuery()) != null) ? '?' + urlEncode(s) : "")
|
||||
+ (((s = url.getRef()) != null) ? '#' + urlEncode(s) : "");
|
||||
}
|
||||
|
||||
private static String urlEncode(String s)
|
||||
{
|
||||
return URLEncoder.encode(s, StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package com.openosrs.client.util;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import net.runelite.client.util.CallableExceptionLogger;
|
||||
import static net.runelite.client.util.RunnableExceptionLogger.wrap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
// Awkward name because plugins already referenced the ExecutorServiceExceptionLogger
|
||||
// (which only handles ScheduledExecutorServices) before this class was introduced
|
||||
public class NonScheduledExecutorServiceExceptionLogger implements ExecutorService
|
||||
{
|
||||
private final ExecutorService service;
|
||||
|
||||
public NonScheduledExecutorServiceExceptionLogger(ExecutorService service)
|
||||
{
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown()
|
||||
{
|
||||
service.shutdown();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<Runnable> shutdownNow()
|
||||
{
|
||||
return service.shutdownNow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShutdown()
|
||||
{
|
||||
return service.isShutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTerminated()
|
||||
{
|
||||
return service.isTerminated();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean awaitTermination(long timeout, @NotNull TimeUnit unit) throws InterruptedException
|
||||
{
|
||||
return service.awaitTermination(timeout, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(@NotNull Runnable command)
|
||||
{
|
||||
service.execute(wrap(command));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <T> Future<T> submit(@NotNull Callable<T> task)
|
||||
{
|
||||
return service.submit(CallableExceptionLogger.wrap(task));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <T> Future<T> submit(@NotNull Runnable task, T result)
|
||||
{
|
||||
return service.submit(wrap(task), result);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Future<?> submit(@NotNull Runnable task)
|
||||
{
|
||||
return service.submit(wrap(task));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <T> List<Future<T>> invokeAll(@NotNull Collection<? extends Callable<T>> tasks) throws InterruptedException
|
||||
{
|
||||
return service.invokeAll(tasks);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <T> List<Future<T>> invokeAll(@NotNull Collection<? extends Callable<T>> tasks, long timeout, @NotNull TimeUnit unit) throws InterruptedException
|
||||
{
|
||||
return service.invokeAll(tasks, timeout, unit);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <T> T invokeAny(@NotNull Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException
|
||||
{
|
||||
return service.invokeAny(tasks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T invokeAny(@NotNull Collection<? extends Callable<T>> tasks, long timeout, @NotNull TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
|
||||
{
|
||||
return service.invokeAny(tasks, timeout, unit);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.openosrs.client.util;
|
||||
|
||||
import java.awt.EventQueue;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
public class SwingUtil extends net.runelite.client.util.SwingUtil
|
||||
{
|
||||
public static void syncExec(final Runnable r) throws InvocationTargetException, InterruptedException
|
||||
{
|
||||
if (EventQueue.isDispatchThread())
|
||||
{
|
||||
r.run();
|
||||
}
|
||||
else
|
||||
{
|
||||
EventQueue.invokeAndWait(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
import com.openosrs.client.PluginManager;
|
||||
import com.openosrs.client.plugins.BuiltInPluginManager;
|
||||
import java.io.File;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.RuntimeMXBean;
|
||||
@@ -112,6 +112,9 @@ public class RuneLite
|
||||
@Inject
|
||||
private ExternalPluginManager externalPluginManager;
|
||||
|
||||
@Inject
|
||||
private com.openosrs.client.plugins.ExternalPluginManager oprsExternalPluginManager;
|
||||
|
||||
@Inject
|
||||
private EventBus eventBus;
|
||||
|
||||
@@ -322,9 +325,19 @@ public class RuneLite
|
||||
// Tell the plugin manager if client is outdated or not
|
||||
pluginManager.setOutdated(isOutdated);
|
||||
|
||||
// Load external plugin manager
|
||||
oprsExternalPluginManager.startExternalUpdateManager();
|
||||
oprsExternalPluginManager.startExternalPluginManager();
|
||||
|
||||
// Update external plugins
|
||||
//oprsExternalPluginManager.update(); //TODO: Re-enable after fixing actions for new repo
|
||||
|
||||
// Load the plugins, but does not start them yet.
|
||||
// This will initialize configuration
|
||||
pluginManager.loadCorePlugins();
|
||||
|
||||
oprsExternalPluginManager.loadPlugins();
|
||||
|
||||
externalPluginManager.loadExternalPlugins();
|
||||
|
||||
SplashScreen.stage(.70, null, "Finalizing configuration");
|
||||
@@ -377,8 +390,8 @@ public class RuneLite
|
||||
overlayManager.add(tooltipOverlay.get());
|
||||
}
|
||||
|
||||
//Load OPRS plugins
|
||||
PluginManager.loadPlugins();
|
||||
//Load built-in OPRS plugins
|
||||
BuiltInPluginManager.loadPlugins();
|
||||
|
||||
// Start plugins
|
||||
pluginManager.startPlugins();
|
||||
|
||||
@@ -24,13 +24,20 @@
|
||||
*/
|
||||
package net.runelite.client;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Provides;
|
||||
import com.google.inject.name.Names;
|
||||
import com.openosrs.client.config.OpenOSRSConfig;
|
||||
import com.openosrs.client.util.NonScheduledExecutorServiceExceptionLogger;
|
||||
import java.applet.Applet;
|
||||
import java.io.File;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Supplier;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Singleton;
|
||||
@@ -129,4 +136,28 @@ public class RuneLiteModule extends AbstractModule
|
||||
{
|
||||
return new ChatClient(okHttpClient);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
OpenOSRSConfig provideOpenOSRSConfig(ConfigManager configManager)
|
||||
{
|
||||
return configManager.getConfig(OpenOSRSConfig.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
ExecutorService provideExecutorService()
|
||||
{
|
||||
int poolSize = 2 * Runtime.getRuntime().availableProcessors();
|
||||
|
||||
// Will start up to poolSize threads (because of allowCoreThreadTimeOut) as necessary, and times out
|
||||
// unused threads after 1 minute
|
||||
ThreadPoolExecutor executor = new ThreadPoolExecutor(poolSize, poolSize,
|
||||
60L, TimeUnit.SECONDS,
|
||||
new LinkedBlockingQueue<>(),
|
||||
new ThreadFactoryBuilder().setNameFormat("worker-%d").build());
|
||||
executor.allowCoreThreadTimeOut(true);
|
||||
|
||||
return new NonScheduledExecutorServiceExceptionLogger(executor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,8 +47,10 @@ import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
@@ -97,6 +99,7 @@ public class PluginManager
|
||||
|
||||
@Setter
|
||||
boolean isOutdated;
|
||||
private Collection<com.openosrs.client.plugins.Plugin> oprsPlugins;
|
||||
|
||||
@Inject
|
||||
@VisibleForTesting
|
||||
@@ -191,7 +194,26 @@ public class PluginManager
|
||||
injectors.add(RuneLite.getInjector());
|
||||
plugins = getPlugins();
|
||||
}
|
||||
plugins.forEach(pl -> injectors.add(pl.getInjector()));
|
||||
plugins.forEach(pl ->
|
||||
{
|
||||
//TODO: Not sure why this is necessary but it is. The Injector isn't null when its handed off from our ExternalPluginManager.
|
||||
// Hopefully we can figure out the root cause of the underlying issue.
|
||||
if (pl.injector == null)
|
||||
{
|
||||
// Create injector for the module
|
||||
Module pluginModule = (Binder binder) ->
|
||||
{
|
||||
// Since the plugin itself is a module, it won't bind itself, so we'll bind it here
|
||||
binder.bind(com.openosrs.client.plugins.Plugin.class).toInstance((com.openosrs.client.plugins.Plugin)pl);
|
||||
binder.install(pl);
|
||||
};
|
||||
Injector pluginInjector = RuneLite.getInjector().createChildInjector(pluginModule);
|
||||
pluginInjector.injectMembers(pl);
|
||||
pl.injector = pluginInjector;
|
||||
}
|
||||
|
||||
injectors.add(pl.getInjector());
|
||||
});
|
||||
|
||||
List<Config> list = new ArrayList<>();
|
||||
for (Injector injector : injectors)
|
||||
@@ -547,6 +569,64 @@ public class PluginManager
|
||||
return plugins;
|
||||
}
|
||||
|
||||
public Collection<com.openosrs.client.plugins.Plugin> getOprsPlugins()
|
||||
{
|
||||
return oprsPlugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Topologically sort a graph into separate groups.
|
||||
* Each group represents the dependency level of the plugins.
|
||||
* Plugins in group (index) 0 has no dependents.
|
||||
* Plugins in group 1 has dependents in group 0.
|
||||
* Plugins in group 2 has dependents in group 1, etc.
|
||||
* This allows for loading dependent groups serially, starting from the last group,
|
||||
* while loading plugins within each group in parallel.
|
||||
*
|
||||
* @param graph
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public static <T> List<List<T>> topologicalGroupSort(Graph<T> graph)
|
||||
{
|
||||
final Set<T> root = graph.nodes().stream()
|
||||
.filter(node -> graph.inDegree(node) == 0)
|
||||
.collect(Collectors.toSet());
|
||||
final Map<T, Integer> dependencyCount = new HashMap<>();
|
||||
|
||||
root.forEach(n -> dependencyCount.put(n, 0));
|
||||
root.forEach(n -> graph.successors(n)
|
||||
.forEach(m -> incrementChildren(graph, dependencyCount, m, dependencyCount.get(n) + 1)));
|
||||
|
||||
// create list<list> dependency grouping
|
||||
final List<List<T>> dependencyGroups = new ArrayList<>();
|
||||
final int[] curGroup = {-1};
|
||||
|
||||
dependencyCount.entrySet().stream()
|
||||
.sorted(Map.Entry.comparingByValue())
|
||||
.forEach(entry ->
|
||||
{
|
||||
if (entry.getValue() != curGroup[0])
|
||||
{
|
||||
curGroup[0] = entry.getValue();
|
||||
dependencyGroups.add(new ArrayList<>());
|
||||
}
|
||||
dependencyGroups.get(dependencyGroups.size() - 1).add(entry.getKey());
|
||||
});
|
||||
|
||||
return dependencyGroups;
|
||||
}
|
||||
|
||||
private static <T> void incrementChildren(Graph<T> graph, Map<T, Integer> dependencyCount, T n, int val)
|
||||
{
|
||||
if (!dependencyCount.containsKey(n) || dependencyCount.get(n) < val)
|
||||
{
|
||||
dependencyCount.put(n, val);
|
||||
graph.successors(n).forEach(m ->
|
||||
incrementChildren(graph, dependencyCount, m, val + 1));
|
||||
}
|
||||
}
|
||||
|
||||
private void schedule(Plugin plugin)
|
||||
{
|
||||
for (Method method : plugin.getClass().getMethods())
|
||||
|
||||
@@ -76,6 +76,7 @@ class PluginListPanel extends PluginPanel
|
||||
private static final String RUNELITE_GROUP_NAME = RuneLiteConfig.class.getAnnotation(ConfigGroup.class).value();
|
||||
private static final String PINNED_PLUGINS_CONFIG_KEY = "pinnedPlugins";
|
||||
private static final ImmutableList<String> CATEGORY_TAGS = ImmutableList.of(
|
||||
"OpenOSRS",
|
||||
"Combat",
|
||||
"Chat",
|
||||
"Item",
|
||||
|
||||
@@ -128,7 +128,8 @@ public class ClientUI
|
||||
private boolean withTitleBar;
|
||||
private BufferedImage sidebarOpenIcon;
|
||||
private BufferedImage sidebarClosedIcon;
|
||||
private ContainableFrame frame;
|
||||
@Getter
|
||||
private static ContainableFrame frame;
|
||||
private JPanel navContainer;
|
||||
private PluginPanel pluginPanel;
|
||||
private ClientPluginToolbar pluginToolbar;
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
runelite-client/src/main/resources/com/openosrs/client/plugins/openosrs/externals/add_icon.png
vendored
Normal file
BIN
runelite-client/src/main/resources/com/openosrs/client/plugins/openosrs/externals/add_icon.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 121 B |
BIN
runelite-client/src/main/resources/com/openosrs/client/plugins/openosrs/externals/add_raw_icon.png
vendored
Normal file
BIN
runelite-client/src/main/resources/com/openosrs/client/plugins/openosrs/externals/add_raw_icon.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 384 B |
BIN
runelite-client/src/main/resources/com/openosrs/client/plugins/openosrs/externals/delete_icon.png
vendored
Normal file
BIN
runelite-client/src/main/resources/com/openosrs/client/plugins/openosrs/externals/delete_icon.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 299 B |
BIN
runelite-client/src/main/resources/com/openosrs/client/plugins/openosrs/externals/discord_icon.png
vendored
Normal file
BIN
runelite-client/src/main/resources/com/openosrs/client/plugins/openosrs/externals/discord_icon.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 312 B |
BIN
runelite-client/src/main/resources/com/openosrs/client/plugins/openosrs/externals/gh_icon.png
vendored
Normal file
BIN
runelite-client/src/main/resources/com/openosrs/client/plugins/openosrs/externals/gh_icon.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 580 B |
Reference in New Issue
Block a user