NoCheatPlus/NCPCore/src/main/java/fr/neatmonster/nocheatplus/event/mini/EventRegistryBukkit.java

240 lines
9.1 KiB
Java
Raw Normal View History

2018-01-17 23:52:50 +01:00
/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
[BLEEDING][BREAKING] Use a new internal event registry. The old ListenerManager is removed, new system in place. Removed doesManageListeners(). (The new system isn't that new, but it's been fixed and adapted to using RegistrationOrder.) New * Register all Bukkit events via the new EventRegistryBukkit. * This way listeners can be ordered by numeric priority and tags (regular expressions for beforeTag and afterTag). * Unregistering listeners is possible (a listener node stays in the Bukkit registry, but only one per event). * It's possible to add listeners with minimal impact (MiniListener). * The registry registers by event class 'hard' i.e., no relations between already registered classes are checked. * Order isn't necessarily stable nor even reproducible for randomized start conditions with the same elements. Point * Compatibility hooks can easily place event listeners before/after/between NCP default listeners, without resorting to tricks like 'load-before'. * Future registry of NCP itself: unregistering listeners is necessary for runtime-removable checks, order is useful if not necessary, to be able to add check listeners at any point of time. Breaks: * Anything relying on the previous (optional) managelisteners feature. Missing: * Lots of testing/inspection. * Ensure all NCP listeners are coming with name/tag at least. * Provide meaningful tags/RegistrationOrder for fine grained access (e.g. after feature.moving but before feature.inventory). * Change cncp to hard depend on NCP and use listener priorities.
2018-01-16 22:19:18 +01:00
package fr.neatmonster.nocheatplus.event.mini;
import java.lang.reflect.Method;
import java.util.Collection;
import org.bukkit.Bukkit;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.EventException;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.server.PluginDisableEvent;
import org.bukkit.plugin.EventExecutor;
import org.bukkit.plugin.Plugin;
import fr.neatmonster.nocheatplus.components.registry.feature.ComponentWithName;
import fr.neatmonster.nocheatplus.components.registry.order.RegistrationOrder;
/**
* A MultiListenerRegistry that registers Bukkit types with a Spigot/CraftBukkit
* server.
*
* <br>
* Listener registration for {@link org.bukkit.event.Listener}, checking methods
* for {@link org.bukkit.event.EventHandler}. <br>
* <br>
* Supports passing a defaultOrder, as well as the per-class annotation
* {@link fr.neatmonster.nocheatplus.components.registry.order.RegistrationOrder.RegisterEventsWithOrder},
* and the per-method annotation
* {@link fr.neatmonster.nocheatplus.components.registry.order.RegistrationOrder.RegisterMethodWithOrder}.
* <br>
* Priority (FCFS): RegisterMethodWithOrder, RegisterEventsWithOrder,
* defaultOrder
*
* <br>
* <br>
* For alternatives and more details and conventions see:
* {@link fr.neatmonster.nocheatplus.event.mini.MiniListenerRegistry}<br>
*
*
* @author asofold
*
*/
public class EventRegistryBukkit extends MultiListenerRegistry<Event, EventPriority> {
/**
* Node for events that implement the Cancellable interface (Bukkit).
*
* @author asofold
*
* @param <E>
*/
protected static class CancellableNodeBukkit<E> extends MiniListenerNode<E, EventPriority> {
public CancellableNodeBukkit(EventPriority basePriority) {
super(basePriority);
}
// TODO: Future java: E extends Cancellable ?
@Override
protected boolean isCancelled(E event) {
return ((Cancellable) event).isCancelled();
}
}
private final Plugin plugin;
/**
* Pass this listener with each event registration to the
* {@link org.bukkit.plugin.PluginManager}.
*/
private final Listener dummyListener = new Listener() {}; // TODO: Get from NCP ?
public EventRegistryBukkit(Plugin plugin) {
this.plugin = plugin;
nodeFactory = new NodeFactory<Event, EventPriority>() {
@Override
public <E extends Event> MiniListenerNode<E, EventPriority> newNode(Class<E> eventClass, EventPriority basePriority) {
if (Cancellable.class.isAssignableFrom(eventClass)) {
// TODO: Check if order is right (eventClass extends Cancellable).
// TODO: Future java (see above) ?
return new CancellableNodeBukkit<E>(basePriority);
} else {
return new MiniListenerNode<E, EventPriority>(basePriority);
}
}
};
// Auto register for plugin disable.
// TODO: Ensure the ignoreCancelled setting is correct (do listeners really not unregister if the event is cancelled).
register(new MiniListener<PluginDisableEvent>() {
@Override
public void onEvent(PluginDisableEvent event) {
unregisterAttached(event.getPlugin());
}
}, EventPriority.MONITOR, new RegistrationOrder("nocheatplus.system.registry", null, ".*"), true);
}
@Override
protected <E extends Event> void registerNode(final Class<E> eventClass,
final MiniListenerNode<E, EventPriority> node, final EventPriority basePriority) {
Bukkit.getPluginManager().registerEvent(eventClass,
dummyListener,
basePriority, new EventExecutor() {
@SuppressWarnings("unchecked")
@Override
public void execute(Listener dummy, Event event) throws EventException {
if (eventClass.isAssignableFrom(event.getClass())) {
node.onEvent((E) event);
}
}
}, plugin, false);
}
/**
* Convenience method to have a listener unregister with disabling a plugin.
*
* @param listener
* Do not call with a plugin class being the listener, use the
* other register method instead!
* @param plugin
*/
public void register(Listener listener, Plugin plugin) {
register(listener, null, plugin);
}
/**
* Convenience method to have a listener unregister with disabling a certain
* other plugin.
*
* @param listener
* Do not call with a plugin class being the listener, use the
* other register method instead!
* @param defaultOrder
* @param plugin
* @see {@link #register(Listener, RegistrationOrder)}
*/
public void register(Listener listener, RegistrationOrder defaultOrder, Plugin plugin) {
attach(internalRegister(listener, defaultOrder), plugin);
}
/**
* Register the given listener similar to
* {@link org.bukkit.plugin.PluginManager#registerEvents(Listener, Plugin)}.
* <br>
* All events are registered for the plugin that was passed upon creation of
* this registry (supposedly NoCheatPlus).
*
* @param listener
* @see {@link #register(Listener, RegistrationOrder)}
*/
public void register(Listener listener) {
register(listener, (RegistrationOrder) null);
}
/**
* Register the given listener similar to
* {@link org.bukkit.plugin.PluginManager#registerEvents(Listener, Plugin)}.
* <br>
* All events are registered for the plugin that was passed upon creation of
* this registry (supposedly NoCheatPlus). Methods are selected by presence
* of the annotation {@link org.bukkit.event.EventHandler}.
* <hr>
* Supports passing a defaultOrder, as well as the per-class annotation
* {@link fr.neatmonster.nocheatplus.components.registry.order.RegistrationOrder.RegisterEventsWithOrder},
* and the per-method annotation
* {@link fr.neatmonster.nocheatplus.components.registry.order.RegistrationOrder.RegisterMethodWithOrder}.
* <br>
* Priority (FCFS): RegisterMethodWithOrder, RegisterEventsWithOrder,
* defaultOrder
*
* <br>
* <br>
* If no order is given, but the listener implements ComponentWithName, a
* RegistrationOrder with the component name as tag is created. <br>
* <br>
* <br>
* For alternatives and more details and conventions see:
* {@link fr.neatmonster.nocheatplus.event.mini.MiniListenerRegistry}<br>
*
* @param listener
* @param defaultOrder
*/
public void register(Listener listener, RegistrationOrder defaultOrder) {
internalRegister(listener, defaultOrder);
}
private Collection<MiniListener<? extends Event>> internalRegister(Listener listener,
RegistrationOrder defaultOrder) {
// Note: default ignoreCancelled and priority should have no effect, as EventHandler sets the defaults anyway.
// NCP for convenience: tag by component name, if no order is given.
if (defaultOrder == null && listener instanceof ComponentWithName) {
defaultOrder = new RegistrationOrder(((ComponentWithName) listener).getComponentName());
}
return super.register((Object) listener, EventPriority.NORMAL, defaultOrder, false);
}
@Override
protected boolean shouldBeEventHandler(Method method) {
return method.getAnnotation(EventHandler.class) != null;
}
@Override
protected boolean getIgnoreCancelled(Method method, boolean defaultIgnoreCancelled) {
EventHandler info = method.getAnnotation(EventHandler.class);
if (info == null) {
return defaultIgnoreCancelled;
}
else {
return info.ignoreCancelled();
}
}
@Override
protected EventPriority getPriority(Method method, EventPriority defaultPriority) {
EventHandler info = method.getAnnotation(EventHandler.class);
if (info == null) {
return defaultPriority;
}
else {
return info.priority();
}
}
}