diff --git a/src/api b/src/api index 7a337a570..64fe8794e 160000 --- a/src/api +++ b/src/api @@ -1 +1 @@ -Subproject commit 7a337a5704c442fb98a0fd4e0ba26210cb54af98 +Subproject commit 64fe8794e71d82f09449e6c916c7902fe890cab7 diff --git a/src/main/java/noppes/npcs/controllers/HookDefinition.java b/src/main/java/noppes/npcs/controllers/HookDefinition.java index b8b78fe70..b5a61ceab 100644 --- a/src/main/java/noppes/npcs/controllers/HookDefinition.java +++ b/src/main/java/noppes/npcs/controllers/HookDefinition.java @@ -2,32 +2,23 @@ package noppes.npcs.controllers; import cpw.mods.fml.common.eventhandler.Cancelable; import noppes.npcs.api.handler.IHookDefinition; -import noppes.npcs.janino.annotations.ParamName; - -import java.lang.reflect.Method; -import java.lang.reflect.Parameter; /** * Implementation of {@link IHookDefinition} with a fluent builder API. * *

Usage Examples

*
{@code
- * // Full definition with class reference
- * HookDefinition.builder("onDBCTransform")
- *     .eventClass(IDBCEvent.TransformEvent.class)
- *     .paramNames("event")
- *     .requiredImports("com.dbc.api.event.IDBCEvent")
+ * // Recommended: auto-derives imports from event class
+ * HookDefinition.of("onDBCTransform", IDBCEvent.TransformEvent.class);
+ *
+ * // Full control with builder
+ * HookDefinition.builder("onDBCPowerUp")
+ *     .eventClass(IDBCEvent.PowerUpEvent.class)
  *     .cancelable(true)
  *     .build();
  *
- * // String-based (avoids classloading issues)
- * HookDefinition.builder("onDBCPowerUp")
- *     .eventClass("com.dbc.api.event.IDBCEvent$PowerUpEvent")
- *     .requiredImports("com.dbc.api.event.IDBCEvent")
- *     .build();
- *
- * // Simple hook (backward compat, no metadata)
- * HookDefinition.simple("legacyHook");
+ * // Simple hook (no event metadata)
+ * HookDefinition.simple("customHook");
  * }
*/ public class HookDefinition implements IHookDefinition { @@ -38,7 +29,6 @@ public class HookDefinition implements IHookDefinition { private final String[] requiredImports; private final boolean cancelable; - // Cached resolved class (transient - not serialized) private transient Class cachedEventClass; private transient boolean classResolutionAttempted; @@ -100,18 +90,19 @@ public class HookDefinition implements IHookDefinition { // ==================== Factory Methods ==================== /** - * Create a new builder for a hook definition. + * Create a hook definition from a hook name and event class. + * Automatically derives imports from the event class. * * @param hookName The hook function name - * @return A new builder instance + * @param eventClass The event class (e.g., INpcEvent.InitEvent.class) + * @return A hook definition with auto-derived imports */ - public static Builder builder(String hookName) { - return new Builder(hookName); + public static HookDefinition of(String hookName, Class eventClass) { + return builder(hookName).eventClass(eventClass).build(); } /** - * Create a simple hook definition with just a name (no metadata). - * For backward compatibility with legacy hook registration. + * Create a simple hook definition with just a name (no event metadata). * * @param hookName The hook function name * @return A minimal hook definition @@ -121,49 +112,13 @@ public class HookDefinition implements IHookDefinition { } /** - * Create a hook definition from an interface method. - * Automatically extracts metadata from the method signature and annotations. + * Create a new builder for a hook definition. * - * @param hookName The hook name (may differ from method name) - * @param method The method to extract metadata from - * @return A hook definition with extracted metadata + * @param hookName The hook function name + * @return A new builder instance */ - public static HookDefinition fromMethod(String hookName, Method method) { - Builder builder = builder(hookName); - - // Extract event type from first parameter - if (method.getParameterCount() > 0) { - Class eventType = method.getParameterTypes()[0]; - builder.eventClass(eventType); - - // Extract @Cancelable annotation - if (eventType.isAnnotationPresent(Cancelable.class)) { - builder.cancelable(true); - } - - // Build required imports from event type - String importName = getImportForClass(eventType); - if (importName != null) { - builder.requiredImports(importName); - } - } - - // Extract parameter names from @ParamName annotations - Parameter[] params = method.getParameters(); - if (params.length > 0) { - String[] names = new String[params.length]; - for (int i = 0; i < params.length; i++) { - ParamName annotation = params[i].getAnnotation(ParamName.class); - if (annotation != null) { - names[i] = annotation.value(); - } else { - names[i] = params[i].getType().getSimpleName().toLowerCase(); - } - } - builder.paramNames(names); - } - - return builder.build(); + public static Builder builder(String hookName) { + return new Builder(hookName); } /** @@ -182,7 +137,6 @@ public class HookDefinition implements IHookDefinition { } String name = enclosing.getName(); - // Skip java.lang classes if (name.startsWith("java.lang.")) { return null; } @@ -192,9 +146,6 @@ public class HookDefinition implements IHookDefinition { // ==================== Builder ==================== - /** - * Builder for creating HookDefinition instances with a fluent API. - */ public static class Builder { private final String hookName; private String eventClassName; @@ -210,27 +161,18 @@ public class HookDefinition implements IHookDefinition { } /** - * Set the event class by name (avoids classloading issues). - * - * @param className Full qualified class name (use $ for nested classes) - * @return this builder - */ - public Builder eventClass(String className) { - this.eventClassName = className; - return this; - } - - /** - * Set the event class directly (convenience method). - * The class name will be extracted automatically. - * - * @param clazz The event class - * @return this builder + * Set the event class directly. + * Automatically derives: class name, required imports, and cancelable status. */ public Builder eventClass(Class clazz) { if (clazz != null) { this.eventClassName = clazz.getName(); - // Auto-detect cancelable + + String importName = getImportForClass(clazz); + if (importName != null) { + this.requiredImports = new String[]{importName}; + } + if (clazz.isAnnotationPresent(Cancelable.class)) { this.cancelable = true; } @@ -238,11 +180,16 @@ public class HookDefinition implements IHookDefinition { return this; } + /** + * Set the event class by name (for addon classes that may not be loaded). + */ + public Builder eventClass(String className) { + this.eventClassName = className; + return this; + } + /** * Set parameter names for stub generation. - * - * @param names Parameter names - * @return this builder */ public Builder paramNames(String... names) { this.paramNames = names; @@ -250,10 +197,7 @@ public class HookDefinition implements IHookDefinition { } /** - * Set required imports for this hook's event type. - * - * @param imports Fully qualified class names to import - * @return this builder + * Set required imports (only needed if using eventClass(String)). */ public Builder requiredImports(String... imports) { this.requiredImports = imports; @@ -262,20 +206,12 @@ public class HookDefinition implements IHookDefinition { /** * Set whether the event is cancelable. - * - * @param cancelable true if the event can be canceled - * @return this builder */ public Builder cancelable(boolean cancelable) { this.cancelable = cancelable; return this; } - /** - * Build the hook definition. - * - * @return A new HookDefinition instance - */ public HookDefinition build() { return new HookDefinition(this); } @@ -285,19 +221,14 @@ public class HookDefinition implements IHookDefinition { @Override public String toString() { - return "HookDefinition{" + - "hookName='" + hookName + '\'' + - ", eventClassName='" + eventClassName + '\'' + - ", cancelable=" + cancelable + - '}'; + return "HookDefinition{hookName='" + hookName + "', eventClass='" + eventClassName + "'}"; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof HookDefinition)) return false; - HookDefinition other = (HookDefinition) obj; - return hookName.equals(other.hookName); + return hookName.equals(((HookDefinition) obj).hookName); } @Override diff --git a/src/main/java/noppes/npcs/controllers/ScriptHookController.java b/src/main/java/noppes/npcs/controllers/ScriptHookController.java index 8ecc8d43f..f89177c9e 100644 --- a/src/main/java/noppes/npcs/controllers/ScriptHookController.java +++ b/src/main/java/noppes/npcs/controllers/ScriptHookController.java @@ -1,5 +1,6 @@ package noppes.npcs.controllers; +import noppes.npcs.api.event.*; import noppes.npcs.api.handler.IHookDefinition; import noppes.npcs.api.handler.IScriptHookHandler; import noppes.npcs.constants.EnumScriptType; @@ -8,28 +9,24 @@ import noppes.npcs.controllers.data.RecipeScript; import java.util.*; +import static noppes.npcs.constants.EnumScriptType.*; +import static noppes.npcs.constants.ScriptContext.*; + /** * Controller for managing script hooks. * Allows addon mods to register custom hooks that appear in script editor GUIs. * - *

Rich Hook Registration

- * Supports full hook definitions with metadata for proper stub generation: + *

Addon Hook Registration

*
{@code
- * ScriptHookController.Instance.registerHookDefinition("player",
- *     HookDefinition.builder("onDBCTransform")
- *         .eventClass(IDBCEvent.TransformEvent.class)
- *         .requiredImports("com.dbc.api.event.IDBCEvent")
- *         .build());
+ * ScriptHookController.Instance.registerHook("player",
+ *     HookDefinition.of("onDBCTransform", IDBCEvent.TransformEvent.class));
  * }
*/ public class ScriptHookController implements IScriptHookHandler { public static ScriptHookController Instance; - // Hook definitions per context - single source of truth private final Map> hookDefinitions = new HashMap<>(); - - // Revision counter for cache invalidation private int hookRevision = 0; public ScriptHookController() { @@ -37,286 +34,268 @@ public class ScriptHookController implements IScriptHookHandler { initializeBuiltInHooks(); } + // ==================== INITIALIZATION ==================== + private void initializeBuiltInHooks() { - // Initialize empty maps for all contexts from ScriptContext for (ScriptContext context : ScriptContext.values()) { if (!context.hookContext.isEmpty()) { hookDefinitions.put(context.hookContext, new LinkedHashMap<>()); } } - // NPC hooks initializeNpcHooks(); - - // Player hooks initializePlayerHooks(); - - // Block hooks initializeBlockHooks(); - - // Item hooks initializeItemHooks(); - - // Linked item hooks initializeLinkedItemHooks(); - - // Recipe hooks - registerHookDef(ScriptContext.RECIPE, RecipeScript.ScriptType.PRE.function, null, null); - registerHookDef(ScriptContext.RECIPE, RecipeScript.ScriptType.POST.function, null, null); - - // Effect hooks - registerHookDef(ScriptContext.EFFECT, EnumScriptType.ON_EFFECT_ADD.function, null, null); - registerHookDef(ScriptContext.EFFECT, EnumScriptType.ON_EFFECT_TICK.function, null, null); - registerHookDef(ScriptContext.EFFECT, EnumScriptType.ON_EFFECT_REMOVE.function, null, null); - - // Forge hooks - registerHookDef(ScriptContext.FORGE, EnumScriptType.INIT.function, null, null); - registerHookDef(ScriptContext.FORGE, EnumScriptType.CNPC_NATURAL_SPAWN.function, null, null); + initializeRecipeHooks(); + initializeEffectHooks(); + initializeForgeHooks(); } private void initializeNpcHooks() { - String ctx = ScriptContext.NPC.hookContext; - String npcImport = "noppes.npcs.api.event.INpcEvent"; - String projImport = "noppes.npcs.api.event.IProjectileEvent"; - String abilityImport = "noppes.npcs.api.event.IAbilityEvent"; - // Core lifecycle - registerHookDef(ctx, EnumScriptType.INIT.function, "noppes.npcs.api.event.INpcEvent$InitEvent", npcImport); - registerHookDef(ctx, EnumScriptType.TICK.function, "noppes.npcs.api.event.INpcEvent$UpdateEvent", npcImport); - registerHookDef(ctx, EnumScriptType.TIMER.function, "noppes.npcs.api.event.INpcEvent$TimerEvent", npcImport); + hook(NPC, INIT, INpcEvent.InitEvent.class); + hook(NPC, TICK, INpcEvent.UpdateEvent.class); + hook(NPC, TIMER, INpcEvent.TimerEvent.class); // Interaction - registerHookDef(ctx, EnumScriptType.INTERACT.function, "noppes.npcs.api.event.INpcEvent$InteractEvent", npcImport); - registerHookDef(ctx, EnumScriptType.DIALOG.function, "noppes.npcs.api.event.INpcEvent$DialogEvent", npcImport); - registerHookDef(ctx, EnumScriptType.DIALOG_CLOSE.function, "noppes.npcs.api.event.INpcEvent$DialogClosedEvent", npcImport); - registerHookDef(ctx, EnumScriptType.COLLIDE.function, "noppes.npcs.api.event.INpcEvent$CollideEvent", npcImport); + hook(NPC, INTERACT, INpcEvent.InteractEvent.class); + hook(NPC, DIALOG, INpcEvent.DialogEvent.class); + hook(NPC, DIALOG_CLOSE, INpcEvent.DialogClosedEvent.class); + hook(NPC, COLLIDE, INpcEvent.CollideEvent.class); // Combat - registerHookDef(ctx, EnumScriptType.DAMAGED.function, "noppes.npcs.api.event.INpcEvent$DamagedEvent", npcImport); - registerHookDef(ctx, EnumScriptType.KILLED.function, "noppes.npcs.api.event.INpcEvent$DiedEvent", npcImport); - registerHookDef(ctx, EnumScriptType.KILLS.function, "noppes.npcs.api.event.INpcEvent$KilledEntityEvent", npcImport); - registerHookDef(ctx, EnumScriptType.ATTACK_MELEE.function, "noppes.npcs.api.event.INpcEvent$MeleeAttackEvent", npcImport); - registerHookDef(ctx, EnumScriptType.ATTACK_SWING.function, "noppes.npcs.api.event.INpcEvent$SwingEvent", npcImport); - registerHookDef(ctx, EnumScriptType.RANGED_LAUNCHED.function, "noppes.npcs.api.event.INpcEvent$RangedLaunchedEvent", npcImport); - registerHookDef(ctx, EnumScriptType.TARGET.function, "noppes.npcs.api.event.INpcEvent$TargetEvent", npcImport); - registerHookDef(ctx, EnumScriptType.TARGET_LOST.function, "noppes.npcs.api.event.INpcEvent$TargetLostEvent", npcImport); + hook(NPC, DAMAGED, INpcEvent.DamagedEvent.class); + hook(NPC, KILLED, INpcEvent.DiedEvent.class); + hook(NPC, KILLS, INpcEvent.KilledEntityEvent.class); + hook(NPC, ATTACK_MELEE, INpcEvent.MeleeAttackEvent.class); + hook(NPC, ATTACK_SWING, INpcEvent.SwingEvent.class); + hook(NPC, RANGED_LAUNCHED, INpcEvent.RangedLaunchedEvent.class); + hook(NPC, TARGET, INpcEvent.TargetEvent.class); + hook(NPC, TARGET_LOST, INpcEvent.TargetLostEvent.class); - // Projectile events - registerHookDef(ctx, EnumScriptType.PROJECTILE_TICK.function, "noppes.npcs.api.event.IProjectileEvent$UpdateEvent", projImport); - registerHookDef(ctx, EnumScriptType.PROJECTILE_IMPACT.function, "noppes.npcs.api.event.IProjectileEvent$ImpactEvent", projImport); + // Projectile + hook(NPC, PROJECTILE_TICK, IProjectileEvent.UpdateEvent.class); + hook(NPC, PROJECTILE_IMPACT, IProjectileEvent.ImpactEvent.class); - // Ability events - registerHookDef(ctx, EnumScriptType.ABILITY_START.function, "noppes.npcs.api.event.IAbilityEvent$StartEvent", abilityImport); - registerHookDef(ctx, EnumScriptType.ABILITY_EXECUTE.function, "noppes.npcs.api.event.IAbilityEvent$ExecuteEvent", abilityImport); - registerHookDef(ctx, EnumScriptType.ABILITY_HIT.function, "noppes.npcs.api.event.IAbilityEvent$HitEvent", abilityImport); - registerHookDef(ctx, EnumScriptType.ABILITY_TICK.function, "noppes.npcs.api.event.IAbilityEvent$TickEvent", abilityImport); - registerHookDef(ctx, EnumScriptType.ABILITY_INTERRUPT.function, "noppes.npcs.api.event.IAbilityEvent$InterruptEvent", abilityImport); - registerHookDef(ctx, EnumScriptType.ABILITY_COMPLETE.function, "noppes.npcs.api.event.IAbilityEvent$CompleteEvent", abilityImport); + // Ability + hook(NPC, ABILITY_START, IAbilityEvent.StartEvent.class); + hook(NPC, ABILITY_EXECUTE, IAbilityEvent.ExecuteEvent.class); + hook(NPC, ABILITY_HIT, IAbilityEvent.HitEvent.class); + hook(NPC, ABILITY_TICK, IAbilityEvent.TickEvent.class); + hook(NPC, ABILITY_INTERRUPT, IAbilityEvent.InterruptEvent.class); + hook(NPC, ABILITY_COMPLETE, IAbilityEvent.CompleteEvent.class); } private void initializePlayerHooks() { - String ctx = ScriptContext.PLAYER.hookContext; - String playerImport = "noppes.npcs.api.event.IPlayerEvent"; - String questImport = "noppes.npcs.api.event.IQuestEvent"; - String dialogImport = "noppes.npcs.api.event.IDialogEvent"; - String factionImport = "noppes.npcs.api.event.IFactionEvent"; - String partyImport = "noppes.npcs.api.event.IPartyEvent"; - String guiImport = "noppes.npcs.api.event.ICustomGuiEvent"; - String animImport = "noppes.npcs.api.event.IAnimationEvent"; - // Core lifecycle - registerHookDef(ctx, EnumScriptType.INIT.function, "noppes.npcs.api.event.IPlayerEvent$InitEvent", playerImport); - registerHookDef(ctx, EnumScriptType.TICK.function, "noppes.npcs.api.event.IPlayerEvent$UpdateEvent", playerImport); - registerHookDef(ctx, EnumScriptType.TIMER.function, "noppes.npcs.api.event.IPlayerEvent$TimerEvent", playerImport); + hook(PLAYER, INIT, IPlayerEvent.InitEvent.class); + hook(PLAYER, TICK, IPlayerEvent.UpdateEvent.class); + hook(PLAYER, TIMER, IPlayerEvent.TimerEvent.class); // Combat - registerHookDef(ctx, EnumScriptType.ATTACK.function, "noppes.npcs.api.event.IPlayerEvent$AttackEvent", playerImport); - registerHookDef(ctx, EnumScriptType.ATTACKED.function, "noppes.npcs.api.event.IPlayerEvent$AttackedEvent", playerImport); - registerHookDef(ctx, EnumScriptType.DAMAGED.function, "noppes.npcs.api.event.IPlayerEvent$DamagedEvent", playerImport); - registerHookDef(ctx, EnumScriptType.DAMAGED_ENTITY.function, "noppes.npcs.api.event.IPlayerEvent$DamagedEntityEvent", playerImport); - registerHookDef(ctx, EnumScriptType.KILLS.function, "noppes.npcs.api.event.IPlayerEvent$KilledEntityEvent", playerImport); - registerHookDef(ctx, EnumScriptType.KILLED.function, "noppes.npcs.api.event.IPlayerEvent$DiedEvent", playerImport); + hook(PLAYER, ATTACK, IPlayerEvent.AttackEvent.class); + hook(PLAYER, ATTACKED, IPlayerEvent.AttackedEvent.class); + hook(PLAYER, DAMAGED, IPlayerEvent.DamagedEvent.class); + hook(PLAYER, DAMAGED_ENTITY, IPlayerEvent.DamagedEntityEvent.class); + hook(PLAYER, KILLS, IPlayerEvent.KilledEntityEvent.class); + hook(PLAYER, KILLED, IPlayerEvent.DiedEvent.class); // Interaction - registerHookDef(ctx, EnumScriptType.INTERACT.function, "noppes.npcs.api.event.IPlayerEvent$InteractEvent", playerImport); - registerHookDef(ctx, EnumScriptType.RIGHT_CLICK.function, "noppes.npcs.api.event.IPlayerEvent$RightClickEvent", playerImport); - registerHookDef(ctx, EnumScriptType.BREAK_BLOCK.function, "noppes.npcs.api.event.IPlayerEvent$BreakEvent", playerImport); - registerHookDef(ctx, EnumScriptType.CHAT.function, "noppes.npcs.api.event.IPlayerEvent$ChatEvent", playerImport); + hook(PLAYER, INTERACT, IPlayerEvent.InteractEvent.class); + hook(PLAYER, RIGHT_CLICK, IPlayerEvent.RightClickEvent.class); + hook(PLAYER, BREAK_BLOCK, IPlayerEvent.BreakEvent.class); + hook(PLAYER, CHAT, IPlayerEvent.ChatEvent.class); // Connection - registerHookDef(ctx, EnumScriptType.LOGIN.function, "noppes.npcs.api.event.IPlayerEvent$LoginEvent", playerImport); - registerHookDef(ctx, EnumScriptType.LOGOUT.function, "noppes.npcs.api.event.IPlayerEvent$LogoutEvent", playerImport); - registerHookDef(ctx, EnumScriptType.RESPAWN.function, "noppes.npcs.api.event.IPlayerEvent$RespawnEvent", playerImport); + hook(PLAYER, LOGIN, IPlayerEvent.LoginEvent.class); + hook(PLAYER, LOGOUT, IPlayerEvent.LogoutEvent.class); + hook(PLAYER, RESPAWN, IPlayerEvent.RespawnEvent.class); // Input - registerHookDef(ctx, EnumScriptType.KEY_PRESSED.function, "noppes.npcs.api.event.IPlayerEvent$KeyPressedEvent", playerImport); - registerHookDef(ctx, EnumScriptType.MOUSE_CLICKED.function, "noppes.npcs.api.event.IPlayerEvent$MouseClickedEvent", playerImport); + hook(PLAYER, KEY_PRESSED, IPlayerEvent.KeyPressedEvent.class); + hook(PLAYER, MOUSE_CLICKED, IPlayerEvent.MouseClickedEvent.class); // Items - registerHookDef(ctx, EnumScriptType.PICKUP.function, "noppes.npcs.api.event.IPlayerEvent$PickUpEvent", playerImport); - registerHookDef(ctx, EnumScriptType.TOSS.function, "noppes.npcs.api.event.IPlayerEvent$TossEvent", playerImport); - registerHookDef(ctx, EnumScriptType.DROP.function, "noppes.npcs.api.event.IPlayerEvent$DropEvent", playerImport); - registerHookDef(ctx, EnumScriptType.PICKUP_XP.function, "noppes.npcs.api.event.IPlayerEvent$PickupXPEvent", playerImport); - registerHookDef(ctx, EnumScriptType.START_USING_ITEM.function, "noppes.npcs.api.event.IPlayerEvent$StartUsingItem", playerImport); - registerHookDef(ctx, EnumScriptType.USING_ITEM.function, "noppes.npcs.api.event.IPlayerEvent$UsingItem", playerImport); - registerHookDef(ctx, EnumScriptType.STOP_USING_ITEM.function, "noppes.npcs.api.event.IPlayerEvent$StopUsingItem", playerImport); - registerHookDef(ctx, EnumScriptType.FINISH_USING_ITEM.function, "noppes.npcs.api.event.IPlayerEvent$FinishUsingItem", playerImport); - registerHookDef(ctx, EnumScriptType.CONTAINER_OPEN.function, "noppes.npcs.api.event.IPlayerEvent$ContainerOpen", playerImport); + hook(PLAYER, PICKUP, IPlayerEvent.PickUpEvent.class); + hook(PLAYER, TOSS, IPlayerEvent.TossEvent.class); + hook(PLAYER, DROP, IPlayerEvent.DropEvent.class); + hook(PLAYER, PICKUP_XP, IPlayerEvent.PickupXPEvent.class); + hook(PLAYER, START_USING_ITEM, IPlayerEvent.StartUsingItem.class); + hook(PLAYER, USING_ITEM, IPlayerEvent.UsingItem.class); + hook(PLAYER, STOP_USING_ITEM, IPlayerEvent.StopUsingItem.class); + hook(PLAYER, FINISH_USING_ITEM, IPlayerEvent.FinishUsingItem.class); + hook(PLAYER, CONTAINER_OPEN, IPlayerEvent.ContainerOpen.class); // Movement - registerHookDef(ctx, EnumScriptType.JUMP.function, "noppes.npcs.api.event.IPlayerEvent$JumpEvent", playerImport); - registerHookDef(ctx, EnumScriptType.FALL.function, "noppes.npcs.api.event.IPlayerEvent$FallEvent", playerImport); - registerHookDef(ctx, EnumScriptType.CHANGED_DIM.function, "noppes.npcs.api.event.IPlayerEvent$ChangedDimension", playerImport); + hook(PLAYER, JUMP, IPlayerEvent.JumpEvent.class); + hook(PLAYER, FALL, IPlayerEvent.FallEvent.class); + hook(PLAYER, CHANGED_DIM, IPlayerEvent.ChangedDimension.class); // World - registerHookDef(ctx, EnumScriptType.RANGED_CHARGE.function, "noppes.npcs.api.event.IPlayerEvent$RangedChargeEvent", playerImport); - registerHookDef(ctx, EnumScriptType.RANGED_LAUNCHED.function, "noppes.npcs.api.event.IPlayerEvent$RangedLaunchedEvent", playerImport); - registerHookDef(ctx, EnumScriptType.USE_HOE.function, "noppes.npcs.api.event.IPlayerEvent$UseHoeEvent", playerImport); - registerHookDef(ctx, EnumScriptType.BONEMEAL.function, "noppes.npcs.api.event.IPlayerEvent$BonemealEvent", playerImport); - registerHookDef(ctx, EnumScriptType.FILL_BUCKET.function, "noppes.npcs.api.event.IPlayerEvent$FillBucketEvent", playerImport); - registerHookDef(ctx, EnumScriptType.WAKE_UP.function, "noppes.npcs.api.event.IPlayerEvent$WakeUpEvent", playerImport); - registerHookDef(ctx, EnumScriptType.SLEEP.function, "noppes.npcs.api.event.IPlayerEvent$SleepEvent", playerImport); - registerHookDef(ctx, EnumScriptType.PLAYSOUND.function, "noppes.npcs.api.event.IPlayerEvent$SoundEvent", playerImport); - registerHookDef(ctx, EnumScriptType.LIGHTNING.function, "noppes.npcs.api.event.IPlayerEvent$LightningEvent", playerImport); - registerHookDef(ctx, EnumScriptType.SCRIPT_COMMAND.function, "noppes.npcs.api.event.ICustomNPCsEvent$ScriptedCommandEvent", "noppes.npcs.api.event.ICustomNPCsEvent"); + hook(PLAYER, RANGED_CHARGE, IPlayerEvent.RangedChargeEvent.class); + hook(PLAYER, RANGED_LAUNCHED, IPlayerEvent.RangedLaunchedEvent.class); + hook(PLAYER, USE_HOE, IPlayerEvent.UseHoeEvent.class); + hook(PLAYER, BONEMEAL, IPlayerEvent.BonemealEvent.class); + hook(PLAYER, FILL_BUCKET, IPlayerEvent.FillBucketEvent.class); + hook(PLAYER, WAKE_UP, IPlayerEvent.WakeUpEvent.class); + hook(PLAYER, SLEEP, IPlayerEvent.SleepEvent.class); + hook(PLAYER, PLAYSOUND, IPlayerEvent.SoundEvent.class); + hook(PLAYER, LIGHTNING, IPlayerEvent.LightningEvent.class); + hook(PLAYER, SCRIPT_COMMAND, ICustomNPCsEvent.ScriptedCommandEvent.class); - // Quest events - registerHookDef(ctx, EnumScriptType.QUEST_START.function, "noppes.npcs.api.event.IQuestEvent$QuestStartEvent", questImport); - registerHookDef(ctx, EnumScriptType.QUEST_COMPLETED.function, "noppes.npcs.api.event.IQuestEvent$QuestCompletedEvent", questImport); - registerHookDef(ctx, EnumScriptType.QUEST_TURNIN.function, "noppes.npcs.api.event.IQuestEvent$QuestTurnedInEvent", questImport); + // Quest + hook(PLAYER, QUEST_START, IQuestEvent.QuestStartEvent.class); + hook(PLAYER, QUEST_COMPLETED, IQuestEvent.QuestCompletedEvent.class); + hook(PLAYER, QUEST_TURNIN, IQuestEvent.QuestTurnedInEvent.class); - // Dialog events - registerHookDef(ctx, EnumScriptType.DIALOG_OPEN.function, "noppes.npcs.api.event.IDialogEvent$DialogOpen", dialogImport); - registerHookDef(ctx, EnumScriptType.DIALOG_OPTION.function, "noppes.npcs.api.event.IDialogEvent$DialogOption", dialogImport); - registerHookDef(ctx, EnumScriptType.DIALOG_CLOSE.function, "noppes.npcs.api.event.IDialogEvent$DialogClosed", dialogImport); + // Dialog + hook(PLAYER, DIALOG_OPEN, IDialogEvent.DialogOpen.class); + hook(PLAYER, DIALOG_OPTION, IDialogEvent.DialogOption.class); + hook(PLAYER, DIALOG_CLOSE, IDialogEvent.DialogClosed.class); - // Faction events - registerHookDef(ctx, EnumScriptType.FACTION_POINTS.function, "noppes.npcs.api.event.IFactionEvent$FactionPoints", factionImport); + // Faction + hook(PLAYER, FACTION_POINTS, IFactionEvent.FactionPoints.class); - // Custom GUI events - registerHookDef(ctx, EnumScriptType.CUSTOM_GUI_CLOSED.function, "noppes.npcs.api.event.ICustomGuiEvent$CloseEvent", guiImport); - registerHookDef(ctx, EnumScriptType.CUSTOM_GUI_BUTTON.function, "noppes.npcs.api.event.ICustomGuiEvent$ButtonEvent", guiImport); - registerHookDef(ctx, EnumScriptType.CUSTOM_GUI_SLOT.function, "noppes.npcs.api.event.ICustomGuiEvent$SlotEvent", guiImport); - registerHookDef(ctx, EnumScriptType.CUSTOM_GUI_SLOT_CLICKED.function, "noppes.npcs.api.event.ICustomGuiEvent$SlotClickEvent", guiImport); - registerHookDef(ctx, EnumScriptType.CUSTOM_GUI_SCROLL.function, "noppes.npcs.api.event.ICustomGuiEvent$ScrollEvent", guiImport); - registerHookDef(ctx, EnumScriptType.CUSTOM_GUI_TEXTFIELD.function, "noppes.npcs.api.event.ICustomGuiEvent$UnfocusedEvent", guiImport); + // Custom GUI + hook(PLAYER, CUSTOM_GUI_CLOSED, ICustomGuiEvent.CloseEvent.class); + hook(PLAYER, CUSTOM_GUI_BUTTON, ICustomGuiEvent.ButtonEvent.class); + hook(PLAYER, CUSTOM_GUI_SLOT, ICustomGuiEvent.SlotEvent.class); + hook(PLAYER, CUSTOM_GUI_SLOT_CLICKED, ICustomGuiEvent.SlotClickEvent.class); + hook(PLAYER, CUSTOM_GUI_SCROLL, ICustomGuiEvent.ScrollEvent.class); + hook(PLAYER, CUSTOM_GUI_TEXTFIELD, ICustomGuiEvent.UnfocusedEvent.class); - // Party events - registerHookDef(ctx, EnumScriptType.PARTY_QUEST_COMPLETED.function, "noppes.npcs.api.event.IPartyEvent$PartyQuestCompletedEvent", partyImport); - registerHookDef(ctx, EnumScriptType.PARTY_QUEST_SET.function, "noppes.npcs.api.event.IPartyEvent$PartyQuestSetEvent", partyImport); - registerHookDef(ctx, EnumScriptType.PARTY_QUEST_TURNED_IN.function, "noppes.npcs.api.event.IPartyEvent$PartyQuestTurnedInEvent", partyImport); - registerHookDef(ctx, EnumScriptType.PARTY_INVITE.function, "noppes.npcs.api.event.IPartyEvent$PartyInviteEvent", partyImport); - registerHookDef(ctx, EnumScriptType.PARTY_KICK.function, "noppes.npcs.api.event.IPartyEvent$PartyKickEvent", partyImport); - registerHookDef(ctx, EnumScriptType.PARTY_LEAVE.function, "noppes.npcs.api.event.IPartyEvent$PartyLeaveEvent", partyImport); - registerHookDef(ctx, EnumScriptType.PARTY_DISBAND.function, "noppes.npcs.api.event.IPartyEvent$PartyDisbandEvent", partyImport); + // Party + hook(PLAYER, PARTY_QUEST_COMPLETED, IPartyEvent.PartyQuestCompletedEvent.class); + hook(PLAYER, PARTY_QUEST_SET, IPartyEvent.PartyQuestSetEvent.class); + hook(PLAYER, PARTY_QUEST_TURNED_IN, IPartyEvent.PartyQuestTurnedInEvent.class); + hook(PLAYER, PARTY_INVITE, IPartyEvent.PartyInviteEvent.class); + hook(PLAYER, PARTY_KICK, IPartyEvent.PartyKickEvent.class); + hook(PLAYER, PARTY_LEAVE, IPartyEvent.PartyLeaveEvent.class); + hook(PLAYER, PARTY_DISBAND, IPartyEvent.PartyDisbandEvent.class); - // Animation events - registerHookDef(ctx, EnumScriptType.ANIMATION_START.function, "noppes.npcs.api.event.IAnimationEvent$Started", animImport); - registerHookDef(ctx, EnumScriptType.ANIMATION_END.function, "noppes.npcs.api.event.IAnimationEvent$Ended", animImport); - registerHookDef(ctx, EnumScriptType.ANIMATION_FRAME_ENTER.function, "noppes.npcs.api.event.IAnimationEvent$IFrameEvent$Entered", animImport); - registerHookDef(ctx, EnumScriptType.ANIMATION_FRAME_EXIT.function, "noppes.npcs.api.event.IAnimationEvent$IFrameEvent$Exited", animImport); + // Animation + hook(PLAYER, ANIMATION_START, IAnimationEvent.Started.class); + hook(PLAYER, ANIMATION_END, IAnimationEvent.Ended.class); + hook(PLAYER, ANIMATION_FRAME_ENTER, IAnimationEvent.IFrameEvent.Entered.class); + hook(PLAYER, ANIMATION_FRAME_EXIT, IAnimationEvent.IFrameEvent.Exited.class); - // Profile events - registerHookDef(ctx, EnumScriptType.PROFILE_CHANGE.function, "noppes.npcs.api.event.IPlayerEvent$ProfileEvent$Changed", playerImport); - registerHookDef(ctx, EnumScriptType.PROFILE_REMOVE.function, "noppes.npcs.api.event.IPlayerEvent$ProfileEvent$Removed", playerImport); - registerHookDef(ctx, EnumScriptType.PROFILE_CREATE.function, "noppes.npcs.api.event.IPlayerEvent$ProfileEvent$Create", playerImport); + // Profile + hook(PLAYER, PROFILE_CHANGE, IPlayerEvent.ProfileEvent.Changed.class); + hook(PLAYER, PROFILE_REMOVE, IPlayerEvent.ProfileEvent.Removed.class); + hook(PLAYER, PROFILE_CREATE, IPlayerEvent.ProfileEvent.Create.class); - // Effect events (for player context) - registerHookDef(ctx, EnumScriptType.ON_EFFECT_ADD.function, "noppes.npcs.api.event.IPlayerEvent$EffectEvent$Added", playerImport); - registerHookDef(ctx, EnumScriptType.ON_EFFECT_TICK.function, "noppes.npcs.api.event.IPlayerEvent$EffectEvent$Ticked", playerImport); - registerHookDef(ctx, EnumScriptType.ON_EFFECT_REMOVE.function, "noppes.npcs.api.event.IPlayerEvent$EffectEvent$Removed", playerImport); + // Effect (player context) + hook(PLAYER, ON_EFFECT_ADD, IPlayerEvent.EffectEvent.Added.class); + hook(PLAYER, ON_EFFECT_TICK, IPlayerEvent.EffectEvent.Ticked.class); + hook(PLAYER, ON_EFFECT_REMOVE, IPlayerEvent.EffectEvent.Removed.class); } private void initializeBlockHooks() { - String ctx = ScriptContext.BLOCK.hookContext; - String blockImport = "noppes.npcs.api.event.IBlockEvent"; - - registerHookDef(ctx, EnumScriptType.INIT.function, "noppes.npcs.api.event.IBlockEvent$InitEvent", blockImport); - registerHookDef(ctx, EnumScriptType.TICK.function, "noppes.npcs.api.event.IBlockEvent$UpdateEvent", blockImport); - registerHookDef(ctx, EnumScriptType.INTERACT.function, "noppes.npcs.api.event.IBlockEvent$InteractEvent", blockImport); - registerHookDef(ctx, EnumScriptType.FALLEN_UPON.function, "noppes.npcs.api.event.IBlockEvent$EntityFallenUponEvent", blockImport); - registerHookDef(ctx, EnumScriptType.REDSTONE.function, "noppes.npcs.api.event.IBlockEvent$RedstoneEvent", blockImport); - registerHookDef(ctx, EnumScriptType.BROKEN.function, "noppes.npcs.api.event.IBlockEvent$BreakEvent", blockImport); - registerHookDef(ctx, EnumScriptType.EXPLODED.function, "noppes.npcs.api.event.IBlockEvent$ExplodedEvent", blockImport); - registerHookDef(ctx, EnumScriptType.RAIN_FILLED.function, "noppes.npcs.api.event.IBlockEvent$RainFillEvent", blockImport); - registerHookDef(ctx, EnumScriptType.NEIGHBOR_CHANGED.function, "noppes.npcs.api.event.IBlockEvent$NeighborChangedEvent", blockImport); - registerHookDef(ctx, EnumScriptType.CLICKED.function, "noppes.npcs.api.event.IBlockEvent$ClickedEvent", blockImport); - registerHookDef(ctx, EnumScriptType.HARVESTED.function, "noppes.npcs.api.event.IBlockEvent$HarvestedEvent", blockImport); - registerHookDef(ctx, EnumScriptType.COLLIDE.function, "noppes.npcs.api.event.IBlockEvent$CollidedEvent", blockImport); - registerHookDef(ctx, EnumScriptType.TIMER.function, "noppes.npcs.api.event.IBlockEvent$TimerEvent", blockImport); + hook(BLOCK, INIT, IBlockEvent.InitEvent.class); + hook(BLOCK, TICK, IBlockEvent.UpdateEvent.class); + hook(BLOCK, INTERACT, IBlockEvent.InteractEvent.class); + hook(BLOCK, FALLEN_UPON, IBlockEvent.EntityFallenUponEvent.class); + hook(BLOCK, REDSTONE, IBlockEvent.RedstoneEvent.class); + hook(BLOCK, BROKEN, IBlockEvent.BreakEvent.class); + hook(BLOCK, EXPLODED, IBlockEvent.ExplodedEvent.class); + hook(BLOCK, RAIN_FILLED, IBlockEvent.RainFillEvent.class); + hook(BLOCK, NEIGHBOR_CHANGED, IBlockEvent.NeighborChangedEvent.class); + hook(BLOCK, CLICKED, IBlockEvent.ClickedEvent.class); + hook(BLOCK, HARVESTED, IBlockEvent.HarvestedEvent.class); + hook(BLOCK, COLLIDE, IBlockEvent.CollidedEvent.class); + hook(BLOCK, TIMER, IBlockEvent.TimerEvent.class); } private void initializeItemHooks() { - String ctx = ScriptContext.ITEM.hookContext; - String itemImport = "noppes.npcs.api.event.IItemEvent"; - - registerHookDef(ctx, EnumScriptType.INIT.function, "noppes.npcs.api.event.IItemEvent$InitEvent", itemImport); - registerHookDef(ctx, EnumScriptType.TICK.function, "noppes.npcs.api.event.IItemEvent$UpdateEvent", itemImport); - registerHookDef(ctx, EnumScriptType.TOSSED.function, "noppes.npcs.api.event.IItemEvent$TossedEvent", itemImport); - registerHookDef(ctx, EnumScriptType.PICKEDUP.function, "noppes.npcs.api.event.IItemEvent$PickedUpEvent", itemImport); - registerHookDef(ctx, EnumScriptType.SPAWN.function, "noppes.npcs.api.event.IItemEvent$SpawnEvent", itemImport); - registerHookDef(ctx, EnumScriptType.INTERACT.function, "noppes.npcs.api.event.IItemEvent$InteractEvent", itemImport); - registerHookDef(ctx, EnumScriptType.RIGHT_CLICK.function, "noppes.npcs.api.event.IItemEvent$RightClickEvent", itemImport); - registerHookDef(ctx, EnumScriptType.ATTACK.function, "noppes.npcs.api.event.IItemEvent$AttackEvent", itemImport); - registerHookDef(ctx, EnumScriptType.START_USING_ITEM.function, "noppes.npcs.api.event.IItemEvent$StartUsingItem", itemImport); - registerHookDef(ctx, EnumScriptType.USING_ITEM.function, "noppes.npcs.api.event.IItemEvent$UsingItem", itemImport); - registerHookDef(ctx, EnumScriptType.STOP_USING_ITEM.function, "noppes.npcs.api.event.IItemEvent$StopUsingItem", itemImport); - registerHookDef(ctx, EnumScriptType.FINISH_USING_ITEM.function, "noppes.npcs.api.event.IItemEvent$FinishUsingItem", itemImport); + hook(ITEM, INIT, IItemEvent.InitEvent.class); + hook(ITEM, TICK, IItemEvent.UpdateEvent.class); + hook(ITEM, TOSSED, IItemEvent.TossedEvent.class); + hook(ITEM, PICKEDUP, IItemEvent.PickedUpEvent.class); + hook(ITEM, SPAWN, IItemEvent.SpawnEvent.class); + hook(ITEM, INTERACT, IItemEvent.InteractEvent.class); + hook(ITEM, RIGHT_CLICK, IItemEvent.RightClickEvent.class); + hook(ITEM, ATTACK, IItemEvent.AttackEvent.class); + hook(ITEM, START_USING_ITEM, IItemEvent.StartUsingItem.class); + hook(ITEM, USING_ITEM, IItemEvent.UsingItem.class); + hook(ITEM, STOP_USING_ITEM, IItemEvent.StopUsingItem.class); + hook(ITEM, FINISH_USING_ITEM, IItemEvent.FinishUsingItem.class); } private void initializeLinkedItemHooks() { - String ctx = ScriptContext.LINKED_ITEM.hookContext; - String itemImport = "noppes.npcs.api.event.IItemEvent"; - String linkedImport = "noppes.npcs.api.event.ILinkedItemEvent"; - - // Linked item specific hooks - registerHookDef(ctx, EnumScriptType.LINKED_ITEM_BUILD.function, "noppes.npcs.api.event.ILinkedItemEvent$BuildEvent", linkedImport); - registerHookDef(ctx, EnumScriptType.LINKED_ITEM_VERSION.function, "noppes.npcs.api.event.ILinkedItemEvent$VersionChangeEvent", linkedImport); + // Linked item specific + hook(LINKED_ITEM, LINKED_ITEM_BUILD, ILinkedItemEvent.BuildEvent.class); + hook(LINKED_ITEM, LINKED_ITEM_VERSION, ILinkedItemEvent.VersionChangeEvent.class); // Standard item hooks - registerHookDef(ctx, EnumScriptType.INIT.function, "noppes.npcs.api.event.IItemEvent$InitEvent", itemImport); - registerHookDef(ctx, EnumScriptType.TICK.function, "noppes.npcs.api.event.IItemEvent$UpdateEvent", itemImport); - registerHookDef(ctx, EnumScriptType.TOSSED.function, "noppes.npcs.api.event.IItemEvent$TossedEvent", itemImport); - registerHookDef(ctx, EnumScriptType.PICKEDUP.function, "noppes.npcs.api.event.IItemEvent$PickedUpEvent", itemImport); - registerHookDef(ctx, EnumScriptType.SPAWN.function, "noppes.npcs.api.event.IItemEvent$SpawnEvent", itemImport); - registerHookDef(ctx, EnumScriptType.INTERACT.function, "noppes.npcs.api.event.IItemEvent$InteractEvent", itemImport); - registerHookDef(ctx, EnumScriptType.RIGHT_CLICK.function, "noppes.npcs.api.event.IItemEvent$RightClickEvent", itemImport); - registerHookDef(ctx, EnumScriptType.ATTACK.function, "noppes.npcs.api.event.IItemEvent$AttackEvent", itemImport); - registerHookDef(ctx, EnumScriptType.START_USING_ITEM.function, "noppes.npcs.api.event.IItemEvent$StartUsingItem", itemImport); - registerHookDef(ctx, EnumScriptType.USING_ITEM.function, "noppes.npcs.api.event.IItemEvent$UsingItem", itemImport); - registerHookDef(ctx, EnumScriptType.STOP_USING_ITEM.function, "noppes.npcs.api.event.IItemEvent$StopUsingItem", itemImport); - registerHookDef(ctx, EnumScriptType.FINISH_USING_ITEM.function, "noppes.npcs.api.event.IItemEvent$FinishUsingItem", itemImport); + hook(LINKED_ITEM, INIT, IItemEvent.InitEvent.class); + hook(LINKED_ITEM, TICK, IItemEvent.UpdateEvent.class); + hook(LINKED_ITEM, TOSSED, IItemEvent.TossedEvent.class); + hook(LINKED_ITEM, PICKEDUP, IItemEvent.PickedUpEvent.class); + hook(LINKED_ITEM, SPAWN, IItemEvent.SpawnEvent.class); + hook(LINKED_ITEM, INTERACT, IItemEvent.InteractEvent.class); + hook(LINKED_ITEM, RIGHT_CLICK, IItemEvent.RightClickEvent.class); + hook(LINKED_ITEM, ATTACK, IItemEvent.AttackEvent.class); + hook(LINKED_ITEM, START_USING_ITEM, IItemEvent.StartUsingItem.class); + hook(LINKED_ITEM, USING_ITEM, IItemEvent.UsingItem.class); + hook(LINKED_ITEM, STOP_USING_ITEM, IItemEvent.StopUsingItem.class); + hook(LINKED_ITEM, FINISH_USING_ITEM, IItemEvent.FinishUsingItem.class); } - /** - * Internal helper to register a hook definition. - */ - private void registerHookDef(String context, String hookName, String eventClassName, String... imports) { - Map contextDefs = hookDefinitions.get(context); - if (contextDefs == null) { - contextDefs = new LinkedHashMap<>(); - hookDefinitions.put(context, contextDefs); - } - - HookDefinition.Builder builder = HookDefinition.builder(hookName); - if (eventClassName != null) { - builder.eventClass(eventClassName); - } - if (imports != null && imports.length > 0 && imports[0] != null) { - builder.requiredImports(imports); - } - contextDefs.put(hookName, builder.build()); + private void initializeRecipeHooks() { + hook(RECIPE, RecipeScript.ScriptType.PRE.function, IRecipeEvent.Pre.class); + hook(RECIPE, RecipeScript.ScriptType.POST.function, IRecipeEvent.Post.class); } - private void registerHookDef(ScriptContext context, String hookName, String eventClassName, String... imports) { - registerHookDef(context.hookContext, hookName, eventClassName, imports); + private void initializeEffectHooks() { + // Effect context uses IPlayerEvent.EffectEvent (same as player context) + hook(EFFECT, ON_EFFECT_ADD, IPlayerEvent.EffectEvent.Added.class); + hook(EFFECT, ON_EFFECT_TICK, IPlayerEvent.EffectEvent.Ticked.class); + hook(EFFECT, ON_EFFECT_REMOVE, IPlayerEvent.EffectEvent.Removed.class); + } + + private void initializeForgeHooks() { + hook(FORGE, INIT, IForgeEvent.InitEvent.class); + hook(FORGE, FORGE_WORLD, IForgeEvent.WorldEvent.class); + hook(FORGE, FORGE_ENTITY, IForgeEvent.EntityEvent.class); + hook(FORGE, CNPC_NATURAL_SPAWN, ICustomNPCsEvent.CNPCNaturalSpawnEvent.class); + } + + // ==================== INTERNAL REGISTRATION ==================== + + private void hook(ScriptContext context, EnumScriptType type, Class eventClass) { + hookDefinitions.get(context.hookContext) + .put(type.function, HookDefinition.of(type.function, eventClass)); + } + + private void hook(ScriptContext context, EnumScriptType type) { + hookDefinitions.get(context.hookContext) + .put(type.function, HookDefinition.simple(type.function)); + } + + private void hook(ScriptContext context, String hookName) { + hookDefinitions.get(context.hookContext) + .put(hookName, HookDefinition.simple(hookName)); + } + + private void hook(ScriptContext context, String hookName, Class eventClass) { + hookDefinitions.get(context.hookContext) + .put(hookName, HookDefinition.of(hookName, eventClass)); } // ==================== PUBLIC API ==================== - @Override - public void registerHookDefinition(String context, IHookDefinition definition) { - if (context == null || definition == null || definition.hookName() == null) + /** + * Register a hook with event class (auto-derives imports). + * For addon mods to register custom hooks. + * + * @param context Hook context (e.g., "npc", "player") + * @param hookName Function name in scripts + * @param eventClass The event class (imports auto-derived from enclosing class) + */ + public void registerHook(String context, String hookName, Class eventClass) { + if (context == null || hookName == null) return; Map contextDefs = hookDefinitions.get(context); @@ -325,19 +304,70 @@ public class ScriptHookController implements IScriptHookHandler { hookDefinitions.put(context, contextDefs); } + contextDefs.put(hookName, HookDefinition.of(hookName, eventClass)); + hookRevision++; + } + + public void registerHook(ScriptContext context, String hookName, Class eventClass) { + registerHook(context.hookContext, hookName, eventClass); + } + + /** + * Register a simple hook with no event class metadata. + */ + public void registerHook(String context, String hookName) { + if (context == null || hookName == null) + return; + + Map contextDefs = hookDefinitions.get(context); + if (contextDefs == null) { + contextDefs = new LinkedHashMap<>(); + hookDefinitions.put(context, contextDefs); + } + + if (!contextDefs.containsKey(hookName)) { + contextDefs.put(hookName, HookDefinition.simple(hookName)); + hookRevision++; + } + } + + public void registerHook(ScriptContext context, String hookName) { + registerHook(context.hookContext, hookName); + } + + /** + * Register a hook definition for addon mods. + */ + public void registerHook(String context, HookDefinition definition) { + if (context == null || definition == null) + return; + + Map contextDefs = hookDefinitions.get(context); + if (contextDefs == null) { + contextDefs = new LinkedHashMap<>(); + hookDefinitions.put(context, contextDefs); + } + + contextDefs.put(definition.hookName(), definition); + hookRevision++; + } + + public void registerHook(ScriptContext context, HookDefinition definition) { + registerHook(context.hookContext, definition); + } + + @Override + public void registerHookDefinition(String context, IHookDefinition definition) { if (definition instanceof HookDefinition) { - contextDefs.put(definition.hookName(), (HookDefinition) definition); - } else { - HookDefinition def = HookDefinition.builder(definition.hookName()) + registerHook(context, (HookDefinition) definition); + } else if (definition != null) { + registerHook(context, HookDefinition.builder(definition.hookName()) .eventClass(definition.eventClassName()) .paramNames(definition.paramNames()) .requiredImports(definition.requiredImports()) .cancelable(definition.isCancelable()) - .build(); - contextDefs.put(definition.hookName(), def); + .build()); } - - hookRevision++; } @Override @@ -361,87 +391,6 @@ public class ScriptHookController implements IScriptHookHandler { return new ArrayList<>(contextDefs.values()); } - @Override - public int getHookRevision() { - return hookRevision; - } - - // ==================== CONVENIENCE METHODS ==================== - - public void registerHook(ScriptContext context, String functionName) { - registerHook(context.hookContext, functionName); - } - - public void registerHooks(ScriptContext context, String... functionNames) { - for (String fn : functionNames) { - registerHook(context.hookContext, fn); - } - } - - public void registerHookDefinition(ScriptContext context, IHookDefinition definition) { - registerHookDefinition(context.hookContext, definition); - } - - // ==================== LEGACY API (IScriptHookHandler) ==================== - - @Override - public void registerHook(String context, String functionName) { - if (context == null || functionName == null || functionName.isEmpty()) - return; - - Map contextDefs = hookDefinitions.get(context); - if (contextDefs == null) { - contextDefs = new LinkedHashMap<>(); - hookDefinitions.put(context, contextDefs); - } - - // Only add if not already present - if (!contextDefs.containsKey(functionName)) { - contextDefs.put(functionName, HookDefinition.builder(functionName).build()); - hookRevision++; - } - } - - @Override - public void registerHooks(String functionName, String... contexts) { - if (functionName == null || functionName.isEmpty() || contexts == null) - return; - - for (String context : contexts) { - registerHook(context, functionName); - } - } - - @Override - public void unregisterHook(String context, String functionName) { - if (context == null || functionName == null) - return; - - Map contextDefs = hookDefinitions.get(context); - if (contextDefs != null && contextDefs.remove(functionName) != null) { - hookRevision++; - } - } - - @Override - public void unregisterHookFromAll(String functionName) { - if (functionName == null) - return; - - for (Map contextDefs : hookDefinitions.values()) { - if (contextDefs.remove(functionName) != null) { - hookRevision++; - } - } - } - - @Override - public List getAddonHooks(String context) { - // With unified storage, we can't distinguish addon vs built-in - // Return empty for backwards compatibility - return Collections.emptyList(); - } - @Override public List getAllHooks(String context) { if (context == null) @@ -455,12 +404,17 @@ public class ScriptHookController implements IScriptHookHandler { } @Override - public boolean hasHook(String context, String functionName) { - if (context == null || functionName == null) + public boolean hasHook(String context, String hookName) { + if (context == null || hookName == null) return false; Map contextDefs = hookDefinitions.get(context); - return contextDefs != null && contextDefs.containsKey(functionName); + return contextDefs != null && contextDefs.containsKey(hookName); + } + + @Override + public int getHookRevision() { + return hookRevision; } @Override