001/* 002 * Trident - A Multithreaded Server Alternative 003 * Copyright 2014 The TridentSDK Team 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package net.tridentsdk.server.event; 018 019import com.esotericsoftware.reflectasm.MethodAccess; 020import com.google.common.collect.Collections2; 021import com.google.common.collect.ForwardingCollection; 022import com.google.common.collect.HashMultimap; 023import com.google.common.collect.Lists; 024import net.tridentsdk.Trident; 025import net.tridentsdk.docs.InternalUseOnly; 026import net.tridentsdk.event.*; 027import net.tridentsdk.plugin.Plugin; 028import net.tridentsdk.plugin.annotation.IgnoreRegistration; 029import net.tridentsdk.server.concurrent.TickSync; 030 031import java.lang.reflect.Method; 032import java.util.*; 033import java.util.concurrent.ConcurrentHashMap; 034import java.util.concurrent.ConcurrentMap; 035import java.util.concurrent.ConcurrentSkipListSet; 036import java.util.concurrent.CountDownLatch; 037import java.util.function.Function; 038 039/** 040 * The server's event handler, should only be created once, and only once by the server only 041 * <p> 042 * <p>To access this handler, use this code: 043 * <pre><code> 044 * Events handler = Registered.events(); 045 * </code></pre></p> 046 * 047 * @author The TridentSDK Team 048 * @since 0.3-alpha-DP 049 */ 050public class EventHandler extends ForwardingCollection<EventNotifier> implements Events { 051 private static final Comparator<ReflectNotifier> COMPARATOR = new ReflectNotifier(null, null, 0, null, null, null); 052 private static final Function<Class<?>, Set<ReflectNotifier>> CREATE_QUEUE = (k) -> 053 new ConcurrentSkipListSet<>(COMPARATOR); 054 055 private final ConcurrentMap<Class<? extends Event>, Set<ReflectNotifier>> callers = new ConcurrentHashMap<>(); 056 private final ConcurrentMap<Class<?>, MethodAccess> accessors = new ConcurrentHashMap<>(); 057 058 private EventHandler() { 059 if (!Trident.isTrident()) { 060 throw new RuntimeException(new IllegalAccessException("EventManager must be initiated by TridentSDK!")); 061 } 062 } 063 064 /** 065 * Creates a new event handler, should only be used internally 066 * <p> 067 * <p>To access this handler, use this code: 068 * <pre><code> 069 * Events handler = Registered.events(); 070 * </code></pre></p> 071 * 072 * @return the new event handler 073 */ 074 @InternalUseOnly 075 public static Events create() { 076 return new EventHandler(); 077 } 078 079 private HashMultimap<Class<? extends Event>, ReflectNotifier> reflectorsFrom(Plugin plugin, Listener listener, 080 final Class<?> c) { 081 MethodAccess access = accessors.computeIfAbsent(c, (k) -> MethodAccess.get(c)); 082 083 Method[] methods = c.getDeclaredMethods(); 084 085 HashMultimap<Class<? extends Event>, ReflectNotifier> map = HashMultimap.create(11, 11); 086 for (Method method : methods) { 087 Class<?>[] parameterTypes = method.getParameterTypes(); 088 if (parameterTypes.length != 1) 089 continue; 090 091 Class<?> type = parameterTypes[0]; 092 093 if (!Event.class.isAssignableFrom(type) || !method.isAnnotationPresent(IgnoreRegistration.class)) 094 continue; 095 096 Class<? extends Event> eventClass = type.asSubclass(Event.class); 097 ListenerOpts handler = method.getAnnotation(ListenerOpts.class); 098 Importance importance = handler == null ? Importance.MEDIUM : handler.importance(); 099 100 ReflectNotifier registeredListener = new ReflectNotifier(access, plugin, access.getIndex(method.getName()), 101 listener, eventClass, importance); 102 map.get(eventClass).add(registeredListener); 103 } 104 105 return map; 106 } 107 108 @Override 109 public void fire(final Event event) { 110 final Set<ReflectNotifier> listeners = callers.get(event.getClass()); 111 if (listeners == null) return; 112 113 final CountDownLatch latch = new CountDownLatch(1); 114 115 TickSync.sync(() -> { 116 for (ReflectNotifier listener : listeners) { 117 listener.handle(event); 118 } 119 120 latch.countDown(); 121 }); 122 123 // Setting of event state happens-before counting down 124 // therefore event state need not be volatile 125 try { 126 latch.await(); 127 } catch (InterruptedException e) { 128 e.printStackTrace(); 129 } 130 } 131 132 @Override 133 @InternalUseOnly 134 public void registerListener(Plugin plugin, Listener listener) { 135 final Class<?> c = listener.getClass(); 136 HashMultimap<Class<? extends Event>, ReflectNotifier> reflectors = reflectorsFrom(plugin, listener, c); 137 138 for (Class<? extends Event> eventClass : reflectors.keySet()) { 139 Set<ReflectNotifier> eventCallers = callers.computeIfAbsent(eventClass, CREATE_QUEUE); 140 eventCallers.addAll(reflectors.get(eventClass)); 141 } 142 } 143 144 @Override 145 public void unregister(Class<? extends Listener> cls) { 146 for (Map.Entry<Class<? extends Event>, Set<ReflectNotifier>> entry : this.callers.entrySet()) { 147 for (Iterator<ReflectNotifier> iterator = entry.getValue().iterator(); iterator.hasNext(); ) { 148 ReflectNotifier it = iterator.next(); 149 if (it.listener().getClass().equals(cls)) { 150 iterator.remove(); 151 break; 152 } 153 } 154 } 155 } 156 157 @Override 158 protected Collection<EventNotifier> delegate() { 159 List<EventNotifier> reflectors = Lists.newArrayList(); 160 callers.values().forEach(q -> reflectors.addAll(Collections2.transform(q, e -> (EventNotifier) e))); 161 162 return reflectors; 163 } 164}