本帖最后由 William_Shi 于 2020-6-28 22:26 编辑
本文纯属Bukkit源码研究
起因是有一个新人问了Bukkit怎么知道某方法是事件监听处理方法的
本文其实就是一个思路的梳理
该从哪里入手?
我们都知道,注册事件监听的过程是PluginManager#registerEvent(或者是events)
https://bukkit.windit.net/javado ... kkit.plugin.Plugin-
那么我们就从PluginManager的实现类入手
其中的注册监听的一个方法如下
首先是检查插件有没有被启用,如果是被disable的插件那就没必要注册事件监听了
之后呢?
Map.Entry<Class<? extends Event>, Set<RegisteredListener>>
这是Map.Entry<K,V>
也就是遍历Map的操作
遍历了plugin.getPluginLoader().createRegisteredListeners(listener, plugin).entrySet()
这又是一个什么Map呢
我们接下来看org.bukkit.plugin.java.JavaPluginLoader
这是PluginLoader的具体实现类
别急,仔细看
Map<Class<? extends Event>, Set<RegisteredListener>> ret = new HashMap<>();
简写return为ret,这是创建map的操作
Method[] publicMethods = listener.getClass().getMethods();
Method[] privateMethods = listener.getClass().getDeclaredMethods();
methods = new HashSet<>(publicMethods.length + privateMethods.length, 1.0F);
说白了就是把你整个实现Listener类的所有方法给存起来了(具体存储的,直接看上面)
接下来最最最最重要的,@Interface,给方法的注解
for (Method method : methods) {
EventHandler eh = method.<EventHandler>getAnnotation(EventHandler.class);
if (eh == null)
continue;
这里就检查了,方法有没有@EventHandler这个注解
如果有,那么再进行下一步添加事件监听的操作,否则就忽略
if ((method.getParameterTypes()).length != 1 || !Event.class.isAssignableFrom(checkClass = method.getParameterTypes()[0])) {
plugin.getLogger().severe(String.valueOf(plugin.getDescription().getFullName()) + " attempted to register an invalid EventHandler method signature \"" + method.toGenericString() + "\" in " + listener.getClass());
continue;
}
这个是什么?这是检查参数的长度
那就可以回答一个问题https://www.mcbbs.net/forum.php? ... 067519&pid=18709678
这个帖子的第二个事件监听
@EventHandler
void onBreakPickaxe(PlayerItemBreakEvent e,Plugin plugin) {
会在register的时候被过滤掉,因为参数列表长度不是1
(Bukkit不可能知道你的奇奇怪怪的参数,他只能给你Event对象,所以必须过滤
不要乱,我们回到起点
for (Map.Entry<Class<? extends Event>, Set<RegisteredListener>> entry : plugin.getPluginLoader().createRegisteredListeners(listener, plugin).entrySet())
getEventListeners(getRegistrationClass(entry.getKey())).registerAll(entry.getValue());
再来看
我们接触到了https://bukkit.windit.net/javado ... nt/HandlerList.html
private HandlerList getEventListeners(@NotNull Class<? extends Event> type)
注意变量名Type
什么意思?每一个事件的类型,都有自己的对应的Handler,
这些Handler,按照事件监听的优先级,存储了事件监听的处理器
当callEvent时,按照优先级,一批一批触发
public class HandlerList {
private final EnumMap<EventPriority, ArrayList<RegisteredListener>> handlerslots;
所以,上面在遍历了Map之后(不要乱掉,仔细思考)
其实就是获取了对应的EventHandler注解的方法的相关参数
得到了事件本身的Class,也就是事件的类型
然后获取这个事件的Handler
最后在这个Handler里面加入事件监听器
当callEvent的时候,就按照优先级去调用
至于@Interface的相关问题 可以看看@蕾米洛伊 的详尽回复
https://www.mcbbs.net/forum.php? ... 067519&pid=18709785
那么现在可能还有一个问题:到底哪里触发了事件监听???
这都是OBC的CraftEventFactory以及具体的NMS类的实现了
举个例子(Spigot1.15.2核心)
EntityItem类(物品堆掉落物实体)128行开始
if (!this.world.isClientSide && this.age >= this.world.spigotConfig.itemDespawnRate) {
if (CraftEventFactory.callItemDespawnEvent(this).isCancelled()) {
this.age = 0;
return;
}
die();
}
起因是有一个新人问了Bukkit怎么知道某方法是事件监听处理方法的
本文其实就是一个思路的梳理
该从哪里入手?
我们都知道,注册事件监听的过程是PluginManager#registerEvent(或者是events)
https://bukkit.windit.net/javado ... kkit.plugin.Plugin-
那么我们就从PluginManager的实现类入手
package org.bukkit.plugin;
public final class SimplePluginManager implements PluginManager
其中的注册监听的一个方法如下
public void registerEvents(@NotNull Listener listener, @NotNull Plugin plugin) {
if (!plugin.isEnabled())
throw new IllegalPluginAccessException("Plugin attempted to register " + listener + " while not enabled");
for (Map.Entry<Class<? extends Event>, Set<RegisteredListener>> entry : plugin.getPluginLoader().createRegisteredListeners(listener, plugin).entrySet())
getEventListeners(getRegistrationClass(entry.getKey())).registerAll(entry.getValue());
}
首先是检查插件有没有被启用,如果是被disable的插件那就没必要注册事件监听了
之后呢?
Map.Entry<Class<? extends Event>, Set<RegisteredListener>>
这是Map.Entry<K,V>
也就是遍历Map的操作
遍历了plugin.getPluginLoader().createRegisteredListeners(listener, plugin).entrySet()
这又是一个什么Map呢
我们接下来看org.bukkit.plugin.java.JavaPluginLoader
这是PluginLoader的具体实现类
@NotNull
public Map<Class<? extends Event>, Set<RegisteredListener>> createRegisteredListeners(@NotNull Listener listener, @NotNull Plugin plugin) {
Set<Method> methods;
Validate.notNull(plugin, "Plugin can not be null");
Validate.notNull(listener, "Listener can not be null");
boolean useTimings = this.server.getPluginManager().useTimings();
Map<Class<? extends Event>, Set<RegisteredListener>> ret = new HashMap<>();
try {
Method[] publicMethods = listener.getClass().getMethods();
Method[] privateMethods = listener.getClass().getDeclaredMethods();
methods = new HashSet<>(publicMethods.length + privateMethods.length, 1.0F);
byte b;
int i;
Method[] arrayOfMethod1;
for (i = (arrayOfMethod1 = publicMethods).length, b = 0; b < i; ) {
final Method method = arrayOfMethod1;
methods.add(method);
b++;
}
for (i = (arrayOfMethod1 = privateMethods).length, b = 0; b < i; ) {
final Method method = arrayOfMethod1;
methods.add(method);
b++;
}
} catch (NoClassDefFoundError e) {
plugin.getLogger().severe("Plugin " + plugin.getDescription().getFullName() + " has failed to register events for " + listener.getClass() + " because " + e.getMessage() + " does not exist.");
return ret;
}
for (Method method : methods) {
EventHandler eh = method.<EventHandler>getAnnotation(EventHandler.class);
if (eh == null)
continue;
if (method.isBridge() || method.isSynthetic())
continue;
Class<?> checkClass;
if ((method.getParameterTypes()).length != 1 || !Event.class.isAssignableFrom(checkClass = method.getParameterTypes()[0])) {
plugin.getLogger().severe(String.valueOf(plugin.getDescription().getFullName()) + " attempted to register an invalid EventHandler method signature \"" + method.toGenericString() + "\" in " + listener.getClass());
continue;
}
final Class<? extends Event> eventClass = checkClass.asSubclass(Event.class);
method.setAccessible(true);
Set<RegisteredListener> eventSet = ret.get(eventClass);
if (eventSet == null) {
eventSet = new HashSet<>();
ret.put(eventClass, eventSet);
}
for (Class<?> clazz = eventClass; Event.class.isAssignableFrom(clazz); clazz = clazz.getSuperclass()) {
if (clazz.getAnnotation(Deprecated.class) != null) {
Warning warning = clazz.<Warning>getAnnotation(Warning.class);
Warning.WarningState warningState = this.server.getWarningState();
if (!warningState.printFor(warning))
break;
plugin.getLogger().log(
Level.WARNING,
String.format(
"\"%s\" has registered a listener for %s on method \"%s\", but the event is Deprecated. \"%s\"; please notify the authors %s.", new Object[] { plugin.getDescription().getFullName(),
clazz.getName(),
method.toGenericString(), (
warning != null && warning.reason().length() != 0) ? warning.reason() : "Server performance will be affected",
Arrays.toString(plugin.getDescription().getAuthors().toArray()) }), (warningState == Warning.WarningState.ON) ? (Throwable)new AuthorNagException(null) : null);
break;
}
}
final CustomTimingsHandler timings = new CustomTimingsHandler("Plugin: " + plugin.getDescription().getFullName() + " Event: " + listener.getClass().getName() + "::" + method.getName() + "(" + eventClass.getSimpleName() + ")", pluginParentTimer);
EventExecutor executor = new EventExecutor() {
public void execute(@NotNull Listener listener, @NotNull Event event) throws EventException {
try {
if (!eventClass.isAssignableFrom(event.getClass()))
return;
boolean isAsync = event.isAsynchronous();
if (!isAsync)
timings.startTiming();
method.invoke(listener, new Object[] { event });
if (!isAsync)
timings.stopTiming();
} catch (InvocationTargetException ex) {
throw new EventException(ex.getCause());
} catch (Throwable t) {
throw new EventException(t);
}
}
};
eventSet.add(new RegisteredListener(listener, executor, eh.priority(), plugin, eh.ignoreCancelled()));
}
return ret;
}
别急,仔细看
Map<Class<? extends Event>, Set<RegisteredListener>> ret = new HashMap<>();
简写return为ret,这是创建map的操作
Method[] publicMethods = listener.getClass().getMethods();
Method[] privateMethods = listener.getClass().getDeclaredMethods();
methods = new HashSet<>(publicMethods.length + privateMethods.length, 1.0F);
说白了就是把你整个实现Listener类的所有方法给存起来了(具体存储的,直接看上面)
接下来最最最最重要的,@Interface,给方法的注解
for (Method method : methods) {
EventHandler eh = method.<EventHandler>getAnnotation(EventHandler.class);
if (eh == null)
continue;
这里就检查了,方法有没有@EventHandler这个注解
如果有,那么再进行下一步添加事件监听的操作,否则就忽略
if ((method.getParameterTypes()).length != 1 || !Event.class.isAssignableFrom(checkClass = method.getParameterTypes()[0])) {
plugin.getLogger().severe(String.valueOf(plugin.getDescription().getFullName()) + " attempted to register an invalid EventHandler method signature \"" + method.toGenericString() + "\" in " + listener.getClass());
continue;
}
这个是什么?这是检查参数的长度
那就可以回答一个问题https://www.mcbbs.net/forum.php? ... 067519&pid=18709678
这个帖子的第二个事件监听
@EventHandler
void onBreakPickaxe(PlayerItemBreakEvent e,Plugin plugin) {
会在register的时候被过滤掉,因为参数列表长度不是1
(Bukkit不可能知道你的奇奇怪怪的参数,他只能给你Event对象,所以必须过滤
不要乱,我们回到起点
for (Map.Entry<Class<? extends Event>, Set<RegisteredListener>> entry : plugin.getPluginLoader().createRegisteredListeners(listener, plugin).entrySet())
getEventListeners(getRegistrationClass(entry.getKey())).registerAll(entry.getValue());
再来看
@NotNull
private HandlerList getEventListeners(@NotNull Class<? extends Event> type) {
try {
Method method = getRegistrationClass(type).getDeclaredMethod("getHandlerList", new Class[0]);
method.setAccessible(true);
return (HandlerList)method.invoke(null, new Object[0]);
} catch (Exception e) {
throw new IllegalPluginAccessException(e.toString());
}
}
@NotNull
private Class<? extends Event> getRegistrationClass(@NotNull Class<? extends Event> clazz) {
try {
clazz.getDeclaredMethod("getHandlerList", new Class[0]);
return clazz;
} catch (NoSuchMethodException e) {
if (clazz.getSuperclass() != null &&
!clazz.getSuperclass().equals(Event.class) &&
Event.class.isAssignableFrom(clazz.getSuperclass()))
return getRegistrationClass(clazz.getSuperclass().asSubclass(Event.class));
throw new IllegalPluginAccessException("Unable to find handler list for event " + clazz.getName() + ". Static getHandlerList method required!");
}
}
我们接触到了https://bukkit.windit.net/javado ... nt/HandlerList.html
private HandlerList getEventListeners(@NotNull Class<? extends Event> type)
注意变量名Type
什么意思?每一个事件的类型,都有自己的对应的Handler,
这些Handler,按照事件监听的优先级,存储了事件监听的处理器
当callEvent时,按照优先级,一批一批触发
public class HandlerList {
private final EnumMap<EventPriority, ArrayList<RegisteredListener>> handlerslots;
所以,上面在遍历了Map之后(不要乱掉,仔细思考)
其实就是获取了对应的EventHandler注解的方法的相关参数
得到了事件本身的Class,也就是事件的类型
然后获取这个事件的Handler
最后在这个Handler里面加入事件监听器
当callEvent的时候,就按照优先级去调用
至于@Interface的相关问题 可以看看@蕾米洛伊 的详尽回复
https://www.mcbbs.net/forum.php? ... 067519&pid=18709785
那么现在可能还有一个问题:到底哪里触发了事件监听???
这都是OBC的CraftEventFactory以及具体的NMS类的实现了
举个例子(Spigot1.15.2核心)
EntityItem类(物品堆掉落物实体)128行开始
if (!this.world.isClientSide && this.age >= this.world.spigotConfig.itemDespawnRate) {
if (CraftEventFactory.callItemDespawnEvent(this).isCancelled()) {
this.age = 0;
return;
}
die();
}
本文纯属Bukkit源码研究
起因是有一个新人问了Bukkit怎么知道某方法是事件监听处理方法的
本文其实就是一个思路的梳理
该从哪里入手?
我们都知道,注册事件监听的过程是PluginManager#registerEvent(或者是events)
https://bukkit.windit.net/javado ... kkit.plugin.Plugin-
那么我们就从PluginManager的实现类入手
其中的注册监听的一个方法如下
首先是检查插件有没有被启用,如果是被disable的插件那就没必要注册事件监听了
之后呢?
Map.Entry<Class<? extends Event>, Set<RegisteredListener>>
这是Map.Entry<K,V>
也就是遍历Map的操作
遍历了plugin.getPluginLoader().createRegisteredListeners(listener, plugin).entrySet()
这又是一个什么Map呢
我们接下来看org.bukkit.plugin.java.JavaPluginLoader
这是PluginLoader的具体实现类
别急,仔细看
Map<Class<? extends Event>, Set<RegisteredListener>> ret = new HashMap<>();
简写return为ret,这是创建map的操作
Method[] publicMethods = listener.getClass().getMethods();
Method[] privateMethods = listener.getClass().getDeclaredMethods();
methods = new HashSet<>(publicMethods.length + privateMethods.length, 1.0F);
说白了就是把你整个实现Listener类的所有方法给存起来了(具体存储的,直接看上面)
接下来最最最最重要的,@Interface,给方法的注解
for (Method method : methods) {
EventHandler eh = method.<EventHandler>getAnnotation(EventHandler.class);
if (eh == null)
continue;
这里就检查了,方法有没有@EventHandler这个注解
如果有,那么再进行下一步添加事件监听的操作,否则就忽略
if ((method.getParameterTypes()).length != 1 || !Event.class.isAssignableFrom(checkClass = method.getParameterTypes()[0])) {
plugin.getLogger().severe(String.valueOf(plugin.getDescription().getFullName()) + " attempted to register an invalid EventHandler method signature \"" + method.toGenericString() + "\" in " + listener.getClass());
continue;
}
这个是什么?这是检查参数的长度
那就可以回答一个问题https://www.mcbbs.net/forum.php? ... 067519&pid=18709678
这个帖子的第二个事件监听
@EventHandler
void onBreakPickaxe(PlayerItemBreakEvent e,Plugin plugin) {
会在register的时候被过滤掉,因为参数列表长度不是1
(Bukkit不可能知道你的奇奇怪怪的参数,他只能给你Event对象,所以必须过滤
不要乱,我们回到起点
for (Map.Entry<Class<? extends Event>, Set<RegisteredListener>> entry : plugin.getPluginLoader().createRegisteredListeners(listener, plugin).entrySet())
getEventListeners(getRegistrationClass(entry.getKey())).registerAll(entry.getValue());
再来看
我们接触到了https://bukkit.windit.net/javado ... nt/HandlerList.html
private HandlerList getEventListeners(@NotNull Class<? extends Event> type)
注意变量名Type
什么意思?每一个事件的类型,都有自己的对应的Handler,
这些Handler,按照事件监听的优先级,存储了事件监听的处理器
当callEvent时,按照优先级,一批一批触发
public class HandlerList {
private final EnumMap<EventPriority, ArrayList<RegisteredListener>> handlerslots;
所以,上面在遍历了Map之后(不要乱掉,仔细思考)
其实就是获取了对应的EventHandler注解的方法的相关参数
得到了事件本身的Class,也就是事件的类型
然后获取这个事件的Handler
最后在这个Handler里面加入事件监听器
当callEvent的时候,就按照优先级去调用
至于@Interface的相关问题 可以看看@蕾米洛伊 的详尽回复
https://www.mcbbs.net/forum.php? ... 067519&pid=18709785
那么现在可能还有一个问题:到底哪里触发了事件监听???
这都是OBC的CraftEventFactory以及具体的NMS类的实现了
举个例子(Spigot1.15.2核心)
EntityItem类(物品堆掉落物实体)128行开始
if (!this.world.isClientSide && this.age >= this.world.spigotConfig.itemDespawnRate) {
if (CraftEventFactory.callItemDespawnEvent(this).isCancelled()) {
this.age = 0;
return;
}
die();
}
2021.12 数据,可能有更多内容
本文纯属Bukkit源码研究起因是有一个新人问了Bukkit怎么知道某方法是事件监听处理方法的
本文其实就是一个思路的梳理
该从哪里入手?
我们都知道,注册事件监听的过程是PluginManager#registerEvent(或者是events)
https://bukkit.windit.net/javado ... kkit.plugin.Plugin-
那么我们就从PluginManager的实现类入手
package org.bukkit.plugin;
public final class SimplePluginManager implements PluginManager
其中的注册监听的一个方法如下
public void registerEvents(@NotNull Listener listener, @NotNull Plugin plugin) {
if (!plugin.isEnabled())
throw new IllegalPluginAccessException("Plugin attempted to register " + listener + " while not enabled");
for (Map.Entry<Class<? extends Event>, Set<RegisteredListener>> entry : plugin.getPluginLoader().createRegisteredListeners(listener, plugin).entrySet())
getEventListeners(getRegistrationClass(entry.getKey())).registerAll(entry.getValue());
}
首先是检查插件有没有被启用,如果是被disable的插件那就没必要注册事件监听了
之后呢?
Map.Entry<Class<? extends Event>, Set<RegisteredListener>>
这是Map.Entry<K,V>
也就是遍历Map的操作
遍历了plugin.getPluginLoader().createRegisteredListeners(listener, plugin).entrySet()
这又是一个什么Map呢
我们接下来看org.bukkit.plugin.java.JavaPluginLoader
这是PluginLoader的具体实现类
@NotNull
public Map<Class<? extends Event>, Set<RegisteredListener>> createRegisteredListeners(@NotNull Listener listener, @NotNull Plugin plugin) {
Set<Method> methods;
Validate.notNull(plugin, "Plugin can not be null");
Validate.notNull(listener, "Listener can not be null");
boolean useTimings = this.server.getPluginManager().useTimings();
Map<Class<? extends Event>, Set<RegisteredListener>> ret = new HashMap<>();
try {
Method[] publicMethods = listener.getClass().getMethods();
Method[] privateMethods = listener.getClass().getDeclaredMethods();
methods = new HashSet<>(publicMethods.length + privateMethods.length, 1.0F);
byte b;
int i;
Method[] arrayOfMethod1;
for (i = (arrayOfMethod1 = publicMethods).length, b = 0; b < i; ) {
final Method method = arrayOfMethod1;
methods.add(method);
b++;
}
for (i = (arrayOfMethod1 = privateMethods).length, b = 0; b < i; ) {
final Method method = arrayOfMethod1;
methods.add(method);
b++;
}
} catch (NoClassDefFoundError e) {
plugin.getLogger().severe("Plugin " + plugin.getDescription().getFullName() + " has failed to register events for " + listener.getClass() + " because " + e.getMessage() + " does not exist.");
return ret;
}
for (Method method : methods) {
EventHandler eh = method.<EventHandler>getAnnotation(EventHandler.class);
if (eh == null)
continue;
if (method.isBridge() || method.isSynthetic())
continue;
Class<?> checkClass;
if ((method.getParameterTypes()).length != 1 || !Event.class.isAssignableFrom(checkClass = method.getParameterTypes()[0])) {
plugin.getLogger().severe(String.valueOf(plugin.getDescription().getFullName()) + " attempted to register an invalid EventHandler method signature \"" + method.toGenericString() + "\" in " + listener.getClass());
continue;
}
final Class<? extends Event> eventClass = checkClass.asSubclass(Event.class);
method.setAccessible(true);
Set<RegisteredListener> eventSet = ret.get(eventClass);
if (eventSet == null) {
eventSet = new HashSet<>();
ret.put(eventClass, eventSet);
}
for (Class<?> clazz = eventClass; Event.class.isAssignableFrom(clazz); clazz = clazz.getSuperclass()) {
if (clazz.getAnnotation(Deprecated.class) != null) {
Warning warning = clazz.<Warning>getAnnotation(Warning.class);
Warning.WarningState warningState = this.server.getWarningState();
if (!warningState.printFor(warning))
break;
plugin.getLogger().log(
Level.WARNING,
String.format(
"\"%s\" has registered a listener for %s on method \"%s\", but the event is Deprecated. \"%s\"; please notify the authors %s.", new Object[] { plugin.getDescription().getFullName(),
clazz.getName(),
method.toGenericString(), (
warning != null && warning.reason().length() != 0) ? warning.reason() : "Server performance will be affected",
Arrays.toString(plugin.getDescription().getAuthors().toArray()) }), (warningState == Warning.WarningState.ON) ? (Throwable)new AuthorNagException(null) : null);
break;
}
}
final CustomTimingsHandler timings = new CustomTimingsHandler("Plugin: " + plugin.getDescription().getFullName() + " Event: " + listener.getClass().getName() + "::" + method.getName() + "(" + eventClass.getSimpleName() + ")", pluginParentTimer);
EventExecutor executor = new EventExecutor() {
public void execute(@NotNull Listener listener, @NotNull Event event) throws EventException {
try {
if (!eventClass.isAssignableFrom(event.getClass()))
return;
boolean isAsync = event.isAsynchronous();
if (!isAsync)
timings.startTiming();
method.invoke(listener, new Object[] { event });
if (!isAsync)
timings.stopTiming();
} catch (InvocationTargetException ex) {
throw new EventException(ex.getCause());
} catch (Throwable t) {
throw new EventException(t);
}
}
};
eventSet.add(new RegisteredListener(listener, executor, eh.priority(), plugin, eh.ignoreCancelled()));
}
return ret;
}
别急,仔细看
Map<Class<? extends Event>, Set<RegisteredListener>> ret = new HashMap<>();
简写return为ret,这是创建map的操作
Method[] publicMethods = listener.getClass().getMethods();
Method[] privateMethods = listener.getClass().getDeclaredMethods();
methods = new HashSet<>(publicMethods.length + privateMethods.length, 1.0F);
说白了就是把你整个实现Listener类的所有方法给存起来了(具体存储的,直接看上面)
接下来最最最最重要的,@Interface,给方法的注解
for (Method method : methods) {
EventHandler eh = method.<EventHandler>getAnnotation(EventHandler.class);
if (eh == null)
continue;
这里就检查了,方法有没有@EventHandler这个注解
如果有,那么再进行下一步添加事件监听的操作,否则就忽略
if ((method.getParameterTypes()).length != 1 || !Event.class.isAssignableFrom(checkClass = method.getParameterTypes()[0])) {
plugin.getLogger().severe(String.valueOf(plugin.getDescription().getFullName()) + " attempted to register an invalid EventHandler method signature \"" + method.toGenericString() + "\" in " + listener.getClass());
continue;
}
这个是什么?这是检查参数的长度
那就可以回答一个问题https://www.mcbbs.net/forum.php? ... 067519&pid=18709678
这个帖子的第二个事件监听
@EventHandler
void onBreakPickaxe(PlayerItemBreakEvent e,Plugin plugin) {
会在register的时候被过滤掉,因为参数列表长度不是1
(Bukkit不可能知道你的奇奇怪怪的参数,他只能给你Event对象,所以必须过滤
不要乱,我们回到起点
for (Map.Entry<Class<? extends Event>, Set<RegisteredListener>> entry : plugin.getPluginLoader().createRegisteredListeners(listener, plugin).entrySet())
getEventListeners(getRegistrationClass(entry.getKey())).registerAll(entry.getValue());
再来看
@NotNull
private HandlerList getEventListeners(@NotNull Class<? extends Event> type) {
try {
Method method = getRegistrationClass(type).getDeclaredMethod("getHandlerList", new Class[0]);
method.setAccessible(true);
return (HandlerList)method.invoke(null, new Object[0]);
} catch (Exception e) {
throw new IllegalPluginAccessException(e.toString());
}
}
@NotNull
private Class<? extends Event> getRegistrationClass(@NotNull Class<? extends Event> clazz) {
try {
clazz.getDeclaredMethod("getHandlerList", new Class[0]);
return clazz;
} catch (NoSuchMethodException e) {
if (clazz.getSuperclass() != null &&
!clazz.getSuperclass().equals(Event.class) &&
Event.class.isAssignableFrom(clazz.getSuperclass()))
return getRegistrationClass(clazz.getSuperclass().asSubclass(Event.class));
throw new IllegalPluginAccessException("Unable to find handler list for event " + clazz.getName() + ". Static getHandlerList method required!");
}
}
我们接触到了https://bukkit.windit.net/javado ... nt/HandlerList.html
private HandlerList getEventListeners(@NotNull Class<? extends Event> type)
注意变量名Type
什么意思?每一个事件的类型,都有自己的对应的Handler,
这些Handler,按照事件监听的优先级,存储了事件监听的处理器
当callEvent时,按照优先级,一批一批触发
public class HandlerList {
private final EnumMap<EventPriority, ArrayList<RegisteredListener>> handlerslots;
所以,上面在遍历了Map之后(不要乱掉,仔细思考)
其实就是获取了对应的EventHandler注解的方法的相关参数
得到了事件本身的Class,也就是事件的类型
然后获取这个事件的Handler
最后在这个Handler里面加入事件监听器
当callEvent的时候,就按照优先级去调用
至于@Interface的相关问题 可以看看@蕾米洛伊 的详尽回复
https://www.mcbbs.net/forum.php? ... 067519&pid=18709785
那么现在可能还有一个问题:到底哪里触发了事件监听???
这都是OBC的CraftEventFactory以及具体的NMS类的实现了
举个例子(Spigot1.15.2核心)
EntityItem类(物品堆掉落物实体)128行开始
if (!this.world.isClientSide && this.age >= this.world.spigotConfig.itemDespawnRate) {
if (CraftEventFactory.callItemDespawnEvent(this).isCancelled()) {
this.age = 0;
return;
}
die();
}
加個[code吧],看著實在不舒服
🐚♥ 发表于 2020-6-27 11:38
加個[code吧],看著實在不舒服
论坛的 [code插件] 吞代码的现象太严重了。。。很久以前贺兰写了半个小时的bungee config的详解全吞了,我写过的一个papi教程也吞过,总之code就不靠谱
反正这些都是反编译SDK拿到的源码,在jdgui、fernflower核心的idea等等地方都可以看
William_Shi 发表于 2020-6-27 12:21
论坛的 [code插件] 吞代码的现象太严重了。。。很久以前贺兰写了半个小时的bungee config的详解全吞了, ...
只要粘贴过去用就没事
不要直接改已经整好的代码(纯文本也没事)
秃头警告qwq 再掉就没了!!