Created
September 10, 2018 01:30
-
-
Save aikar/f9566353d9b63dee35d71f544dd97050 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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