diff --git a/patches/api/Add-paper-dumplisteners-command.patch b/patches/api/Add-paper-dumplisteners-command.patch index e27ba2dd51..abdb47e4bd 100644 --- a/patches/api/Add-paper-dumplisteners-command.patch +++ b/patches/api/Add-paper-dumplisteners-command.patch @@ -4,6 +4,88 @@ Date: Sat, 19 Nov 2022 19:46:44 +0100 Subject: [PATCH] Add /paper dumplisteners command +diff --git a/src/main/java/co/aikar/timings/TimedEventExecutor.java b/src/main/java/co/aikar/timings/TimedEventExecutor.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/co/aikar/timings/TimedEventExecutor.java ++++ b/src/main/java/co/aikar/timings/TimedEventExecutor.java +@@ -0,0 +0,0 @@ public class TimedEventExecutor implements EventExecutor { + executor.execute(listener, event); + } + } ++ ++ @Override ++ @NotNull ++ public String toString() { ++ return "TimedEventExecutor['" + this.executor.toString() + "']"; ++ } + } +diff --git a/src/main/java/com/destroystokyo/paper/event/executor/MethodHandleEventExecutor.java b/src/main/java/com/destroystokyo/paper/event/executor/MethodHandleEventExecutor.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/com/destroystokyo/paper/event/executor/MethodHandleEventExecutor.java ++++ b/src/main/java/com/destroystokyo/paper/event/executor/MethodHandleEventExecutor.java +@@ -0,0 +0,0 @@ import org.jetbrains.annotations.NotNull; + public class MethodHandleEventExecutor implements EventExecutor { + private final Class eventClass; + private final MethodHandle handle; ++ private final Method method; + + public MethodHandleEventExecutor(@NotNull Class eventClass, @NotNull MethodHandle handle) { + this.eventClass = eventClass; + this.handle = handle; ++ this.method = null; + } + + public MethodHandleEventExecutor(@NotNull Class eventClass, @NotNull Method m) { +@@ -0,0 +0,0 @@ public class MethodHandleEventExecutor implements EventExecutor { + } catch (IllegalAccessException e) { + throw new AssertionError("Unable to set accessible", e); + } ++ this.method = m; + } + + @Override +@@ -0,0 +0,0 @@ public class MethodHandleEventExecutor implements EventExecutor { + SneakyThrow.sneaky(t); + } + } ++ ++ @Override ++ @NotNull ++ public String toString() { ++ return "MethodHandleEventExecutor['" + this.method + "']"; ++ } + } +diff --git a/src/main/java/com/destroystokyo/paper/event/executor/StaticMethodHandleEventExecutor.java b/src/main/java/com/destroystokyo/paper/event/executor/StaticMethodHandleEventExecutor.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/com/destroystokyo/paper/event/executor/StaticMethodHandleEventExecutor.java ++++ b/src/main/java/com/destroystokyo/paper/event/executor/StaticMethodHandleEventExecutor.java +@@ -0,0 +0,0 @@ import org.jetbrains.annotations.NotNull; + public class StaticMethodHandleEventExecutor implements EventExecutor { + private final Class eventClass; + private final MethodHandle handle; ++ private final Method method; + + public StaticMethodHandleEventExecutor(@NotNull Class eventClass, @NotNull Method m) { + Preconditions.checkArgument(Modifier.isStatic(m.getModifiers()), "Not a static method: %s", m); +@@ -0,0 +0,0 @@ public class StaticMethodHandleEventExecutor implements EventExecutor { + } catch (IllegalAccessException e) { + throw new AssertionError("Unable to set accessible", e); + } ++ this.method = m; + } + + @Override +@@ -0,0 +0,0 @@ public class StaticMethodHandleEventExecutor implements EventExecutor { + SneakyThrow.sneaky(throwable); + } + } ++ ++ @Override ++ @NotNull ++ public String toString() { ++ return "StaticMethodHandleEventExecutor['" + this.method + "']"; ++ } + } diff --git a/src/main/java/org/bukkit/event/HandlerList.java b/src/main/java/org/bukkit/event/HandlerList.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/org/bukkit/event/HandlerList.java @@ -35,3 +117,61 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 handlerslots = new EnumMap>(EventPriority.class); for (EventPriority o : EventPriority.values()) { handlerslots.put(o, new ArrayList()); +diff --git a/src/main/java/org/bukkit/plugin/EventExecutor.java b/src/main/java/org/bukkit/plugin/EventExecutor.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/org/bukkit/plugin/EventExecutor.java ++++ b/src/main/java/org/bukkit/plugin/EventExecutor.java +@@ -0,0 +0,0 @@ public interface EventExecutor { + try { + EventExecutor asmExecutor = executorClass.newInstance(); + // Define a wrapper to conform to bukkit stupidity (passing in events that don't match and wrapper exception) +- return (listener, event) -> { +- if (!eventClass.isInstance(event)) return; +- asmExecutor.execute(listener, event); ++ return new EventExecutor() { ++ @Override ++ public void execute(@NotNull Listener listener, @NotNull Event event) throws EventException { ++ if (!eventClass.isInstance(event)) return; ++ asmExecutor.execute(listener, event); ++ } ++ ++ @Override ++ @NotNull ++ public String toString() { ++ return "ASMEventExecutor['" + m + "']"; ++ } + }; + } catch (InstantiationException | IllegalAccessException e) { + throw new AssertionError("Unable to initialize generated event executor", e); +diff --git a/src/main/java/org/bukkit/plugin/RegisteredListener.java b/src/main/java/org/bukkit/plugin/RegisteredListener.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/org/bukkit/plugin/RegisteredListener.java ++++ b/src/main/java/org/bukkit/plugin/RegisteredListener.java +@@ -0,0 +0,0 @@ public class RegisteredListener { + public boolean isIgnoringCancelled() { + return ignoreCancelled; + } ++ ++ // Paper start ++ /** ++ * Get the executor for this registration. ++ * ++ * @return executor ++ */ ++ @NotNull ++ public EventExecutor getExecutor() { ++ return this.executor; ++ } ++ ++ @Override ++ public String toString() { ++ return "RegisteredListener{" ++ + "plugin=\"" + this.plugin.getName() ++ + "\", listener=\"" + this.listener ++ + "\", executor=\"" + this.executor ++ + "\", priority=\"" + this.priority.name() + " (" + this.priority.getSlot() + ")" ++ + "\", ignoringCancelled=" + this.ignoreCancelled ++ + "}"; ++ } ++ // Paper end + } diff --git a/patches/server/Add-paper-dumplisteners-command.patch b/patches/server/Add-paper-dumplisteners-command.patch index 79de5eb417..5bb19f5cdb 100644 --- a/patches/server/Add-paper-dumplisteners-command.patch +++ b/patches/server/Add-paper-dumplisteners-command.patch @@ -35,27 +35,36 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + +import com.destroystokyo.paper.util.SneakyThrow; +import io.papermc.paper.command.PaperSubcommand; ++import java.io.File; ++import java.io.IOException; ++import java.io.PrintWriter; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Field; ++import java.time.LocalDateTime; ++import java.time.format.DateTimeFormatter; ++import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Set; -+import java.util.stream.Stream; ++import net.kyori.adventure.text.Component; +import net.minecraft.server.MinecraftServer; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.event.HandlerList; +import org.bukkit.plugin.Plugin; ++import org.bukkit.plugin.RegisteredListener; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.framework.qual.DefaultQualifier; + ++import static net.kyori.adventure.text.Component.newline; +import static net.kyori.adventure.text.Component.space; +import static net.kyori.adventure.text.Component.text; +import static net.kyori.adventure.text.format.NamedTextColor.GRAY; +import static net.kyori.adventure.text.format.NamedTextColor.GREEN; +import static net.kyori.adventure.text.format.NamedTextColor.RED; ++import static net.kyori.adventure.text.format.NamedTextColor.WHITE; + +@DefaultQualifier(NonNull.class) +public final class DumpListenersCommand implements PaperSubcommand { @@ -73,13 +82,41 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + + @Override + public boolean execute(final CommandSender sender, final String subCommand, final String[] args) { ++ if (args.length >= 1 && args[0].equals("tofile")) { ++ this.dumpToFile(sender); ++ return true; ++ } + this.doDumpListeners(sender, args); + return true; + } + ++ private void dumpToFile(final CommandSender sender) { ++ final File file = new File("debug/listeners-" ++ + DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss").format(LocalDateTime.now()) + ".txt"); ++ try (final PrintWriter writer = new PrintWriter(file)) { ++ for (final String eventClass : eventClassNames()) { ++ final HandlerList handlers; ++ try { ++ handlers = (HandlerList) findClass(eventClass).getMethod("getHandlerList").invoke(null); ++ } catch (final ReflectiveOperationException e) { ++ continue; ++ } ++ if (handlers.getRegisteredListeners().length != 0) { ++ writer.println(eventClass); ++ } ++ for (final RegisteredListener registeredListener : handlers.getRegisteredListeners()) { ++ writer.println(" - " + registeredListener); ++ } ++ } ++ } catch (final IOException ex) { ++ throw new RuntimeException(ex); ++ } ++ sender.sendMessage(text("Dumped listeners to " + file, GREEN)); ++ } ++ + private void doDumpListeners(final CommandSender sender, final String[] args) { + if (args.length == 0) { -+ sender.sendMessage(text("Usage: /paper dumplisteners [className]", RED)); ++ sender.sendMessage(text("Usage: /paper dumplisteners tofile|", RED)); + return; + } + @@ -93,12 +130,19 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + + sender.sendMessage(text("Listeners for " + args[0] + ":")); + -+ Stream.of(handlers.getRegisteredListeners()) -+ .map(listener -> text(listener.getPlugin().getName(), GREEN) ++ for (final RegisteredListener listener : handlers.getRegisteredListeners()) { ++ final Component hoverText = text("Priority: " + listener.getPriority().name() + " (" + listener.getPriority().getSlot() + ")", WHITE) ++ .append(newline()) ++ .append(text("Listener: " + listener.getListener())) ++ .append(newline()) ++ .append(text("Executor: " + listener.getExecutor())) ++ .append(newline()) ++ .append(text("Ignoring cancelled: " + listener.isIgnoringCancelled())); ++ ++ sender.sendMessage(text(listener.getPlugin().getName(), GREEN) + .append(space()) -+ .append(text("(" + listener.getListener().getClass().getName() + ")", GRAY) -+ .hoverEvent(text("Priority: " + listener.getPriority().name() + " (" + listener.getPriority().getSlot() + ")", GRAY)))) -+ .forEach(sender::sendMessage); ++ .append(text("(" + listener.getListener().getClass().getName() + ")", GRAY).hoverEvent(hoverText))); ++ } + + sender.sendMessage(text("Total listeners: " + handlers.getRegisteredListeners().length)); + @@ -115,16 +159,23 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + @Override + public List tabComplete(final CommandSender sender, final String subCommand, final String[] args) { + return switch (args.length) { -+ case 0 -> List.copyOf(getEventClasses()); -+ case 1 -> getEventClasses().stream() ++ case 0 -> suggestions(); ++ case 1 -> suggestions().stream() + .filter(clazz -> clazz.toLowerCase(Locale.ROOT).contains(args[0].toLowerCase(Locale.ROOT))) + .toList(); + default -> Collections.emptyList(); + }; + } + ++ private static List suggestions() { ++ final List ret = new ArrayList<>(); ++ ret.add("tofile"); ++ ret.addAll(eventClassNames()); ++ return ret; ++ } ++ + @SuppressWarnings("unchecked") -+ private static Set getEventClasses() { ++ private static Set eventClassNames() { + try { + return (Set) EVENT_TYPES_HANDLE.invokeExact(); + } catch (final Throwable e) {