package cpw.mods.fml.common.eventhandler; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import javax.annotation.Nonnull; import org.apache.logging.log4j.Level; import com.google.common.base.Preconditions; import com.google.common.base.Throwables; import com.google.common.collect.MapMaker; import com.google.common.reflect.TypeToken; import cpw.mods.fml.common.FMLLog; import cpw.mods.fml.common.Loader; import cpw.mods.fml.common.ModContainer; public class EventBus implements IEventExceptionHandler { private static int maxID = 0; private ConcurrentHashMap<Object, ArrayList<IEventListener>> listeners = new ConcurrentHashMap<Object, ArrayList<IEventListener>>(); private Map<Object,ModContainer> listenerOwners = new MapMaker().weakKeys().weakValues().makeMap(); private final int busID = maxID++; private IEventExceptionHandler exceptionHandler; public EventBus() { ListenerList.resize(busID + 1); exceptionHandler = this; } public EventBus(@Nonnull IEventExceptionHandler handler) { this(); Preconditions.checkArgument(handler != null, "EventBus exception handler can not be null"); exceptionHandler = handler; } public void register(Object target) { if (listeners.containsKey(target)) { return; } ModContainer activeModContainer = Loader.instance().activeModContainer(); if (activeModContainer == null) { FMLLog.log(Level.ERROR, new Throwable(), "Unable to determine registrant mod for %s. This is a critical error and should be impossible", target); activeModContainer = Loader.instance().getMinecraftModContainer(); } listenerOwners.put(target, activeModContainer); Set<? extends Class<?>> supers = TypeToken.of(target.getClass()).getTypes().rawTypes(); for (Method method : target.getClass().getMethods()) { for (Class<?> cls : supers) { try { Method real = cls.getDeclaredMethod(method.getName(), method.getParameterTypes()); if (real.isAnnotationPresent(SubscribeEvent.class)) { Class<?>[] parameterTypes = method.getParameterTypes(); if (parameterTypes.length != 1) { throw new IllegalArgumentException( "Method " + method + " has @SubscribeEvent annotation, but requires " + parameterTypes.length + " arguments. Event handler methods must require a single argument." ); } Class<?> eventType = parameterTypes[0]; if (!Event.class.isAssignableFrom(eventType)) { throw new IllegalArgumentException("Method " + method + " has @SubscribeEvent annotation, but takes a argument that is not an Event " + eventType); } register(eventType, target, method, activeModContainer); break; } } catch (NoSuchMethodException e) { ; } } } } private void register(Class<?> eventType, Object target, Method method, ModContainer owner) { try { Constructor<?> ctr = eventType.getConstructor(); ctr.setAccessible(true); Event event = (Event)ctr.newInstance(); ASMEventHandler listener = new ASMEventHandler(target, method, owner); event.getListenerList().register(busID, listener.getPriority(), listener); ArrayList<IEventListener> others = listeners.get(target); if (others == null) { others = new ArrayList<IEventListener>(); listeners.put(target, others); } others.add(listener); } catch (Exception e) { e.printStackTrace(); } } public void unregister(Object object) { ArrayList<IEventListener> list = listeners.remove(object); for (IEventListener listener : list) { ListenerList.unregisterAll(busID, listener); } } public boolean post(Event event) { IEventListener[] listeners = event.getListenerList().getListeners(busID); int index = 0; try { for (; index < listeners.length; index++) { listeners[index].invoke(event); } } catch (Throwable throwable) { exceptionHandler.handleException(this, event, listeners, index, throwable); Throwables.propagate(throwable); } return (event.isCancelable() ? event.isCanceled() : false); } @Override public void handleException(EventBus bus, Event event, IEventListener[] listeners, int index, Throwable throwable) { FMLLog.log(Level.ERROR, throwable, "Exception caught during firing event %s:", event); FMLLog.log(Level.ERROR, "Index: %d Listeners:", index); for (int x = 0; x < listeners.length; x++) { FMLLog.log(Level.ERROR, "%d: %s", x, listeners[x]); } } }