Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@aikar
Created September 10, 2018 01:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aikar/f9566353d9b63dee35d71f544dd97050 to your computer and use it in GitHub Desktop.
Save aikar/f9566353d9b63dee35d71f544dd97050 to your computer and use it in GitHub Desktop.
/*
* Copyright (c) 2016. Starlis LLC / dba Empire Minecraft
*
* This source code is proprietary software and must not be redistributed without Starlis LLC's approval
*
*/
package com.empireminecraft.util;
import com.empireminecraft.EmpirePlugin;
import com.empireminecraft.api.meta.Meta;
import com.empireminecraft.api.meta.MetaKey.TempKey;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import lombok.AllArgsConstructor;
import org.bukkit.Bukkit;
import org.bukkit.entity.Entity;
import org.bukkit.event.Event;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityEvent;
import org.bukkit.event.player.PlayerEvent;
import org.bukkit.event.vehicle.VehicleEnterEvent;
import org.bukkit.event.vehicle.VehicleExitEvent;
import org.bukkit.plugin.EventExecutor;
import org.bukkit.plugin.PluginManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class EntityEvents extends BukkitUtil.Listener {
private static final Set<Class<? extends Event>> registeredEvents = new HashSet<>();
private static final Set<? extends Class<? extends Event>> OTHER_EVENTS =
Sets.newHashSet(VehicleEnterEvent.class, VehicleExitEvent.class);
@AllArgsConstructor
private static class ListenerInfo {
Class<? extends Event> checkClass;
Method method;
}
private static final Multimap<Class<?>, ListenerInfo> cachedListeners = ArrayListMultimap.create();
private static TempKey getMetaKey(final Class clazz) {
return Meta.createTempKey("__EntityEvent_" + clazz.getName());
}
public static void registerEntityListener(final Entity entity, final Listener scope) {
final PluginManager pluginManager = Bukkit.getPluginManager();
final EmpirePlugin empire = EmpirePlugin.getEmpire();
Collection<ListenerInfo> methodListeners = getListeners(scope);
for (ListenerInfo info: methodListeners) {
Class<? extends Event> checkClass = info.checkClass;
Method method = info.method;
final TempKey metaKey = getMetaKey(checkClass);
List<EntityListener> listeners = Meta.getEntityMeta(entity, metaKey);
if (listeners == null) {
listeners = new ArrayList<>();
Meta.setEntityMeta(entity, metaKey, listeners);
}
final EntityListener listener = getEntityListener(scope, checkClass, method);
if (listener == null) {
continue;
}
listeners.add(listener);
if (!registeredEvents.contains(checkClass)) {
EventExecutor executer = getEventExecutor(checkClass, metaKey, listener);
pluginManager.registerEvent(checkClass, scope, EventPriority.HIGHEST, executer, empire, true);
registeredEvents.add(checkClass);
}
}
}
@Nullable
private static EntityListener getEntityListener(Listener scope, Class<? extends Event> checkClass, Method method) {
final EntityListener listener;
if (EntityEvent.class.isAssignableFrom(checkClass)) {
listener = new EntityEventListener(scope, method);
} else if (PlayerEvent.class.isAssignableFrom(checkClass)) {
listener = new PlayerEventListener(scope, method);
} else if (checkClass == VehicleEnterEvent.class) {
listener = new VehicleEnteredListener(scope, method);
} else if (checkClass == VehicleExitEvent.class) {
listener = new VehicleExitedListener(scope, method);
} else {
Log.severe("how did this happen?");
return null;
}
return listener;
}
@NotNull
private static EventExecutor getEventExecutor(Class<? extends Event> checkClass, TempKey metaKey, EntityListener listener) {
return (scope1, event) -> {
if (!checkClass.isAssignableFrom(event.getClass())) {
return;
}
final Entity ent = listener.getEntity(event);
List<EntityListener> listeners1 = Meta.getEntityMeta(ent, metaKey);
if (listeners1 != null) {
for (EntityListener entityListener: listeners1) {
entityListener.call(event);
}
}
};
}
private static Collection<ListenerInfo> getListeners(Listener scope) {
final Class<?> clazz = scope.getClass();
Collection<ListenerInfo> listeners = cachedListeners.get(clazz);
if (cachedListeners.containsKey(clazz)) {
return listeners;
}
Set<Method> methods = new HashSet<>(Arrays.asList(clazz.getMethods()));
methods.addAll(Arrays.asList(clazz.getDeclaredMethods()));
for (final Method method: methods) {
if (method.isAnnotationPresent(EventHandler.class)) {
method.setAccessible(true);
final Class<? extends Event> checkClass;
final Class<?>[] types = method.getParameterTypes();
boolean fail = false;
if (types.length != 1) {
fail = true;
checkClass = null;
} else {
checkClass = (Class<? extends Event>) types[0];
if (!EntityEvent.class.isAssignableFrom(checkClass) &&
!PlayerEvent.class.isAssignableFrom(checkClass) &&
!OTHER_EVENTS.contains(checkClass)) {
fail = true;
}
}
if (fail) {
Log.severe(clazz.getName() + " Invalid EntityEventHandler method signature \"" +
method.toGenericString() + "\"");
continue;
}
listeners.add(new ListenerInfo(checkClass, method));
}
}
return listeners;
}
static class EntityEventListener extends EntityListener<EntityEvent> {
EntityEventListener(Listener scope, Method method) {
super(scope, method);
}
@Override
Entity getEntity(EntityEvent event) {
return event.getEntity();
}
}
static class PlayerEventListener extends EntityListener<PlayerEvent> {
PlayerEventListener(Listener scope, Method method) {super(scope, method);}
@Override
Entity getEntity(PlayerEvent event) {
return event.getPlayer();
}
}
static class VehicleEnteredListener extends EntityListener<VehicleEnterEvent> {
VehicleEnteredListener(Listener scope, Method method) {
super(scope, method);
}
@Override
Entity getEntity(VehicleEnterEvent event) {
return event.getEntered();
}
}
static class VehicleExitedListener extends EntityListener<VehicleExitEvent> {
VehicleExitedListener(Listener scope, Method method) {
super(scope, method);
}
@Override
Entity getEntity(VehicleExitEvent event) {
return event.getExited();
}
}
abstract static class EntityListener<T extends Event> {
final Listener scope;
final Method method;
EntityListener(Listener scope, Method method) {
this.scope = scope;
this.method = method;
}
abstract Entity getEntity(T event);
void call(Event event) {
try {
method.invoke(scope, event);
} catch (Exception ex) {
Log.exception("Method " + scope.getClass().getName() +
":" +method.getName(), ex);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment