From f1f8688e44bc074e81c8ecc3fbe1a149de0162a1 Mon Sep 17 00:00:00 2001 From: Mark Vainomaa Date: Wed, 18 Jan 2017 04:17:32 +0200 Subject: [PATCH] Add Fake Plugin factory --- .../bukkit/fakeplugin/AbstractFakePlugin.java | 84 +++++++++++++++++++ .../bukkit/fakeplugin/FakePluginFactory.java | 80 ++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 BukkitUtils/src/main/java/eu/mikroskeem/utils/bukkit/fakeplugin/AbstractFakePlugin.java create mode 100644 BukkitUtils/src/main/java/eu/mikroskeem/utils/bukkit/fakeplugin/FakePluginFactory.java diff --git a/BukkitUtils/src/main/java/eu/mikroskeem/utils/bukkit/fakeplugin/AbstractFakePlugin.java b/BukkitUtils/src/main/java/eu/mikroskeem/utils/bukkit/fakeplugin/AbstractFakePlugin.java new file mode 100644 index 0000000..1e0b7df --- /dev/null +++ b/BukkitUtils/src/main/java/eu/mikroskeem/utils/bukkit/fakeplugin/AbstractFakePlugin.java @@ -0,0 +1,84 @@ +package eu.mikroskeem.utils.bukkit.fakeplugin; + +import lombok.Getter; +import lombok.experimental.Delegate; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.TabCompleter; +import org.bukkit.event.Event; +import org.bukkit.event.Listener; +import org.bukkit.plugin.*; + +import java.io.File; +import java.io.StringReader; +import java.util.Map; +import java.util.Set; +import java.util.StringJoiner; +import java.util.regex.Pattern; + +public abstract class AbstractFakePlugin extends PluginBase implements PluginLoader { + public final Plugin loadPlugin(File file) throws InvalidPluginException, UnknownDependencyException { + throw new UnsupportedOperationException("Not supported yet."); + } + + public final PluginDescriptionFile getPluginDescription(File file) throws InvalidDescriptionException { + throw new UnsupportedOperationException("Not supported yet."); + } + + public Pattern[] getPluginFileFilters() { + return new Pattern[0]; + } + + public Map, Set> createRegisteredListeners(Listener listener, Plugin plugin) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void enablePlugin(Plugin plugin) {} + public void disablePlugin(Plugin plugin) {} + + @Override public boolean isEnabled() { return true; } + public PluginLoader getPluginLoader() + { + return this; + } + + private interface Excludes { + PluginLoader getPluginLoader(); + PluginDescriptionFile getDescription(); + String getName(); + boolean isEnabled(); + } + + /* Forward most calls to parent plugin */ + @Delegate(excludes = AbstractFakePlugin.Excludes.class, types = { + CommandExecutor.class, TabCompleter.class, Plugin.class + }) + private final Plugin plugin; + + @Getter private PluginDescriptionFile description; + public AbstractFakePlugin(Plugin plugin, + String pluginName, + String pluginVersion, + String mainClass, + String descriptionStr, + String author){ + this.plugin = plugin; + + /* Generate plugin description file */ + StringJoiner sj = new StringJoiner("\n"); + sj.add(String.format("name: %s", pluginName)); + sj.add(String.format("version: %s", pluginVersion==null?"1.0-STUB":pluginVersion)); + sj.add(String.format("description: %s", descriptionStr)); + sj.add(String.format("author: %s", author)); + sj.add(String.format("main: %s", mainClass)); + + try { + description = new PluginDescriptionFile(new StringReader(sj.toString())); + } catch (InvalidDescriptionException e){ + e.printStackTrace(); + } + } + + @Override public String toString() { + return description.getFullName(); + } +} diff --git a/BukkitUtils/src/main/java/eu/mikroskeem/utils/bukkit/fakeplugin/FakePluginFactory.java b/BukkitUtils/src/main/java/eu/mikroskeem/utils/bukkit/fakeplugin/FakePluginFactory.java new file mode 100644 index 0000000..d8e0515 --- /dev/null +++ b/BukkitUtils/src/main/java/eu/mikroskeem/utils/bukkit/fakeplugin/FakePluginFactory.java @@ -0,0 +1,80 @@ +package eu.mikroskeem.utils.bukkit.fakeplugin; + +import eu.mikroskeem.utils.reflect.Reflect; +import lombok.Getter; +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.SimplePluginManager; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import static com.google.common.base.Preconditions.checkNotNull; + +public class FakePluginFactory { + private final List fakePlugins = new ArrayList<>(); + + private final Plugin plugin; + private final List plugins; + @Getter private final Map lookupNames; + private final boolean isPaper; + + @SuppressWarnings("unchecked") + public FakePluginFactory(Plugin parentPlugin){ + this.plugin = parentPlugin; + Field lookupNamesField, pluginsField; + if(plugin.getServer().getPluginManager().getClass() != SimplePluginManager.class){ + throw new RuntimeException("Server PluginManager is not SimplePluginManager"); + } + isPaper = Reflect.getMethod(Bukkit.class, "getTPS") != null; + try { + pluginsField = checkNotNull(Reflect.getField( + SimplePluginManager.class, + "plugins")); + lookupNamesField = checkNotNull(Reflect.getField( + SimplePluginManager.class, + "lookupNames")); + plugins = (List) checkNotNull(Reflect.readField(pluginsField, + plugin.getServer().getPluginManager())); + lookupNames = (Map) checkNotNull(Reflect.readField(lookupNamesField, + plugin.getServer().getPluginManager())); + } catch (NullPointerException e){ + throw new RuntimeException("Failed to get 'plugins' and 'lookupNames' field!"); + } + } + + @SuppressWarnings("unchecked") + public synchronized T newPlugin(String name, String version, String mainClass){ + T fakePlugin = (T) new AbstractFakePlugin(plugin, name, version, mainClass, null, null){}; + fakePlugins.add(fakePlugin); + return fakePlugin; + } + + public synchronized void addPlugin(T plugin){ + /* Note: took me 5 hours to realize lookupNames map keys are lower case (on PaperSpigot) + * Note 2: Took me another 5 hours to realize lower case lookupNames keys are *only* on PaperSpigot + */ + String lookupName = isPaper?plugin.getName().toLowerCase(Locale.ENGLISH):plugin.getName(); + lookupNames.put(lookupName, plugin); + plugins.add(plugin); + fakePlugins.add(plugin); + } + + public synchronized void removePlugin(T plugin){ + if (plugins.contains(plugin)) plugins.remove(plugin); + if (lookupNames.containsKey(plugin.getName())) { + String lookupName = isPaper?plugin.getName().toLowerCase(Locale.ENGLISH):plugin.getName(); + lookupNames.remove(lookupName); + } + if (fakePlugins.contains(plugin)) fakePlugins.remove(plugin); + } + + /* Note: Not needed, SimplePluginManager handles plugin removal already + synchronized void removeAllPlugins() { + fakePlugins.forEach(this::removePlugin); + } + */ +} \ No newline at end of file