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}