侧边栏壁纸
  • 累计撰写 781 篇文章
  • 累计创建 1 个标签
  • 累计收到 1 条评论
标签搜索
MC

EventBus (注解 解析)

Dettan
2021-07-10 / 0 评论 / 0 点赞 / 119 阅读 / 6,066 字
温馨提示:
本文最后更新于 2022-04-30,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。
我的世界 Forge 的时间总线, 提供注册和 post方法, 注册表示监听某个事件,post表示某个事件发生, 会依次调用监听此事件的方法.

/*
 * Minecraft Forge
 * Copyright (c) 2016-2018.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation version 2.1
 * of the License.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

package net.minecraftforge.fml.common.eventhandler;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.Nonnull;
import net.minecraftforge.fml.common.FMLLog;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.ModContainer;
import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import com.google.common.collect.MapMaker;
import com.google.common.collect.Sets;
import com.google.common.reflect.TypeToken;

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;
private boolean shutdown;

public EventBus()
{
    ListenerList.resize(busID + 1);
    exceptionHandler = this;
}

public EventBus(@Nonnull IEventExceptionHandler handler)
{
    this();
			//先决条件检查, 可能会有性能问题
    Preconditions.checkNotNull(handler, "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.error("Unable to determine registrant mod for {}. This is a critical error and should be impossible", target, new Throwable());
        activeModContainer = Loader.instance().getMinecraftModContainer();
    }
    listenerOwners.put(target, activeModContainer);
    boolean isStatic = target.getClass() == Class.class;
    @SuppressWarnings("unchecked")
    Set<? extends Class<?>> supers = isStatic ? Sets.newHashSet((Class<?>)target) : TypeToken.of(target.getClass()).getTypes().rawTypes();
    for (Method method : (isStatic ? (Class<?>)target : target.getClass()).getMethods())
    {
        if (isStatic && !Modifier.isStatic(method.getModifiers()))
            continue;
        else if (!isStatic && Modifier.isStatic(method.getModifiers()))
            continue;

        for (Class<?> cls : supers)
        {
            try
            {
                Method real = cls.getDeclaredMethod(method.getName(), method.getParameterTypes());
									//检测 method 是不是有 @SubscribeEvent 注解
                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, real, activeModContainer);
                    break;
                }
            }
            catch (NoSuchMethodException e)
            {
                ; // Eat the error, this is not unexpected
            }
        }
    }
}

private void register(Class<?> eventType, Object target, Method method, final ModContainer owner)
{
    try
    {
        Constructor<?> ctr = eventType.getConstructor();
        ctr.setAccessible(true);
        Event event = (Event)ctr.newInstance();
        final ASMEventHandler asm = new ASMEventHandler(target, method, owner, IGenericEvent.class.isAssignableFrom(eventType));

        IEventListener listener = asm;
        if (IContextSetter.class.isAssignableFrom(eventType))
        {
            listener = new IEventListener()
            {
                @Override
                public void invoke(Event event)
                {
                    ModContainer old = Loader.instance().activeModContainer();
                    Loader.instance().setActiveModContainer(owner);
                    ((IContextSetter)event).setModContainer(owner);
                    asm.invoke(event);
                    Loader.instance().setActiveModContainer(old);
                }
            };
        }

        event.getListenerList().register(busID, asm.getPriority(), listener);

        ArrayList<IEventListener> others = listeners.computeIfAbsent(target, k -> new ArrayList<>());
        others.add(listener);
    }
    catch (Exception e)
    {
        FMLLog.log.error("Error registering event handler: {} {} {}", owner, eventType, method, e);
    }
}

public void unregister(Object object)
{
    ArrayList<IEventListener> list = listeners.remove(object);
    if(list == null)
        return;
    for (IEventListener listener : list)
    {
        ListenerList.unregisterAll(busID, listener);
    }
}

public boolean post(Event event)
{
    if (shutdown) return false;

    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.throwIfUnchecked(throwable);
        throw new RuntimeException(throwable);
    }
    return event.isCancelable() && event.isCanceled();
}

public void shutdown()
{
    FMLLog.log.warn("EventBus {} shutting down - future events will not be posted.", busID);
    shutdown = true;
}

@Override
public void handleException(EventBus bus, Event event, IEventListener[] listeners, int index, Throwable throwable)
{
    FMLLog.log.error("Exception caught during firing event {}:", event, throwable);
    FMLLog.log.error("Index: {} Listeners:", index);
    for (int x = 0; x < listeners.length; x++)
    {
        FMLLog.log.error("{}: {}", x, listeners[x]);
    }
}

}

0

评论区