mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00:00
Implement Event Management and move Plugins to utilize that
Instead of plugins being the focus Geyser instead implements an Event Management system that can be used internally. Plugins are then able to utilize this through the convenience of an external jar and registering annotated classes.
This commit is contained in:
parent
c88469e322
commit
ef20c0fa3a
12 changed files with 405 additions and 168 deletions
|
@ -35,6 +35,7 @@ import org.geysermc.common.AuthType;
|
||||||
import org.geysermc.common.PlatformType;
|
import org.geysermc.common.PlatformType;
|
||||||
import org.geysermc.connector.bootstrap.GeyserBootstrap;
|
import org.geysermc.connector.bootstrap.GeyserBootstrap;
|
||||||
import org.geysermc.connector.command.CommandManager;
|
import org.geysermc.connector.command.CommandManager;
|
||||||
|
import org.geysermc.connector.event.EventManager;
|
||||||
import org.geysermc.connector.metrics.Metrics;
|
import org.geysermc.connector.metrics.Metrics;
|
||||||
import org.geysermc.connector.network.ConnectorServerEventHandler;
|
import org.geysermc.connector.network.ConnectorServerEventHandler;
|
||||||
import org.geysermc.connector.network.remote.RemoteServer;
|
import org.geysermc.connector.network.remote.RemoteServer;
|
||||||
|
@ -50,8 +51,8 @@ import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||||
import org.geysermc.connector.network.translators.effect.EffectRegistry;
|
import org.geysermc.connector.network.translators.effect.EffectRegistry;
|
||||||
import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator;
|
import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator;
|
||||||
import org.geysermc.connector.plugin.PluginManager;
|
import org.geysermc.connector.plugin.PluginManager;
|
||||||
import org.geysermc.connector.plugin.events.DisableEvent;
|
import org.geysermc.connector.event.events.DisableEvent;
|
||||||
import org.geysermc.connector.plugin.events.EnableEvent;
|
import org.geysermc.connector.event.events.EnableEvent;
|
||||||
import org.geysermc.connector.utils.DimensionUtils;
|
import org.geysermc.connector.utils.DimensionUtils;
|
||||||
import org.geysermc.connector.utils.DockerCheck;
|
import org.geysermc.connector.utils.DockerCheck;
|
||||||
import org.geysermc.connector.utils.LocaleUtils;
|
import org.geysermc.connector.utils.LocaleUtils;
|
||||||
|
@ -92,6 +93,7 @@ public class GeyserConnector {
|
||||||
private PlatformType platformType;
|
private PlatformType platformType;
|
||||||
private GeyserBootstrap bootstrap;
|
private GeyserBootstrap bootstrap;
|
||||||
|
|
||||||
|
private final EventManager eventManager;
|
||||||
private final PluginManager pluginManager;
|
private final PluginManager pluginManager;
|
||||||
|
|
||||||
private Metrics metrics;
|
private Metrics metrics;
|
||||||
|
@ -102,6 +104,7 @@ public class GeyserConnector {
|
||||||
instance = this;
|
instance = this;
|
||||||
|
|
||||||
this.bootstrap = bootstrap;
|
this.bootstrap = bootstrap;
|
||||||
|
this.eventManager = new EventManager(this);
|
||||||
this.pluginManager = new PluginManager(this, new File(bootstrap.getDataFolder(), "plugins"));
|
this.pluginManager = new PluginManager(this, new File(bootstrap.getDataFolder(), "plugins"));
|
||||||
|
|
||||||
GeyserLogger logger = bootstrap.getGeyserLogger();
|
GeyserLogger logger = bootstrap.getGeyserLogger();
|
||||||
|
@ -163,7 +166,7 @@ public class GeyserConnector {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trigger all plugins Enable Events
|
// Trigger all plugins Enable Events
|
||||||
pluginManager.triggerEvent(new EnableEvent());
|
eventManager.triggerEvent(new EnableEvent());
|
||||||
|
|
||||||
double completeTime = (System.currentTimeMillis() - startupTime) / 1000D;
|
double completeTime = (System.currentTimeMillis() - startupTime) / 1000D;
|
||||||
logger.info(String.format("Done (%ss)! Run /geyser help for help!", new DecimalFormat("#.###").format(completeTime)));
|
logger.info(String.format("Done (%ss)! Run /geyser help for help!", new DecimalFormat("#.###").format(completeTime)));
|
||||||
|
@ -174,7 +177,7 @@ public class GeyserConnector {
|
||||||
shuttingDown = true;
|
shuttingDown = true;
|
||||||
|
|
||||||
// Trigger all plugins Disable Events
|
// Trigger all plugins Disable Events
|
||||||
pluginManager.triggerEvent(new DisableEvent());
|
eventManager.triggerEvent(new DisableEvent());
|
||||||
|
|
||||||
if (players.size() >= 1) {
|
if (players.size() >= 1) {
|
||||||
bootstrap.getGeyserLogger().info("Kicking " + players.size() + " player(s)");
|
bootstrap.getGeyserLogger().info("Kicking " + players.size() + " player(s)");
|
||||||
|
|
|
@ -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.event;
|
||||||
|
|
||||||
|
public interface EventContext {
|
||||||
|
void unregister();
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* 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.event;
|
||||||
|
|
||||||
|
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.geysermc.connector.event.events.GeyserEvent;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class EventHandler<T extends GeyserEvent> implements Comparator<EventHandler<T>>, Comparable<EventHandler<T>> {
|
||||||
|
public final Class<? extends T> eventClass;
|
||||||
|
public final Executor<T> executor;
|
||||||
|
public final int priority;
|
||||||
|
public final boolean ignoreCancelled;
|
||||||
|
|
||||||
|
public void execute(EventContext ctx, T event) {
|
||||||
|
executor.run(ctx, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(EventHandler<T> left, EventHandler<T> right) {
|
||||||
|
return left.getPriority() - right.getPriority();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(EventHandler<T> other) {
|
||||||
|
return getPriority() - other.getPriority();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Executor<T extends GeyserEvent> {
|
||||||
|
void run(EventContext ctx, T event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
public static final class PRIORITY {
|
||||||
|
public final static int LOWEST = 10;
|
||||||
|
public final static int LOW = 4;
|
||||||
|
public final static int NORMAL = 50;
|
||||||
|
public final static int HIGH = 60;
|
||||||
|
public final static int HIGHEST = 90;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,141 @@
|
||||||
|
/*
|
||||||
|
* 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.event;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.geysermc.connector.GeyserConnector;
|
||||||
|
import org.geysermc.connector.event.events.CancellableGeyserEvent;
|
||||||
|
import org.geysermc.connector.event.events.GeyserEvent;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.PriorityQueue;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class EventManager {
|
||||||
|
private final Map<Class<? extends GeyserEvent>, PriorityQueue<EventHandler<? extends GeyserEvent>>> eventHandlers = new HashMap<>();
|
||||||
|
|
||||||
|
private final GeyserConnector connector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<? extends GeyserEvent> handler : eventHandlers.get(event.getClass())) {
|
||||||
|
Context ctx = new Context(this, handler);
|
||||||
|
|
||||||
|
//noinspection unchecked
|
||||||
|
((EventHandler<GeyserEvent>)handler).execute(ctx, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<? extends GeyserEvent> handler : eventHandlers.get(event.getClass())) {
|
||||||
|
if (!cancelled || !handler.isIgnoreCancelled()) {
|
||||||
|
Context ctx = new Context(this, handler);
|
||||||
|
|
||||||
|
//noinspection unchecked
|
||||||
|
((EventHandler<GeyserEvent>) handler).execute(ctx, event);
|
||||||
|
|
||||||
|
cancelled = event.isCancelled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new EventHandler using an Executor
|
||||||
|
*/
|
||||||
|
public <T extends GeyserEvent> EventHandler<T> on(Class<? extends T> cls, EventHandler.Executor<T> executor, int priority, boolean ignoreCancelled) {
|
||||||
|
EventHandler<T> handler = new EventHandler<>(cls, executor, priority, ignoreCancelled);
|
||||||
|
register(handler);
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends GeyserEvent> EventHandler<T> on(Class<? extends T> cls, EventHandler.Executor<T> executor) {
|
||||||
|
return on(cls, executor, EventHandler.PRIORITY.NORMAL, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends GeyserEvent> EventHandler<T> on(Class<? extends T> cls, EventHandler.Executor<T> executor, boolean ignoreCancelled) {
|
||||||
|
return on(cls, executor, EventHandler.PRIORITY.NORMAL, ignoreCancelled);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends GeyserEvent> EventHandler<T> on(Class<? extends T> cls, EventHandler.Executor<T> executor, int priority) {
|
||||||
|
return on(cls, executor, priority, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an EventHandler
|
||||||
|
*/
|
||||||
|
public void register(EventHandler<?> handler) {
|
||||||
|
if (!eventHandlers.containsKey(handler.getEventClass())) {
|
||||||
|
eventHandlers.put(handler.getEventClass(), new PriorityQueue<>());
|
||||||
|
}
|
||||||
|
eventHandlers.get(handler.getEventClass()).add(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister an EventHandler
|
||||||
|
*/
|
||||||
|
public void unregister(EventHandler<?> handler) {
|
||||||
|
if (eventHandlers.containsKey(handler.getEventClass())) {
|
||||||
|
eventHandlers.get(handler.getEventClass()).remove(handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
public static class Context implements EventContext {
|
||||||
|
private final EventManager manager;
|
||||||
|
private final EventHandler<?> handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister an EventHandler from within an Event execution
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void unregister() {
|
||||||
|
manager.unregister(handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,7 +24,7 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.connector.plugin.api;
|
package org.geysermc.connector.event.events;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
|
@ -24,10 +24,7 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.connector.plugin.events;
|
package org.geysermc.connector.event.events;
|
||||||
|
|
||||||
import lombok.ToString;
|
|
||||||
import org.geysermc.connector.plugin.api.GeyserEvent;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DisableEvent is triggered for each plugin when disabling it
|
* DisableEvent is triggered for each plugin when disabling it
|
|
@ -24,9 +24,7 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.connector.plugin.events;
|
package org.geysermc.connector.event.events;
|
||||||
|
|
||||||
import org.geysermc.connector.plugin.api.CancellableGeyserEvent;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EnableEvent is triggered for each plugin when enabling it
|
* EnableEvent is triggered for each plugin when enabling it
|
|
@ -24,7 +24,7 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.connector.plugin.api;
|
package org.geysermc.connector.event.events;
|
||||||
|
|
||||||
public abstract class GeyserEvent {
|
public abstract class GeyserEvent {
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
/*
|
||||||
|
* 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.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.geysermc.connector.event.EventHandler;
|
||||||
|
import org.geysermc.connector.event.events.GeyserEvent;
|
||||||
|
import org.geysermc.connector.plugin.annotations.Event;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All GeyserPlugins extend from this
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public abstract class GeyserPlugin {
|
||||||
|
private final Map<Object, ArrayList<EventHandler<?>>> classEventHandlers = new HashMap<>();
|
||||||
|
|
||||||
|
private final PluginManager pluginManager;
|
||||||
|
private final PluginClassLoader pluginClassLoader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register all Events contained in a class
|
||||||
|
*/
|
||||||
|
public void registerEvents(Object obj) {
|
||||||
|
for (Method method : obj.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() != 2 || !GeyserEvent.class.isAssignableFrom(method.getParameters()[1].getType())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//noinspection unchecked
|
||||||
|
EventHandler<?> handler = pluginManager.getConnector().getEventManager()
|
||||||
|
.on((Class<? extends GeyserEvent>)method.getParameters()[1].getType(), (ctx, event) -> {
|
||||||
|
try {
|
||||||
|
method.invoke(obj, ctx, event);
|
||||||
|
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!classEventHandlers.containsKey(obj.getClass())) {
|
||||||
|
classEventHandlers.put(obj, new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
classEventHandlers.get(obj).add(handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister events in class
|
||||||
|
*/
|
||||||
|
public void unregisterEvents(Object obj) {
|
||||||
|
if (!classEventHandlers.containsKey(obj)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (EventHandler<?> handler : classEventHandlers.get(obj)) {
|
||||||
|
pluginManager.getConnector().getEventManager().unregister(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
classEventHandlers.remove(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister all events for a plugin
|
||||||
|
*/
|
||||||
|
public void unregisterPluginEvents(Class<?> plugin) {
|
||||||
|
for (Object obj : classEventHandlers.keySet()) {
|
||||||
|
unregisterEvents(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -52,16 +52,16 @@ public class PluginClassLoader extends URLClassLoader {
|
||||||
private final Map<String, Class<?>> classes = new ConcurrentHashMap<>();
|
private final Map<String, Class<?>> classes = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private final Object plugin;
|
private final Class<? extends GeyserPlugin> pluginClass;
|
||||||
|
|
||||||
PluginClassLoader(PluginManager pluginManager, ClassLoader parent, File pluginFile) throws IOException, InvalidPluginClassLoaderException {
|
PluginClassLoader(PluginManager pluginManager, ClassLoader parent, File pluginFile) throws IOException, InvalidPluginClassLoaderException {
|
||||||
super(new URL[] {pluginFile.toURI().toURL()}, parent);
|
super(new URL[] {pluginFile.toURI().toURL()}, parent);
|
||||||
|
|
||||||
this.jar = new JarFile(pluginFile);
|
this.jar = new JarFile(pluginFile);
|
||||||
this.pluginManager = pluginManager;
|
this.pluginManager = pluginManager;
|
||||||
this.plugin = findPlugin();
|
this.pluginClass = findPlugin();
|
||||||
|
|
||||||
if (this.plugin == null) {
|
if (this.pluginClass == null) {
|
||||||
throw new InvalidPluginClassLoaderException("Unable to find class annotated by @Plugin");
|
throw new InvalidPluginClassLoaderException("Unable to find class annotated by @Plugin");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ public class PluginClassLoader extends URLClassLoader {
|
||||||
/**
|
/**
|
||||||
* Find first class annotated by @Plugin
|
* Find first class annotated by @Plugin
|
||||||
*/
|
*/
|
||||||
private Object findPlugin() {
|
private Class<? extends GeyserPlugin> findPlugin() {
|
||||||
for (Enumeration<JarEntry> entries = jar.entries(); entries.hasMoreElements();) {
|
for (Enumeration<JarEntry> entries = jar.entries(); entries.hasMoreElements();) {
|
||||||
JarEntry entry = entries.nextElement();
|
JarEntry entry = entries.nextElement();
|
||||||
if (!entry.getName().endsWith(".class")) {
|
if (!entry.getName().endsWith(".class")) {
|
||||||
|
@ -83,9 +83,10 @@ public class PluginClassLoader extends URLClassLoader {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cls.getAnnotation(Plugin.class) != null) {
|
if (GeyserPlugin.class.isAssignableFrom(cls) && cls.getAnnotation(Plugin.class) != null) {
|
||||||
cacheClass(cls, true);
|
cacheClass(cls, true);
|
||||||
return cls;
|
//noinspection unchecked
|
||||||
|
return (Class<? extends GeyserPlugin>) cls;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -98,7 +99,7 @@ public class PluginClassLoader extends URLClassLoader {
|
||||||
classes.put(cls.getName(), cls);
|
classes.put(cls.getName(), cls);
|
||||||
|
|
||||||
if (global) {
|
if (global) {
|
||||||
pluginManager.getPluginClasses().put(cls.getName(), cls);
|
pluginManager.getGlobalClasses().put(cls.getName(), cls);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,11 +158,11 @@ public class PluginClassLoader extends URLClassLoader {
|
||||||
return classes.get(name);
|
return classes.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean global = plugin.getClass().getAnnotation(Plugin.class).global();
|
boolean global = pluginClass.getAnnotation(Plugin.class).global();
|
||||||
|
|
||||||
// Now try load from global if permitted
|
// Now try load from global if permitted
|
||||||
if (global) {
|
if (global) {
|
||||||
Class<?> cls = pluginManager.getPluginClasses().get(name);
|
Class<?> cls = pluginManager.getGlobalClasses().get(name);
|
||||||
if (cls != null) {
|
if (cls != null) {
|
||||||
return cls;
|
return cls;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,26 +29,19 @@ package org.geysermc.connector.plugin;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
import org.geysermc.connector.GeyserConnector;
|
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.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.PriorityQueue;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles 3rd party plugins for Geyser
|
* Handles 3rd party plugins for Geyser and will hook into our Event system using annotations
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
@ToString
|
@ToString
|
||||||
|
@ -57,9 +50,8 @@ public class PluginManager {
|
||||||
private final GeyserConnector connector;
|
private final GeyserConnector connector;
|
||||||
private final File pluginPath;
|
private final File pluginPath;
|
||||||
|
|
||||||
private final Map<String, Class<?>> pluginClasses = new ConcurrentHashMap<>();
|
private final Map<String, Class<?>> globalClasses = new ConcurrentHashMap<>();
|
||||||
private final List<PluginClassLoader> loaders = new ArrayList<>();
|
private final List<GeyserPlugin> plugins = new ArrayList<>();
|
||||||
private final Map<Class<?>, PriorityQueue<EventHandler>> eventHandlers = new HashMap<>();
|
|
||||||
|
|
||||||
public PluginManager(GeyserConnector connector, File pluginPath) {
|
public PluginManager(GeyserConnector connector, File pluginPath) {
|
||||||
this.connector = connector;
|
this.connector = connector;
|
||||||
|
@ -70,15 +62,16 @@ public class PluginManager {
|
||||||
/**
|
/**
|
||||||
* Load all plugins in the defined pluginPath
|
* Load all plugins in the defined pluginPath
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||||
public void loadPlugins() {
|
public void loadPlugins() {
|
||||||
pluginPath.mkdirs();
|
pluginPath.mkdirs();
|
||||||
for (File entry : pluginPath.listFiles()) {
|
for (File entry : Objects.requireNonNull(pluginPath.listFiles())) {
|
||||||
if (entry.isDirectory() || !entry.getName().toLowerCase().endsWith(".jar")) {
|
if (entry.isDirectory() || !entry.getName().toLowerCase().endsWith(".jar")) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
loadPlugin(entry);
|
loadPlugin(entry);
|
||||||
} catch (IOException | PluginClassLoader.InvalidPluginClassLoaderException e) {
|
} catch (IOException | PluginManagerException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,139 +80,40 @@ public class PluginManager {
|
||||||
/**
|
/**
|
||||||
* Load a specific plugin and register its events
|
* Load a specific plugin and register its events
|
||||||
*/
|
*/
|
||||||
private void loadPlugin(File pluginFile) throws IOException, PluginClassLoader.InvalidPluginClassLoaderException {
|
private void loadPlugin(File pluginFile) throws IOException, PluginManagerException {
|
||||||
if (!pluginFile.exists()) {
|
if (!pluginFile.exists()) {
|
||||||
throw new FileNotFoundException(String.format("%s does not exist", pluginFile.getName()));
|
throw new FileNotFoundException(String.format("%s does not exist", pluginFile.getName()));
|
||||||
}
|
}
|
||||||
PluginClassLoader loader = new PluginClassLoader(this, getClass().getClassLoader(), pluginFile);
|
|
||||||
registerEvents(loader.getPlugin(), loader.getPlugin());
|
PluginClassLoader loader = null;
|
||||||
loaders.add(loader);
|
try {
|
||||||
|
loader = new PluginClassLoader(this, getClass().getClassLoader(), pluginFile);
|
||||||
|
} catch (PluginClassLoader.InvalidPluginClassLoaderException e) {
|
||||||
|
throw new PluginManagerException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
GeyserPlugin plugin;
|
||||||
|
|
||||||
|
try {
|
||||||
|
plugin = loader.getPluginClass().getConstructor(PluginManager.class, PluginClassLoader.class).newInstance(this, loader);
|
||||||
|
} catch (InstantiationException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
|
||||||
|
throw new PluginManagerException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins.add(plugin);
|
||||||
|
plugin.registerEvents(plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
public static class PluginManagerException extends Exception {
|
||||||
* 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
|
public PluginManagerException(String message, Throwable ex) {
|
||||||
if (eventAnnotation == null) {
|
super(message, ex);
|
||||||
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<Class<?>, PriorityQueue<EventHandler>> entry : eventHandlers.entrySet()) {
|
|
||||||
List<EventHandler> 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<Class<?>, PriorityQueue<EventHandler>> entry : eventHandlers.entrySet()) {
|
|
||||||
List<EventHandler> 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())) {
|
public PluginManagerException(Throwable ex) {
|
||||||
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<EventHandler> {
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
|
|
||||||
package org.geysermc.connector.plugin.annotations;
|
package org.geysermc.connector.plugin.annotations;
|
||||||
|
|
||||||
import lombok.Getter;
|
import org.geysermc.connector.event.EventHandler;
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
|
@ -43,16 +43,8 @@ import java.lang.annotation.Target;
|
||||||
@Target({ElementType.METHOD})
|
@Target({ElementType.METHOD})
|
||||||
public @interface Event {
|
public @interface Event {
|
||||||
// Events are processed from lowest to highest priority
|
// Events are processed from lowest to highest priority
|
||||||
int priority() default 50;
|
int priority() default EventHandler.PRIORITY.NORMAL;
|
||||||
|
|
||||||
// If ignoreCancelled is true then the handler will not be executed
|
// If ignoreCancelled is true then the handler will not be executed
|
||||||
boolean ignoreCancelled() default false;
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue