From 430f85c5fbe24429155b22a87860c1d150376bbe Mon Sep 17 00:00:00 2001 From: CrebsTheCoder Date: Sat, 14 Nov 2026 21:20:54 +0800 Subject: [PATCH] Fix legacy event attribution using Skript caller metadata --- .../org/skdocs/skdocstool/Skdocstool.java | 2 +- .../docs/addon/SyntaxOwnership.java | 21 +++++++--- .../docs/element/EventDocumentation.java | 3 +- .../registration/LegacyEventAttribution.java | 36 ++++++++++++++++ .../docs/addon/SyntaxOwnershipTest.java | 33 ++++++++++++++- .../LegacyEventAttributionTest.java | 41 +++++++++++++++++++ 6 files changed, 127 insertions(+), 9 deletions(-) create mode 100644 src/main/java/org/skdocs/skdocstool/docs/registration/LegacyEventAttribution.java create mode 100644 src/test/java/org/skdocs/skdocstool/docs/registration/LegacyEventAttributionTest.java diff --git a/src/main/java/org/skdocs/skdocstool/Skdocstool.java b/src/main/java/org/skdocs/skdocstool/Skdocstool.java index cee2579..e7fda92 100644 --- a/src/main/java/org/skdocs/skdocstool/Skdocstool.java +++ b/src/main/java/org/skdocs/skdocstool/Skdocstool.java @@ -22,4 +22,4 @@ public void onEnable() { public void onDisable() { getLogger().info("Skdocstool has been disabled"); } -} \ No newline at end of file +} diff --git a/src/main/java/org/skdocs/skdocstool/docs/addon/SyntaxOwnership.java b/src/main/java/org/skdocs/skdocstool/docs/addon/SyntaxOwnership.java index 85a53d7..7431a76 100644 --- a/src/main/java/org/skdocs/skdocstool/docs/addon/SyntaxOwnership.java +++ b/src/main/java/org/skdocs/skdocstool/docs/addon/SyntaxOwnership.java @@ -1,20 +1,28 @@ package org.skdocs.skdocstool.docs.addon; import ch.njol.skript.lang.SyntaxElementInfo; +import ch.njol.skript.lang.SkriptEventInfo; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import org.skriptlang.skript.bukkit.registration.BukkitSyntaxInfos; import org.skriptlang.skript.docs.Origin; import org.skriptlang.skript.registration.SyntaxInfo; +import org.skdocs.skdocstool.docs.registration.LegacyEventAttribution; public final class SyntaxOwnership { private final List addons; private final ClassLoader coreLoader; + private final boolean explicitLegacyEventOrigins; public SyntaxOwnership(List addons, ClassLoader coreLoader) { + this(addons, coreLoader, LegacyEventAttribution.supported()); + } + + SyntaxOwnership(List addons, ClassLoader coreLoader, boolean explicitLegacyEventOrigins) { this.addons = List.copyOf(addons); this.coreLoader = coreLoader; + this.explicitLegacyEventOrigins = explicitLegacyEventOrigins; } public boolean owns(SyntaxInfo syntax, AddonContext addon) { @@ -23,17 +31,20 @@ public boolean owns(SyntaxInfo syntax, AddonContext addon) { @SuppressWarnings("removal") public AddonContext owner(SyntaxInfo syntax) { + AddonContext declared = null; + if (syntax.origin() instanceof Origin.AddonOrigin origin) { + declared = addons.stream().filter(a -> a.addon().name().equals(origin.addon().name())) + .findFirst().orElse(null); + } + if (explicitLegacyEventOrigins && syntax instanceof SkriptEventInfo.ModernSkriptEventInfo + && declared != null) return declared; + Set registered = new LinkedHashSet<>(); for (AddonContext addon : addons) if (addon.registrationDocs().syntax(syntax.type(), syntax.patterns()) != null) registered.add(addon); if (registered.size() == 1) return registered.iterator().next(); - AddonContext declared = null; - if (syntax.origin() instanceof Origin.AddonOrigin origin) { - declared = addons.stream().filter(a -> a.addon().name().equals(origin.addon().name())) - .findFirst().orElse(null); - } if (!(syntax instanceof SyntaxElementInfo) && declared != null) return declared; if (syntax instanceof BukkitSyntaxInfos.Event event) { diff --git a/src/main/java/org/skdocs/skdocstool/docs/element/EventDocumentation.java b/src/main/java/org/skdocs/skdocstool/docs/element/EventDocumentation.java index acdbf67..ff48646 100644 --- a/src/main/java/org/skdocs/skdocstool/docs/element/EventDocumentation.java +++ b/src/main/java/org/skdocs/skdocstool/docs/element/EventDocumentation.java @@ -14,6 +14,7 @@ import org.skdocs.skdocstool.docs.addon.AddonContext; import org.skdocs.skdocstool.docs.addon.SyntaxOwnership; import org.skdocs.skdocstool.docs.registration.RegistrationMetadata; +import org.skdocs.skdocstool.docs.registration.LegacyEventAttribution; import org.skdocs.skdocstool.docs.builder.DocumentationFormat; import org.skriptlang.skript.Skript; import org.skriptlang.skript.bukkit.lang.eventvalue.EventValue; @@ -115,7 +116,7 @@ private JsonArray eventsArray(AddonContext addon, SyntaxOwnership ownership, boo obj.add("since", strings(since)); obj.add("requiredPlugins", strings(info.requiredPlugins())); obj.add("keywords", strings(prefer(keywords, info.keywords().toArray(new String[0])))); - obj.addProperty("source", type.getName()); + obj.addProperty("source", LegacyEventAttribution.source(info)); } String[] eventValues = collectEventValues(info.events()); diff --git a/src/main/java/org/skdocs/skdocstool/docs/registration/LegacyEventAttribution.java b/src/main/java/org/skdocs/skdocstool/docs/registration/LegacyEventAttribution.java new file mode 100644 index 0000000..5e0fe4e --- /dev/null +++ b/src/main/java/org/skdocs/skdocstool/docs/registration/LegacyEventAttribution.java @@ -0,0 +1,36 @@ +package org.skdocs.skdocstool.docs.registration; + +import ch.njol.skript.lang.SkriptEventInfo; +import org.skriptlang.skript.bukkit.registration.BukkitSyntaxInfos; +import org.skriptlang.skript.docs.Origin; + +@SuppressWarnings("removal") +public final class LegacyEventAttribution { + private static final boolean SUPPORTED = detectSupport(); + + private LegacyEventAttribution() {} + + public static boolean supported() { return SUPPORTED; } + + private static boolean detectSupport() { + try { + SkriptEventInfo.ModernSkriptEventInfo.class.getConstructor( + String.class, String[].class, Class.class, String.class, Class[].class, Origin.class); + return true; + } catch (NoSuchMethodException e) { + return false; + } + } + + public static String source(BukkitSyntaxInfos.Event event) { + return source(event, SUPPORTED); + } + + static String source(BukkitSyntaxInfos.Event event, boolean supported) { + if (supported && event instanceof SkriptEventInfo.ModernSkriptEventInfo legacy) { + String path = legacy.getOriginClassPath(); + if (path != null && !path.isBlank()) return path; + } + return event.type().getName(); + } +} diff --git a/src/test/java/org/skdocs/skdocstool/docs/addon/SyntaxOwnershipTest.java b/src/test/java/org/skdocs/skdocstool/docs/addon/SyntaxOwnershipTest.java index de3406d..92817a2 100644 --- a/src/test/java/org/skdocs/skdocstool/docs/addon/SyntaxOwnershipTest.java +++ b/src/test/java/org/skdocs/skdocstool/docs/addon/SyntaxOwnershipTest.java @@ -21,7 +21,36 @@ class SyntaxOwnershipTest { private final AddonContext core = addon("Engine", coreLoader); private final Class eventClass = eventClass(); private final AddonContext addon = addon("UnrelatedName", eventClass.getClassLoader()); - private final SyntaxOwnership ownership = new SyntaxOwnership(List.of(core, addon), coreLoader); + private final SyntaxOwnership ownership = new SyntaxOwnership(List.of(core, addon), coreLoader, false); + + @Test void fixedLegacyOriginWinsOverUnderlyingEventProvider() { + var resolver = new SyntaxOwnership(List.of(core, addon), coreLoader, true); + var event = legacyEvent(); + assertSame(core, resolver.owner(event)); + } + + @Test void fixedLegacyAddonOriginWinsOverAnotherAddonsEvent() { + var registrar = addon("Registrar", new ClassLoader() {}); + var resolver = new SyntaxOwnership(List.of(core, addon, registrar), coreLoader, true); + var event = legacyEvent(); + when(event.origin()).thenReturn((Origin.AddonOrigin) () -> registrar.addon()); + assertSame(registrar, resolver.owner(event)); + } + + @Test void fixedLegacyUnknownOriginStillUsesEventProvider() { + var resolver = new SyntaxOwnership(List.of(core, addon), coreLoader, true); + var event = legacyEvent(); + when(event.origin()).thenReturn(Origin.UNKNOWN); + assertSame(addon, resolver.owner(event)); + } + + @Test void fixedLegacyOriginWinsOverScannedRegistrationMetadata() { + var resolver = new SyntaxOwnership(List.of(core, addon), coreLoader, true); + var event = legacyEvent(); + addon.registrationDocs().putSyntax(SimpleEvent.class, new String[]{"signal"}, + new RegistrationMetadata(null, "Signal", null, null, null, null, false)); + assertSame(core, resolver.owner(event)); + } @Test void legacySharedSyntaxBelongsToTheUnderlyingEventProvider() { var event = legacyEvent(); @@ -56,7 +85,7 @@ class SyntaxOwnershipTest { @Test void ambiguousEventProvidersAreNotAssignedToCore() { var otherClass = eventClass(); var other = addon("AnotherName", otherClass.getClassLoader()); - var resolver = new SyntaxOwnership(List.of(core, addon, other), coreLoader); + var resolver = new SyntaxOwnership(List.of(core, addon, other), coreLoader, false); var event = legacyEvent(); when(event.events()).thenReturn(List.of(eventClass, otherClass)); assertNull(resolver.owner(event)); diff --git a/src/test/java/org/skdocs/skdocstool/docs/registration/LegacyEventAttributionTest.java b/src/test/java/org/skdocs/skdocstool/docs/registration/LegacyEventAttributionTest.java new file mode 100644 index 0000000..9a2165f --- /dev/null +++ b/src/test/java/org/skdocs/skdocstool/docs/registration/LegacyEventAttributionTest.java @@ -0,0 +1,41 @@ +package org.skdocs.skdocstool.docs.registration; + +import ch.njol.skript.lang.SkriptEventInfo; +import ch.njol.skript.lang.util.SimpleEvent; +import org.junit.jupiter.api.Test; +import org.skriptlang.skript.bukkit.registration.BukkitSyntaxInfos; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +@SuppressWarnings({"removal", "unchecked"}) +class LegacyEventAttributionTest { + @Test void sharedImplementationRetainsEachRegisteringClass() { + var first = legacy("example.First$Registration"); + var second = legacy("example.Second"); + assertEquals("example.First$Registration", LegacyEventAttribution.source(first, true)); + assertEquals("example.Second", LegacyEventAttribution.source(second, true)); + } + + @Test void missingPathsFallBackToImplementation() { + for (String path : new String[]{null, "", " "}) + assertEquals(SimpleEvent.class.getName(), LegacyEventAttribution.source(legacy(path), true)); + } + + @Test void olderSkriptDoesNotTrustLegacyPaths() { + assertEquals(SimpleEvent.class.getName(), + LegacyEventAttribution.source(legacy("Addon label"), false)); + } + + @Test void modernEventsKeepImplementationSource() { + var modern = mock(BukkitSyntaxInfos.Event.class); + when(modern.type()).thenReturn(SimpleEvent.class); + assertEquals(SimpleEvent.class.getName(), LegacyEventAttribution.source(modern, true)); + } + + private SkriptEventInfo.ModernSkriptEventInfo legacy(String path) { + var event = mock(SkriptEventInfo.ModernSkriptEventInfo.class); + when(event.type()).thenReturn(SimpleEvent.class); + when(event.getOriginClassPath()).thenReturn(path); + return event; + } +}