From c88469e322a240a97b3e62564064b18dd7b200db Mon Sep 17 00:00:00 2001 From: Brendan Grieve Date: Fri, 5 Jun 2020 13:02:36 +0800 Subject: [PATCH] Initial Plugin System Implemented --- .gitignore | 1 + .../platform/sponge/GeyserSpongePlugin.java | 5 + .../standalone/GeyserStandaloneBootstrap.java | 5 + .../velocity/GeyserVelocityPlugin.java | 8 +- .../geysermc/connector/GeyserConnector.java | 13 + .../connector/bootstrap/GeyserBootstrap.java | 9 + .../connector/plugin/PluginClassLoader.java | 193 +++++++++++++++ .../connector/plugin/PluginManager.java | 225 ++++++++++++++++++ .../connector/plugin/annotations/Event.java | 58 +++++ .../connector/plugin/annotations/Plugin.java | 45 ++++ .../plugin/api/CancellableGeyserEvent.java | 38 +++ .../connector/plugin/api/GeyserEvent.java | 31 +++ .../connector/plugin/events/DisableEvent.java | 36 +++ .../connector/plugin/events/EnableEvent.java | 38 +++ 14 files changed, 703 insertions(+), 2 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/plugin/PluginClassLoader.java create mode 100644 connector/src/main/java/org/geysermc/connector/plugin/PluginManager.java create mode 100644 connector/src/main/java/org/geysermc/connector/plugin/annotations/Event.java create mode 100644 connector/src/main/java/org/geysermc/connector/plugin/annotations/Plugin.java create mode 100644 connector/src/main/java/org/geysermc/connector/plugin/api/CancellableGeyserEvent.java create mode 100644 connector/src/main/java/org/geysermc/connector/plugin/api/GeyserEvent.java create mode 100644 connector/src/main/java/org/geysermc/connector/plugin/events/DisableEvent.java create mode 100644 connector/src/main/java/org/geysermc/connector/plugin/events/EnableEvent.java diff --git a/.gitignore b/.gitignore index 0af6ecd05..89a0a98e3 100644 --- a/.gitignore +++ b/.gitignore @@ -227,3 +227,4 @@ config.yml logs/ public-key.pem locales/ +plugins/ diff --git a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java index d226add77..44ab5548f 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java @@ -147,6 +147,11 @@ public class GeyserSpongePlugin implements GeyserBootstrap { return geyserSpongePingPassthrough; } + @Override + public File getDataFolder() { + return configDir; + } + @Listener public void onServerStart(GameStartedServerEvent event) { onEnable(); diff --git a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java index aa0d2392e..5ebd1eeaa 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java @@ -100,4 +100,9 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap { public IGeyserPingPassthrough getGeyserPingPassthrough() { return geyserPingPassthrough; } + + @Override + public File getDataFolder() { + return new File(System.getProperty("user.dir")); + } } diff --git a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityPlugin.java b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityPlugin.java index e7b44da53..2f986013a 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityPlugin.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityPlugin.java @@ -68,11 +68,10 @@ public class GeyserVelocityPlugin implements GeyserBootstrap { private IGeyserPingPassthrough geyserPingPassthrough; private GeyserConnector connector; + private final File configDir = new File("plugins/" + GeyserConnector.NAME + "-Velocity/"); @Override public void onEnable() { - File configDir = new File("plugins/" + GeyserConnector.NAME + "-Velocity/"); - try { if (!configDir.exists()) configDir.mkdir(); @@ -134,6 +133,11 @@ public class GeyserVelocityPlugin implements GeyserBootstrap { return geyserPingPassthrough; } + @Override + public File getDataFolder() { + return configDir; + } + @Subscribe public void onInit(ProxyInitializeEvent event) { onEnable(); diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java index 02e0c5003..25d92f4ad 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -49,11 +49,15 @@ import org.geysermc.connector.network.translators.world.WorldManager; import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.network.translators.effect.EffectRegistry; import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator; +import org.geysermc.connector.plugin.PluginManager; +import org.geysermc.connector.plugin.events.DisableEvent; +import org.geysermc.connector.plugin.events.EnableEvent; import org.geysermc.connector.utils.DimensionUtils; import org.geysermc.connector.utils.DockerCheck; import org.geysermc.connector.utils.LocaleUtils; import org.geysermc.connector.network.translators.sound.SoundRegistry; +import java.io.File; import java.net.InetSocketAddress; import java.text.DecimalFormat; import java.util.HashMap; @@ -88,6 +92,8 @@ public class GeyserConnector { private PlatformType platformType; private GeyserBootstrap bootstrap; + private final PluginManager pluginManager; + private Metrics metrics; private GeyserConnector(PlatformType platformType, GeyserBootstrap bootstrap) { @@ -96,6 +102,7 @@ public class GeyserConnector { instance = this; this.bootstrap = bootstrap; + this.pluginManager = new PluginManager(this, new File(bootstrap.getDataFolder(), "plugins")); GeyserLogger logger = bootstrap.getGeyserLogger(); GeyserConfiguration config = bootstrap.getGeyserConfig(); @@ -155,6 +162,9 @@ public class GeyserConnector { metrics.addCustomChart(new Metrics.SimplePie("platform", platformType::getPlatformName)); } + // Trigger all plugins Enable Events + pluginManager.triggerEvent(new EnableEvent()); + double completeTime = (System.currentTimeMillis() - startupTime) / 1000D; logger.info(String.format("Done (%ss)! Run /geyser help for help!", new DecimalFormat("#.###").format(completeTime))); } @@ -163,6 +173,9 @@ public class GeyserConnector { bootstrap.getGeyserLogger().info("Shutting down Geyser."); shuttingDown = true; + // Trigger all plugins Disable Events + pluginManager.triggerEvent(new DisableEvent()); + if (players.size() >= 1) { bootstrap.getGeyserLogger().info("Kicking " + players.size() + " player(s)"); diff --git a/connector/src/main/java/org/geysermc/connector/bootstrap/GeyserBootstrap.java b/connector/src/main/java/org/geysermc/connector/bootstrap/GeyserBootstrap.java index 24ce81cfd..bb7e054cd 100644 --- a/connector/src/main/java/org/geysermc/connector/bootstrap/GeyserBootstrap.java +++ b/connector/src/main/java/org/geysermc/connector/bootstrap/GeyserBootstrap.java @@ -33,6 +33,8 @@ import org.geysermc.connector.command.CommandManager; import org.geysermc.connector.network.translators.world.CachedChunkManager; import org.geysermc.connector.network.translators.world.WorldManager; +import java.io.File; + public interface GeyserBootstrap { CachedChunkManager DEFAULT_CHUNK_MANAGER = new CachedChunkManager(); @@ -83,4 +85,11 @@ public interface GeyserBootstrap { default WorldManager getWorldManager() { return DEFAULT_CHUNK_MANAGER; } + + /** + * Return the data folder where files get stored + * + * @return File location of data folder + */ + File getDataFolder(); } diff --git a/connector/src/main/java/org/geysermc/connector/plugin/PluginClassLoader.java b/connector/src/main/java/org/geysermc/connector/plugin/PluginClassLoader.java new file mode 100644 index 000000000..52b789eaa --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/plugin/PluginClassLoader.java @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + * + */ + +package org.geysermc.connector.plugin; + +import com.google.common.io.ByteStreams; +import lombok.Getter; +import org.geysermc.connector.plugin.annotations.Plugin; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Enumeration; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +/** + * ClassLoader for Plugins + * + * If a plugin is marked as shared then its libraries will be available to other plugins. + */ +public class PluginClassLoader extends URLClassLoader { + private final PluginManager pluginManager; + private final JarFile jar; + private final Map> classes = new ConcurrentHashMap<>(); + + @Getter + private final Object plugin; + + PluginClassLoader(PluginManager pluginManager, ClassLoader parent, File pluginFile) throws IOException, InvalidPluginClassLoaderException { + super(new URL[] {pluginFile.toURI().toURL()}, parent); + + this.jar = new JarFile(pluginFile); + this.pluginManager = pluginManager; + this.plugin = findPlugin(); + + if (this.plugin == null) { + throw new InvalidPluginClassLoaderException("Unable to find class annotated by @Plugin"); + } + } + + /** + * Find first class annotated by @Plugin + */ + private Object findPlugin() { + for (Enumeration entries = jar.entries(); entries.hasMoreElements();) { + JarEntry entry = entries.nextElement(); + if (!entry.getName().endsWith(".class")) { + continue; + } + + Class cls; + try { + cls = loadFromJar(entry); + } catch (ClassNotFoundException ignored) { + continue; + } + + if (cls.getAnnotation(Plugin.class) != null) { + cacheClass(cls, true); + return cls; + } + } + return null; + } + + /** + * Cache class locally and if required, globally as well + */ + private void cacheClass(Class cls, boolean global) { + classes.put(cls.getName(), cls); + + if (global) { + pluginManager.getPluginClasses().put(cls.getName(), cls); + } + } + + /** + * Load a classfile from the jar + */ + private Class loadFromJar(String classPath) throws ClassNotFoundException { + JarEntry entry = jar.getJarEntry(classPath); + + if (entry == null) { + throw new ClassNotFoundException(classPath); + } + + return loadFromJar(entry); + } + + /** + * Load a classfile from the jar + */ + private Class loadFromJar(JarEntry entry) throws ClassNotFoundException { + byte[] classBytes; + + try { + try (InputStream is = jar.getInputStream(entry)) { + classBytes = ByteStreams.toByteArray(is); + } + } catch (IOException e) { + throw new ClassNotFoundException(entry.getName(), e); + } + + String packageName = getPackageName(entry.getName()); + if (packageName != null && getPackageName(packageName) == null) { + definePackage(packageName, null, null, null, null, null, null, null); + } + + return defineClass( + entry.getName().replace('/', '.').substring(0,entry.getName().length()-6), + classBytes, + 0, + classBytes.length + ); + } + + private String getPackageName(String className) { + int i = className.lastIndexOf('.'); + if (i != -1) { + return className.substring(0, i); + } + return null; + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + // First try load it from our cache + if (classes.containsKey(name)) { + return classes.get(name); + } + + boolean global = plugin.getClass().getAnnotation(Plugin.class).global(); + + // Now try load from global if permitted + if (global) { + Class cls = pluginManager.getPluginClasses().get(name); + if (cls != null) { + return cls; + } + } + + // Try load from our jar + try { + String classPath = name.replace('.','/').concat(".class"); + Class cls = loadFromJar(classPath); + cacheClass(cls, global); + return cls; + } catch (ClassNotFoundException ignored) { + } + + // Try load from parent + Class cls = super.findClass(name); + + if (cls != null) { + cacheClass(cls, global); + } + return cls; + } + + public static class InvalidPluginClassLoaderException extends Exception { + InvalidPluginClassLoaderException(String msg) { + super(msg); + } + } +} diff --git a/connector/src/main/java/org/geysermc/connector/plugin/PluginManager.java b/connector/src/main/java/org/geysermc/connector/plugin/PluginManager.java new file mode 100644 index 000000000..4e42880c7 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/plugin/PluginManager.java @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + * + */ + +package org.geysermc.connector.plugin; + +import lombok.Getter; +import lombok.ToString; +import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.plugin.annotations.Event; +import org.geysermc.connector.plugin.api.CancellableGeyserEvent; +import org.geysermc.connector.plugin.api.GeyserEvent; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.PriorityQueue; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Handles 3rd party plugins for Geyser + */ +@Getter +@ToString +public class PluginManager { + + private final GeyserConnector connector; + private final File pluginPath; + + private final Map> pluginClasses = new ConcurrentHashMap<>(); + private final List loaders = new ArrayList<>(); + private final Map, PriorityQueue> eventHandlers = new HashMap<>(); + + public PluginManager(GeyserConnector connector, File pluginPath) { + this.connector = connector; + this.pluginPath = pluginPath; + loadPlugins(); + } + + /** + * Load all plugins in the defined pluginPath + */ + public void loadPlugins() { + pluginPath.mkdirs(); + for (File entry : pluginPath.listFiles()) { + if (entry.isDirectory() || !entry.getName().toLowerCase().endsWith(".jar")) { + continue; + } + try { + loadPlugin(entry); + } catch (IOException | PluginClassLoader.InvalidPluginClassLoaderException e) { + e.printStackTrace(); + } + } + } + + /** + * Load a specific plugin and register its events + */ + private void loadPlugin(File pluginFile) throws IOException, PluginClassLoader.InvalidPluginClassLoaderException { + if (!pluginFile.exists()) { + throw new FileNotFoundException(String.format("%s does not exist", pluginFile.getName())); + } + PluginClassLoader loader = new PluginClassLoader(this, getClass().getClassLoader(), pluginFile); + registerEvents(loader.getPlugin(), loader.getPlugin()); + loaders.add(loader); + } + + + /** + * Register all Events contained in class + */ + public void registerEvents(Object plugin, Object cls) { + for (Method method : cls.getClass().getMethods()) { + Event eventAnnotation = method.getAnnotation(Event.class); + + // Check that the method is annotated with @Event + if (eventAnnotation == null) { + continue; + } + + // Make sure it only has a single Event parameter + if (method.getParameterCount() != 1 || !GeyserEvent.class.isAssignableFrom(method.getParameters()[0].getType())) { + continue; + } + + if (!eventHandlers.containsKey(method.getParameters()[0].getType())) { + eventHandlers.put(method.getParameters()[0].getType(), new PriorityQueue<>()); + } + + eventHandlers.get(method.getParameters()[0].getType()).add( + new EventHandler( + plugin, + cls, + method, + eventAnnotation + ) + ); + } + } + + /** + * Unregister events in class + */ + public void unregisterEvents(Class cls) { + for (Map.Entry, PriorityQueue> entry : eventHandlers.entrySet()) { + List remove = new ArrayList<>(); + for (EventHandler handler : entry.getValue()) { + if (handler.cls == cls) { + remove.add(handler); + } + } + entry.getValue().removeAll(remove); + } + } + + /** + * Unregister all events for a plugin + */ + public void unregisterPluginEvents(Class plugin) { + for (Map.Entry, PriorityQueue> entry : eventHandlers.entrySet()) { + List remove = new ArrayList<>(); + for (EventHandler handler : entry.getValue()) { + if (handler.plugin == plugin) { + remove.add(handler); + } + } + entry.getValue().removeAll(remove); + } + } + + /** + * Trigger a new event + * + * This will be executed with all registered handlers in order of priority + */ + public void triggerEvent(GeyserEvent event) { + if (!eventHandlers.containsKey(event.getClass())) { + return; + } + + for (EventHandler handler : eventHandlers.get(event.getClass())) { + try { + handler.method.invoke(handler.cls, event); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + } + } + + /** + * Trigger a new cancellable event + * + * This will be executed with all registered handlers in order of priority ignoring those who + * do not wish to process events that are cancelled + */ + public void triggerEvent(CancellableGeyserEvent event) { + if (!eventHandlers.containsKey(event.getClass())) { + return; + } + + boolean cancelled = event.isCancelled(); + for (EventHandler handler : eventHandlers.get(event.getClass())) { + if (cancelled && handler.annotation.ignoreCancelled()) { + continue; + } + + try { + handler.method.invoke(handler.cls, event); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + cancelled = event.isCancelled(); + } + } + + static class EventHandler implements Comparator { + Object plugin; + Object cls; + Method method; + Event annotation; + + EventHandler(Object plugin, Object cls, Method method, Event annotation) { + this.plugin = plugin; + this.cls = cls; + this.method = method; + this.annotation = annotation; + } + + @Override + public int compare(EventHandler left, EventHandler right) { + return left.annotation.priority() - right.annotation.priority(); + } + } +} diff --git a/connector/src/main/java/org/geysermc/connector/plugin/annotations/Event.java b/connector/src/main/java/org/geysermc/connector/plugin/annotations/Event.java new file mode 100644 index 000000000..b024fc184 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/plugin/annotations/Event.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + * + */ + +package org.geysermc.connector.plugin.annotations; + +import lombok.Getter; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Event Methods must be decorated with this annotation to receive events. + * + * The class the method belongs to must also be registered with the plugin manager + * unless it is already decorated with the @Plugin annotation + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD}) +public @interface Event { + // Events are processed from lowest to highest priority + int priority() default 50; + + // If ignoreCancelled is true then the handler will not be executed + boolean ignoreCancelled() default false; + + class PRIORITY { + public static int LOWEST = 10; + public static int LOW = 40; + public static int NORMAL = 50; + public static int HIGH = 60; + public static int HIGHEST = 90; + } +} diff --git a/connector/src/main/java/org/geysermc/connector/plugin/annotations/Plugin.java b/connector/src/main/java/org/geysermc/connector/plugin/annotations/Plugin.java new file mode 100644 index 000000000..0ec76db72 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/plugin/annotations/Plugin.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + * + */ + +package org.geysermc.connector.plugin.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * A plugin main class must be decorated with this annotation + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +public @interface Plugin { + String name(); + String version(); + String[] authors(); + String description(); + boolean global() default true; +} diff --git a/connector/src/main/java/org/geysermc/connector/plugin/api/CancellableGeyserEvent.java b/connector/src/main/java/org/geysermc/connector/plugin/api/CancellableGeyserEvent.java new file mode 100644 index 000000000..fdc4cf0d5 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/plugin/api/CancellableGeyserEvent.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + * + */ + +package org.geysermc.connector.plugin.api; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@Getter +@ToString +public abstract class CancellableGeyserEvent extends GeyserEvent { + @Setter + private boolean cancelled = false; +} diff --git a/connector/src/main/java/org/geysermc/connector/plugin/api/GeyserEvent.java b/connector/src/main/java/org/geysermc/connector/plugin/api/GeyserEvent.java new file mode 100644 index 000000000..0e8e2f1f6 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/plugin/api/GeyserEvent.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + * + */ + +package org.geysermc.connector.plugin.api; + +public abstract class GeyserEvent { + +} diff --git a/connector/src/main/java/org/geysermc/connector/plugin/events/DisableEvent.java b/connector/src/main/java/org/geysermc/connector/plugin/events/DisableEvent.java new file mode 100644 index 000000000..f41def665 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/plugin/events/DisableEvent.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + * + */ + +package org.geysermc.connector.plugin.events; + +import lombok.ToString; +import org.geysermc.connector.plugin.api.GeyserEvent; + +/** + * DisableEvent is triggered for each plugin when disabling it + */ +public class DisableEvent extends GeyserEvent { +} diff --git a/connector/src/main/java/org/geysermc/connector/plugin/events/EnableEvent.java b/connector/src/main/java/org/geysermc/connector/plugin/events/EnableEvent.java new file mode 100644 index 000000000..a3242f4aa --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/plugin/events/EnableEvent.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + * + */ + +package org.geysermc.connector.plugin.events; + +import org.geysermc.connector.plugin.api.CancellableGeyserEvent; + +/** + * EnableEvent is triggered for each plugin when enabling it + * + * If the event is cancelled then the plugin will be unregistered from the EventManager and the DisableEvent will + * not trigger + */ +public class EnableEvent extends CancellableGeyserEvent { +}