From c82f7517de49dd8be6ce8afa63e5545dae72be5a Mon Sep 17 00:00:00 2001 From: bigguy345 Date: Sat, 10 Jan 2026 07:13:00 +0200 Subject: [PATCH] Revert "Annotations for API events" This reverts commit 06aa0ce22e609e0e894afc97f4a0499381f255be. --- .../noppes/npcs/constants/ScriptContext.java | 219 ++++-------------- .../npcs/constants/ScriptContextMarker.java | 27 --- 2 files changed, 43 insertions(+), 203 deletions(-) delete mode 100644 src/main/java/noppes/npcs/constants/ScriptContextMarker.java diff --git a/src/main/java/noppes/npcs/constants/ScriptContext.java b/src/main/java/noppes/npcs/constants/ScriptContext.java index 59d02d98d..942c406e6 100644 --- a/src/main/java/noppes/npcs/constants/ScriptContext.java +++ b/src/main/java/noppes/npcs/constants/ScriptContext.java @@ -2,49 +2,65 @@ package noppes.npcs.constants; import noppes.npcs.api.event.*; -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.net.URLDecoder; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; /** * Represents a script context that maps to one or more event interface namespaces. * - * Script contexts are automatically discovered by scanning for {@link ScriptContextMarker} - * annotations on event interfaces. For example: + * A script context can have multiple namespaces because script editors often + * support hooks from multiple event types. For example, Player scripts support: + * - IPlayerEvent (core player hooks) + * - IAnimationEvent (animation hooks) + * - IPartyEvent (party hooks) + * - ICustomGuiEvent (custom GUI hooks) + * - etc. * - *
- * {@literal @}ScriptContextMarker("NPC")
- * public interface INpcEvent extends ICustomNPCsEvent { }
- * 
+ * This is a registerable system - any mod can register their own contexts: * - * Multiple interfaces can share the same context ID - they will be grouped together. - * This is a registerable system - any mod can register their own contexts by annotating - * their event interfaces. + * // Register a new context with multiple namespaces: + * ScriptContext.register("DBC", "IDBCEvent", "IDBCPlayerEvent", "IDBCFormEvent"); + * + * // Or add namespaces to an existing context: + * ScriptContext.PLAYER.addNamespace("IDBCPlayerEvent"); */ public class ScriptContext { // Registry of all script contexts private static final Map REGISTRY = new ConcurrentHashMap<>(); - - // Static initializer to scan and register annotated classes - static { - scanAndRegisterAnnotatedClasses(); - } // ==================== BUILT-IN CONTEXTS ==================== - // These are loaded dynamically from annotations, but we keep references for convenience - public static final ScriptContext NPC = byId("NPC"); - public static final ScriptContext PLAYER = byId("PLAYER"); - public static final ScriptContext BLOCK = byId("BLOCK"); - public static final ScriptContext ITEM = byId("ITEM"); - public static final ScriptContext FORGE = byId("FORGE"); - public static final ScriptContext GLOBAL = byId("GLOBAL"); + public static final ScriptContext NPC = register("NPC", + INpcEvent.class, + IProjectileEvent.class + ); + + public static final ScriptContext PLAYER = register("PLAYER", + IPlayerEvent.class, + IAnimationEvent.class, + IPartyEvent.class, + IDialogEvent.class, + IQuestEvent.class, + IFactionEvent.class, + ICustomGuiEvent.class + ); + + public static final ScriptContext BLOCK = register("BLOCK", + IBlockEvent.class + ); + + public static final ScriptContext ITEM = register("ITEM", + IItemEvent.class + ); + + public static final ScriptContext FORGE = register("FORGE", + IForgeEvent.class + ); + + public static final ScriptContext GLOBAL = register("GLOBAL", + "Global" // Special case: Global namespace doesn't have a corresponding event class + ); // ==================== INSTANCE FIELDS ==================== @@ -106,155 +122,6 @@ public class ScriptContext { // ==================== REGISTRATION API ==================== - /** - * Scan the classpath for classes annotated with {@link ScriptContextMarker} - * and automatically register them. - */ - private static void scanAndRegisterAnnotatedClasses() { - try { - // Scan the noppes.npcs.api.event package for annotated interfaces - List> annotatedClasses = findAnnotatedClasses("noppes.npcs.api.event", ScriptContextMarker.class); - - // Group classes by their context ID - Map>> contextGroups = new HashMap<>(); - - for (Class eventClass : annotatedClasses) { - ScriptContextMarker annotation = eventClass.getAnnotation(ScriptContextMarker.class); - if (annotation != null) { - String contextId = annotation.value(); - contextGroups.computeIfAbsent(contextId, k -> new ArrayList<>()).add(eventClass); - } - } - - // Register each context with its grouped classes - for (Map.Entry>> entry : contextGroups.entrySet()) { - String contextId = entry.getKey(); - List> classes = entry.getValue(); - register(contextId, classes.toArray(new Class[0])); - } - - // Special case: GLOBAL context (no annotation needed) - if (!REGISTRY.containsKey("GLOBAL")) { - register("GLOBAL", "Global"); - } - } catch (Exception e) { - // Fallback: if scanning fails, at least register known core events - System.err.println("[ScriptContext] Failed to scan for annotated classes: " + e.getMessage()); - e.printStackTrace(); - fallbackRegistration(); - } - } - - /** - * Find all classes in a package that have a specific annotation. - */ - private static List> findAnnotatedClasses(String packageName, Class annotationClass) throws Exception { - List> annotatedClasses = new ArrayList<>(); - ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); - String packagePath = packageName.replace('.', '/'); - - // Get resources for the package - Enumeration resources = classLoader.getResources(packagePath); - - while (resources.hasMoreElements()) { - URL resource = resources.nextElement(); - String protocol = resource.getProtocol(); - - if ("file".equals(protocol)) { - // Scanning from file system (development environment) - String filePath = URLDecoder.decode(resource.getFile(), "UTF-8"); - File directory = new File(filePath); - if (directory.exists()) { - findClassesInDirectory(directory, packageName, annotationClass, annotatedClasses); - } - } else if ("jar".equals(protocol)) { - // Scanning from JAR file (production) - String jarPath = resource.getPath().substring(5, resource.getPath().indexOf("!")); - jarPath = URLDecoder.decode(jarPath, "UTF-8"); - findClassesInJar(jarPath, packageName, annotationClass, annotatedClasses); - } - } - - return annotatedClasses; - } - - /** - * Find classes with annotation in a directory (file system). - */ - private static void findClassesInDirectory(File directory, String packageName, - Class annotationClass, - List> result) { - File[] files = directory.listFiles(); - if (files == null) return; - - for (File file : files) { - String fileName = file.getName(); - if (file.isDirectory()) { - findClassesInDirectory(file, packageName + "." + fileName, annotationClass, result); - } else if (fileName.endsWith(".class")) { - String className = packageName + '.' + fileName.substring(0, fileName.length() - 6); - try { - Class clazz = Class.forName(className); - if (clazz.isAnnotationPresent(annotationClass)) { - result.add(clazz); - } - } catch (Throwable e) { - // Skip classes that can't be loaded - } - } - } - } - - /** - * Find classes with annotation in a JAR file. - */ - private static void findClassesInJar(String jarPath, String packageName, - Class annotationClass, - List> result) { - try { - JarFile jarFile = new JarFile(jarPath); - Enumeration entries = jarFile.entries(); - String packagePath = packageName.replace('.', '/'); - - while (entries.hasMoreElements()) { - JarEntry entry = entries.nextElement(); - String entryName = entry.getName(); - - if (entryName.startsWith(packagePath) && entryName.endsWith(".class")) { - String className = entryName.replace('/', '.').substring(0, entryName.length() - 6); - try { - Class clazz = Class.forName(className); - if (clazz.isAnnotationPresent(annotationClass)) { - result.add(clazz); - } - } catch (Throwable e) { - // Skip classes that can't be loaded - } - } - } - jarFile.close(); - } catch (IOException e) { - System.err.println("[ScriptContext] Failed to scan JAR: " + jarPath); - } - } - - /** - * Fallback registration if classpath scanning fails. - */ - private static void fallbackRegistration() { - try { - register("NPC", INpcEvent.class, IProjectileEvent.class); - register("PLAYER", IPlayerEvent.class, IAnimationEvent.class, IPartyEvent.class, - IDialogEvent.class, IQuestEvent.class, IFactionEvent.class, ICustomGuiEvent.class); - register("BLOCK", IBlockEvent.class); - register("ITEM", IItemEvent.class); - register("FORGE", IForgeEvent.class); - register("GLOBAL", "Global"); - } catch (Exception e) { - System.err.println("[ScriptContext] Fallback registration also failed: " + e.getMessage()); - } - } - /** * Register a new script context with one or more namespaces. * diff --git a/src/main/java/noppes/npcs/constants/ScriptContextMarker.java b/src/main/java/noppes/npcs/constants/ScriptContextMarker.java deleted file mode 100644 index 6da6e23ae..000000000 --- a/src/main/java/noppes/npcs/constants/ScriptContextMarker.java +++ /dev/null @@ -1,27 +0,0 @@ -package noppes.npcs.constants; - -import java.lang.annotation.*; - -/** - * Marks an event interface as belonging to a specific script context. - * - * Usage: - *
- * {@literal @}ScriptContextMarker("NPC")
- * public interface INpcEvent extends ICustomNPCsEvent {
- *     // ...
- * }
- * 
- * - * Multiple interfaces can share the same context name - they will all be - * registered as namespaces for that context. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -public @interface ScriptContextMarker { - /** - * The script context ID this event interface belongs to. - * Examples: "NPC", "PLAYER", "BLOCK", "ITEM", "FORGE", "GLOBAL" - */ - String value(); -}