Hook Context

This commit is contained in:
Kam committed 2026-01-22 17:59:03 -05:00
1 parent f4a5bbd1f6
commit bee4d18bc6
6 files changed
+903 -198

No files matched your search

@@ -0,0 +1,307 @@
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.
*
* <h3>Usage Examples</h3>
* <pre>{@code
* // Full definition with class reference
* HookDefinition.builder("onDBCTransform")
* .eventClass(IDBCEvent.TransformEvent.class)
* .paramNames("event")
* .requiredImports("com.dbc.api.event.IDBCEvent")
* .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");
* }</pre>
*/
public class HookDefinition implements IHookDefinition {
private final String hookName;
private final String eventClassName;
private final String[] paramNames;
private final String[] requiredImports;
private final boolean cancelable;
// Cached resolved class (transient - not serialized)
private transient Class<?> cachedEventClass;
private transient boolean classResolutionAttempted;
private HookDefinition(Builder builder) {
this.hookName = builder.hookName;
this.eventClassName = builder.eventClassName;
this.paramNames = builder.paramNames != null ? builder.paramNames : new String[]{"event"};
this.requiredImports = builder.requiredImports != null ? builder.requiredImports : new String[0];
this.cancelable = builder.cancelable;
}
// ==================== IHookDefinition Implementation ====================
@Override
public String hookName() {
return hookName;
}
@Override
public String eventClassName() {
return eventClassName;
}
@Override
public String[] paramNames() {
return paramNames;
}
@Override
public String[] requiredImports() {
return requiredImports;
}
@Override
public boolean isCancelable() {
return cancelable;
}
@Override
public Class<?> getEventClass() {
if (classResolutionAttempted) {
return cachedEventClass;
}
classResolutionAttempted = true;
if (eventClassName == null || eventClassName.isEmpty()) {
return null;
}
try {
cachedEventClass = Class.forName(eventClassName);
} catch (ClassNotFoundException e) {
cachedEventClass = null;
}
return cachedEventClass;
}
// ==================== Factory Methods ====================
/**
* Create a new builder for a hook definition.
*
* @param hookName The hook function name
* @return A new builder instance
*/
public static Builder builder(String hookName) {
return new Builder(hookName);
}
/**
* Create a simple hook definition with just a name (no metadata).
* For backward compatibility with legacy hook registration.
*
* @param hookName The hook function name
* @return A minimal hook definition
*/
public static HookDefinition simple(String hookName) {
return builder(hookName).build();
}
/**
* Create a hook definition from an interface method.
* Automatically extracts metadata from the method signature and annotations.
*
* @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
*/
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();
}
/**
* Get the import statement needed for a class.
* For nested classes, returns the enclosing class.
*/
private static String getImportForClass(Class<?> clazz) {
if (clazz == null || clazz.isPrimitive()) {
return null;
}
// For nested classes, get the top-level enclosing class
Class<?> enclosing = clazz;
while (enclosing.getEnclosingClass() != null) {
enclosing = enclosing.getEnclosingClass();
}
String name = enclosing.getName();
// Skip java.lang classes
if (name.startsWith("java.lang.")) {
return null;
}
return name;
}
// ==================== Builder ====================
/**
* Builder for creating HookDefinition instances with a fluent API.
*/
public static class Builder {
private final String hookName;
private String eventClassName;
private String[] paramNames;
private String[] requiredImports;
private boolean cancelable;
private Builder(String hookName) {
if (hookName == null || hookName.isEmpty()) {
throw new IllegalArgumentException("hookName cannot be null or empty");
}
this.hookName = hookName;
}
/**
* 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
*/
public Builder eventClass(Class<?> clazz) {
if (clazz != null) {
this.eventClassName = clazz.getName();
// Auto-detect cancelable
if (clazz.isAnnotationPresent(Cancelable.class)) {
this.cancelable = true;
}
}
return this;
}
/**
* Set parameter names for stub generation.
*
* @param names Parameter names
* @return this builder
*/
public Builder paramNames(String... names) {
this.paramNames = names;
return this;
}
/**
* Set required imports for this hook's event type.
*
* @param imports Fully qualified class names to import
* @return this builder
*/
public Builder requiredImports(String... imports) {
this.requiredImports = imports;
return this;
}
/**
* 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);
}
}
// ==================== Object Methods ====================
@Override
public String toString() {
return "HookDefinition{" +
"hookName='" + hookName + '\'' +
", eventClassName='" + eventClassName + '\'' +
", cancelable=" + cancelable +
'}';
}
@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);
}
@Override
public int hashCode() {
return hookName.hashCode();
}
}
@@ -1,5 +1,6 @@
package noppes.npcs.controllers;
import noppes.npcs.api.handler.IHookDefinition;
import noppes.npcs.api.handler.IScriptHookHandler;
import noppes.npcs.constants.EnumScriptType;
import noppes.npcs.constants.ScriptContext;
@@ -14,17 +15,33 @@ import java.util.Map;
/**
* Controller for managing script hooks.
* Allows addon mods to register custom hooks that appear in script editor GUIs.
*
* <h3>Rich Hook Registration</h3>
* Supports full hook definitions with metadata for proper stub generation:
* <pre>{@code
* ScriptHookController.Instance.registerHookDefinition("player",
* HookDefinition.builder("onDBCTransform")
* .eventClass(IDBCEvent.TransformEvent.class)
* .requiredImports("com.dbc.api.event.IDBCEvent")
* .build());
* }</pre>
*/
public class ScriptHookController implements IScriptHookHandler {
public static ScriptHookController Instance;
// Built-in hooks per context
// Built-in hooks per context (legacy string list)
private final Map<String, List<String>> builtInHooks = new HashMap<>();
// Addon-registered hooks per context
// Addon-registered hooks per context (legacy string list)
private final Map<String, List<String>> addonHooks = new HashMap<>();
// Rich hook definitions per context (NEW)
private final Map<String, Map<String, HookDefinition>> hookDefinitions = new HashMap<>();
// Revision counter for cache invalidation (NEW)
private int hookRevision = 0;
public ScriptHookController() {
Instance = this;
initializeBuiltInHooks();
@@ -36,160 +53,24 @@ public class ScriptHookController implements IScriptHookHandler {
if (!context.hookContext.isEmpty()) {
builtInHooks.put(context.hookContext, new ArrayList<>());
addonHooks.put(context.hookContext, new ArrayList<>());
hookDefinitions.put(context.hookContext, new HashMap<>());
}
}
// NPC hooks
registerBuiltIn(ScriptContext.NPC,
EnumScriptType.INIT.function,
EnumScriptType.TICK.function,
EnumScriptType.INTERACT.function,
EnumScriptType.DIALOG.function,
EnumScriptType.DAMAGED.function,
EnumScriptType.KILLED.function,
EnumScriptType.ATTACK_MELEE.function,
EnumScriptType.ATTACK_SWING.function,
EnumScriptType.RANGED_LAUNCHED.function,
EnumScriptType.TARGET.function,
EnumScriptType.COLLIDE.function,
EnumScriptType.KILLS.function,
EnumScriptType.DIALOG_CLOSE.function,
EnumScriptType.TIMER.function,
EnumScriptType.TARGET_LOST.function,
EnumScriptType.PROJECTILE_TICK.function,
EnumScriptType.PROJECTILE_IMPACT.function,
EnumScriptType.ABILITY_TICK.function,
EnumScriptType.ABILITY_EXECUTE.function,
EnumScriptType.ABILITY_HIT.function,
EnumScriptType.ABILITY_START.function,
EnumScriptType.ABILITY_INTERRUPT.function,
EnumScriptType.ABILITY_COMPLETE.function
);
// NPC hooks with full definitions
initializeNpcHooks();
// Player hooks
registerBuiltIn(ScriptContext.PLAYER,
EnumScriptType.INIT.function,
EnumScriptType.TICK.function,
EnumScriptType.INTERACT.function,
EnumScriptType.RIGHT_CLICK.function,
EnumScriptType.ATTACK.function,
EnumScriptType.ATTACKED.function,
EnumScriptType.DAMAGED_ENTITY.function,
EnumScriptType.DAMAGED.function,
EnumScriptType.KILLS.function,
EnumScriptType.KILLED.function,
EnumScriptType.DROP.function,
EnumScriptType.RESPAWN.function,
EnumScriptType.BREAK_BLOCK.function,
EnumScriptType.CHAT.function,
EnumScriptType.LOGIN.function,
EnumScriptType.LOGOUT.function,
EnumScriptType.KEY_PRESSED.function,
EnumScriptType.MOUSE_CLICKED.function,
EnumScriptType.TOSS.function,
EnumScriptType.PICKUP.function,
EnumScriptType.PICKUP_XP.function,
EnumScriptType.RANGED_CHARGE.function,
EnumScriptType.RANGED_LAUNCHED.function,
EnumScriptType.TIMER.function,
EnumScriptType.START_USING_ITEM.function,
EnumScriptType.USING_ITEM.function,
EnumScriptType.STOP_USING_ITEM.function,
EnumScriptType.FINISH_USING_ITEM.function,
EnumScriptType.CONTAINER_OPEN.function,
EnumScriptType.USE_HOE.function,
EnumScriptType.BONEMEAL.function,
EnumScriptType.FILL_BUCKET.function,
EnumScriptType.JUMP.function,
EnumScriptType.FALL.function,
EnumScriptType.WAKE_UP.function,
EnumScriptType.SLEEP.function,
EnumScriptType.PLAYSOUND.function,
EnumScriptType.LIGHTNING.function,
EnumScriptType.CHANGED_DIM.function,
EnumScriptType.QUEST_START.function,
EnumScriptType.QUEST_COMPLETED.function,
EnumScriptType.QUEST_TURNIN.function,
EnumScriptType.FACTION_POINTS.function,
EnumScriptType.DIALOG_OPEN.function,
EnumScriptType.DIALOG_OPTION.function,
EnumScriptType.DIALOG_CLOSE.function,
EnumScriptType.SCRIPT_COMMAND.function,
EnumScriptType.CUSTOM_GUI_CLOSED.function,
EnumScriptType.CUSTOM_GUI_BUTTON.function,
EnumScriptType.CUSTOM_GUI_SLOT.function,
EnumScriptType.CUSTOM_GUI_SLOT_CLICKED.function,
EnumScriptType.CUSTOM_GUI_SCROLL.function,
EnumScriptType.CUSTOM_GUI_TEXTFIELD.function,
EnumScriptType.PARTY_QUEST_COMPLETED.function,
EnumScriptType.PARTY_QUEST_SET.function,
EnumScriptType.PARTY_QUEST_TURNED_IN.function,
EnumScriptType.PARTY_INVITE.function,
EnumScriptType.PARTY_KICK.function,
EnumScriptType.PARTY_LEAVE.function,
EnumScriptType.PARTY_DISBAND.function,
EnumScriptType.ANIMATION_START.function,
EnumScriptType.ANIMATION_END.function,
EnumScriptType.ANIMATION_FRAME_ENTER.function,
EnumScriptType.ANIMATION_FRAME_EXIT.function,
EnumScriptType.PROFILE_CHANGE.function,
EnumScriptType.PROFILE_REMOVE.function,
EnumScriptType.PROFILE_CREATE.function,
EnumScriptType.ON_EFFECT_ADD.function,
EnumScriptType.ON_EFFECT_TICK.function,
EnumScriptType.ON_EFFECT_REMOVE.function
);
// Player hooks with full definitions
initializePlayerHooks();
// Block hooks
registerBuiltIn(ScriptContext.BLOCK,
EnumScriptType.INIT.function,
EnumScriptType.TICK.function,
EnumScriptType.INTERACT.function,
EnumScriptType.FALLEN_UPON.function,
EnumScriptType.REDSTONE.function,
EnumScriptType.BROKEN.function,
EnumScriptType.EXPLODED.function,
EnumScriptType.RAIN_FILLED.function,
EnumScriptType.NEIGHBOR_CHANGED.function,
EnumScriptType.CLICKED.function,
EnumScriptType.HARVESTED.function,
EnumScriptType.COLLIDE.function,
EnumScriptType.TIMER.function
);
// Block hooks with full definitions
initializeBlockHooks();
// Item hooks
registerBuiltIn(ScriptContext.ITEM,
EnumScriptType.INIT.function,
EnumScriptType.TICK.function,
EnumScriptType.TOSSED.function,
EnumScriptType.PICKEDUP.function,
EnumScriptType.SPAWN.function,
EnumScriptType.INTERACT.function,
EnumScriptType.RIGHT_CLICK.function,
EnumScriptType.ATTACK.function,
EnumScriptType.START_USING_ITEM.function,
EnumScriptType.USING_ITEM.function,
EnumScriptType.STOP_USING_ITEM.function,
EnumScriptType.FINISH_USING_ITEM.function
);
// Item hooks with full definitions
initializeItemHooks();
// Linked item hooks
registerBuiltIn(ScriptContext.LINKED_ITEM,
EnumScriptType.LINKED_ITEM_BUILD.function,
EnumScriptType.LINKED_ITEM_VERSION.function,
EnumScriptType.INIT.function,
EnumScriptType.TICK.function,
EnumScriptType.TOSSED.function,
EnumScriptType.PICKEDUP.function,
EnumScriptType.SPAWN.function,
EnumScriptType.INTERACT.function,
EnumScriptType.RIGHT_CLICK.function,
EnumScriptType.ATTACK.function,
EnumScriptType.START_USING_ITEM.function,
EnumScriptType.USING_ITEM.function,
EnumScriptType.STOP_USING_ITEM.function,
EnumScriptType.FINISH_USING_ITEM.function
);
// Linked item hooks with full definitions
initializeLinkedItemHooks();
// Recipe hooks
registerBuiltIn(ScriptContext.RECIPE,
@@ -211,6 +92,372 @@ public class ScriptHookController implements IScriptHookHandler {
);
}
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
registerBuiltInDefinition(ctx, EnumScriptType.INIT.function,
"noppes.npcs.api.event.INpcEvent$InitEvent", npcImport);
registerBuiltInDefinition(ctx, EnumScriptType.TICK.function,
"noppes.npcs.api.event.INpcEvent$UpdateEvent", npcImport);
registerBuiltInDefinition(ctx, EnumScriptType.TIMER.function,
"noppes.npcs.api.event.INpcEvent$TimerEvent", npcImport);
// Interaction
registerBuiltInDefinition(ctx, EnumScriptType.INTERACT.function,
"noppes.npcs.api.event.INpcEvent$InteractEvent", npcImport);
registerBuiltInDefinition(ctx, EnumScriptType.DIALOG.function,
"noppes.npcs.api.event.INpcEvent$DialogEvent", npcImport);
registerBuiltInDefinition(ctx, EnumScriptType.DIALOG_CLOSE.function,
"noppes.npcs.api.event.INpcEvent$DialogClosedEvent", npcImport);
registerBuiltInDefinition(ctx, EnumScriptType.COLLIDE.function,
"noppes.npcs.api.event.INpcEvent$CollideEvent", npcImport);
// Combat
registerBuiltInDefinition(ctx, EnumScriptType.DAMAGED.function,
"noppes.npcs.api.event.INpcEvent$DamagedEvent", npcImport);
registerBuiltInDefinition(ctx, EnumScriptType.KILLED.function,
"noppes.npcs.api.event.INpcEvent$DiedEvent", npcImport);
registerBuiltInDefinition(ctx, EnumScriptType.KILLS.function,
"noppes.npcs.api.event.INpcEvent$KilledEntityEvent", npcImport);
registerBuiltInDefinition(ctx, EnumScriptType.ATTACK_MELEE.function,
"noppes.npcs.api.event.INpcEvent$MeleeAttackEvent", npcImport);
registerBuiltInDefinition(ctx, EnumScriptType.ATTACK_SWING.function,
"noppes.npcs.api.event.INpcEvent$SwingEvent", npcImport);
registerBuiltInDefinition(ctx, EnumScriptType.RANGED_LAUNCHED.function,
"noppes.npcs.api.event.INpcEvent$RangedLaunchedEvent", npcImport);
registerBuiltInDefinition(ctx, EnumScriptType.TARGET.function,
"noppes.npcs.api.event.INpcEvent$TargetEvent", npcImport);
registerBuiltInDefinition(ctx, EnumScriptType.TARGET_LOST.function,
"noppes.npcs.api.event.INpcEvent$TargetLostEvent", npcImport);
// Projectile events
registerBuiltInDefinition(ctx, EnumScriptType.PROJECTILE_TICK.function,
"noppes.npcs.api.event.IProjectileEvent$UpdateEvent", projImport);
registerBuiltInDefinition(ctx, EnumScriptType.PROJECTILE_IMPACT.function,
"noppes.npcs.api.event.IProjectileEvent$ImpactEvent", projImport);
// Ability events
registerBuiltInDefinition(ctx, EnumScriptType.ABILITY_START.function,
"noppes.npcs.api.event.IAbilityEvent$StartEvent", abilityImport);
registerBuiltInDefinition(ctx, EnumScriptType.ABILITY_EXECUTE.function,
"noppes.npcs.api.event.IAbilityEvent$ExecuteEvent", abilityImport);
registerBuiltInDefinition(ctx, EnumScriptType.ABILITY_HIT.function,
"noppes.npcs.api.event.IAbilityEvent$HitEvent", abilityImport);
registerBuiltInDefinition(ctx, EnumScriptType.ABILITY_TICK.function,
"noppes.npcs.api.event.IAbilityEvent$TickEvent", abilityImport);
registerBuiltInDefinition(ctx, EnumScriptType.ABILITY_INTERRUPT.function,
"noppes.npcs.api.event.IAbilityEvent$InterruptEvent", abilityImport);
registerBuiltInDefinition(ctx, EnumScriptType.ABILITY_COMPLETE.function,
"noppes.npcs.api.event.IAbilityEvent$CompleteEvent", abilityImport);
}
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
registerBuiltInDefinition(ctx, EnumScriptType.INIT.function,
"noppes.npcs.api.event.IPlayerEvent$InitEvent", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.TICK.function,
"noppes.npcs.api.event.IPlayerEvent$UpdateEvent", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.TIMER.function,
"noppes.npcs.api.event.IPlayerEvent$TimerEvent", playerImport);
// Combat
registerBuiltInDefinition(ctx, EnumScriptType.ATTACK.function,
"noppes.npcs.api.event.IPlayerEvent$AttackEvent", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.ATTACKED.function,
"noppes.npcs.api.event.IPlayerEvent$AttackedEvent", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.DAMAGED.function,
"noppes.npcs.api.event.IPlayerEvent$DamagedEvent", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.DAMAGED_ENTITY.function,
"noppes.npcs.api.event.IPlayerEvent$DamagedEntityEvent", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.KILLS.function,
"noppes.npcs.api.event.IPlayerEvent$KilledEntityEvent", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.KILLED.function,
"noppes.npcs.api.event.IPlayerEvent$DiedEvent", playerImport);
// Interaction
registerBuiltInDefinition(ctx, EnumScriptType.INTERACT.function,
"noppes.npcs.api.event.IPlayerEvent$InteractEvent", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.RIGHT_CLICK.function,
"noppes.npcs.api.event.IPlayerEvent$RightClickEvent", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.BREAK_BLOCK.function,
"noppes.npcs.api.event.IPlayerEvent$BreakEvent", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.CHAT.function,
"noppes.npcs.api.event.IPlayerEvent$ChatEvent", playerImport);
// Connection
registerBuiltInDefinition(ctx, EnumScriptType.LOGIN.function,
"noppes.npcs.api.event.IPlayerEvent$LoginEvent", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.LOGOUT.function,
"noppes.npcs.api.event.IPlayerEvent$LogoutEvent", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.RESPAWN.function,
"noppes.npcs.api.event.IPlayerEvent$RespawnEvent", playerImport);
// Input
registerBuiltInDefinition(ctx, EnumScriptType.KEY_PRESSED.function,
"noppes.npcs.api.event.IPlayerEvent$KeyPressedEvent", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.MOUSE_CLICKED.function,
"noppes.npcs.api.event.IPlayerEvent$MouseClickedEvent", playerImport);
// Items
registerBuiltInDefinition(ctx, EnumScriptType.PICKUP.function,
"noppes.npcs.api.event.IPlayerEvent$PickUpEvent", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.TOSS.function,
"noppes.npcs.api.event.IPlayerEvent$TossEvent", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.DROP.function,
"noppes.npcs.api.event.IPlayerEvent$DropEvent", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.PICKUP_XP.function,
"noppes.npcs.api.event.IPlayerEvent$PickupXPEvent", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.START_USING_ITEM.function,
"noppes.npcs.api.event.IPlayerEvent$StartUsingItem", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.USING_ITEM.function,
"noppes.npcs.api.event.IPlayerEvent$UsingItem", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.STOP_USING_ITEM.function,
"noppes.npcs.api.event.IPlayerEvent$StopUsingItem", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.FINISH_USING_ITEM.function,
"noppes.npcs.api.event.IPlayerEvent$FinishUsingItem", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.CONTAINER_OPEN.function,
"noppes.npcs.api.event.IPlayerEvent$ContainerOpen", playerImport);
// Movement
registerBuiltInDefinition(ctx, EnumScriptType.JUMP.function,
"noppes.npcs.api.event.IPlayerEvent$JumpEvent", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.FALL.function,
"noppes.npcs.api.event.IPlayerEvent$FallEvent", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.CHANGED_DIM.function,
"noppes.npcs.api.event.IPlayerEvent$ChangedDimension", playerImport);
// World
registerBuiltInDefinition(ctx, EnumScriptType.RANGED_CHARGE.function,
"noppes.npcs.api.event.IPlayerEvent$RangedChargeEvent", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.RANGED_LAUNCHED.function,
"noppes.npcs.api.event.IPlayerEvent$RangedLaunchedEvent", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.USE_HOE.function,
"noppes.npcs.api.event.IPlayerEvent$UseHoeEvent", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.BONEMEAL.function,
"noppes.npcs.api.event.IPlayerEvent$BonemealEvent", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.FILL_BUCKET.function,
"noppes.npcs.api.event.IPlayerEvent$FillBucketEvent", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.WAKE_UP.function,
"noppes.npcs.api.event.IPlayerEvent$WakeUpEvent", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.SLEEP.function,
"noppes.npcs.api.event.IPlayerEvent$SleepEvent", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.PLAYSOUND.function,
"noppes.npcs.api.event.IPlayerEvent$SoundEvent", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.LIGHTNING.function,
"noppes.npcs.api.event.IPlayerEvent$LightningEvent", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.SCRIPT_COMMAND.function,
"noppes.npcs.api.event.ICustomNPCsEvent$ScriptedCommandEvent", "noppes.npcs.api.event.ICustomNPCsEvent");
// Quest events
registerBuiltInDefinition(ctx, EnumScriptType.QUEST_START.function,
"noppes.npcs.api.event.IQuestEvent$QuestStartEvent", questImport);
registerBuiltInDefinition(ctx, EnumScriptType.QUEST_COMPLETED.function,
"noppes.npcs.api.event.IQuestEvent$QuestCompletedEvent", questImport);
registerBuiltInDefinition(ctx, EnumScriptType.QUEST_TURNIN.function,
"noppes.npcs.api.event.IQuestEvent$QuestTurnedInEvent", questImport);
// Dialog events
registerBuiltInDefinition(ctx, EnumScriptType.DIALOG_OPEN.function,
"noppes.npcs.api.event.IDialogEvent$DialogOpen", dialogImport);
registerBuiltInDefinition(ctx, EnumScriptType.DIALOG_OPTION.function,
"noppes.npcs.api.event.IDialogEvent$DialogOption", dialogImport);
registerBuiltInDefinition(ctx, EnumScriptType.DIALOG_CLOSE.function,
"noppes.npcs.api.event.IDialogEvent$DialogClosed", dialogImport);
// Faction events
registerBuiltInDefinition(ctx, EnumScriptType.FACTION_POINTS.function,
"noppes.npcs.api.event.IFactionEvent$FactionPoints", factionImport);
// Custom GUI events
registerBuiltInDefinition(ctx, EnumScriptType.CUSTOM_GUI_CLOSED.function,
"noppes.npcs.api.event.ICustomGuiEvent$CloseEvent", guiImport);
registerBuiltInDefinition(ctx, EnumScriptType.CUSTOM_GUI_BUTTON.function,
"noppes.npcs.api.event.ICustomGuiEvent$ButtonEvent", guiImport);
registerBuiltInDefinition(ctx, EnumScriptType.CUSTOM_GUI_SLOT.function,
"noppes.npcs.api.event.ICustomGuiEvent$SlotEvent", guiImport);
registerBuiltInDefinition(ctx, EnumScriptType.CUSTOM_GUI_SLOT_CLICKED.function,
"noppes.npcs.api.event.ICustomGuiEvent$SlotClickEvent", guiImport);
registerBuiltInDefinition(ctx, EnumScriptType.CUSTOM_GUI_SCROLL.function,
"noppes.npcs.api.event.ICustomGuiEvent$ScrollEvent", guiImport);
registerBuiltInDefinition(ctx, EnumScriptType.CUSTOM_GUI_TEXTFIELD.function,
"noppes.npcs.api.event.ICustomGuiEvent$UnfocusedEvent", guiImport);
// Party events
registerBuiltInDefinition(ctx, EnumScriptType.PARTY_QUEST_COMPLETED.function,
"noppes.npcs.api.event.IPartyEvent$PartyQuestCompletedEvent", partyImport);
registerBuiltInDefinition(ctx, EnumScriptType.PARTY_QUEST_SET.function,
"noppes.npcs.api.event.IPartyEvent$PartyQuestSetEvent", partyImport);
registerBuiltInDefinition(ctx, EnumScriptType.PARTY_QUEST_TURNED_IN.function,
"noppes.npcs.api.event.IPartyEvent$PartyQuestTurnedInEvent", partyImport);
registerBuiltInDefinition(ctx, EnumScriptType.PARTY_INVITE.function,
"noppes.npcs.api.event.IPartyEvent$PartyInviteEvent", partyImport);
registerBuiltInDefinition(ctx, EnumScriptType.PARTY_KICK.function,
"noppes.npcs.api.event.IPartyEvent$PartyKickEvent", partyImport);
registerBuiltInDefinition(ctx, EnumScriptType.PARTY_LEAVE.function,
"noppes.npcs.api.event.IPartyEvent$PartyLeaveEvent", partyImport);
registerBuiltInDefinition(ctx, EnumScriptType.PARTY_DISBAND.function,
"noppes.npcs.api.event.IPartyEvent$PartyDisbandEvent", partyImport);
// Animation events
registerBuiltInDefinition(ctx, EnumScriptType.ANIMATION_START.function,
"noppes.npcs.api.event.IAnimationEvent$Started", animImport);
registerBuiltInDefinition(ctx, EnumScriptType.ANIMATION_END.function,
"noppes.npcs.api.event.IAnimationEvent$Ended", animImport);
registerBuiltInDefinition(ctx, EnumScriptType.ANIMATION_FRAME_ENTER.function,
"noppes.npcs.api.event.IAnimationEvent$IFrameEvent$Entered", animImport);
registerBuiltInDefinition(ctx, EnumScriptType.ANIMATION_FRAME_EXIT.function,
"noppes.npcs.api.event.IAnimationEvent$IFrameEvent$Exited", animImport);
// Profile events
registerBuiltInDefinition(ctx, EnumScriptType.PROFILE_CHANGE.function,
"noppes.npcs.api.event.IPlayerEvent$ProfileEvent$Changed", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.PROFILE_REMOVE.function,
"noppes.npcs.api.event.IPlayerEvent$ProfileEvent$Removed", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.PROFILE_CREATE.function,
"noppes.npcs.api.event.IPlayerEvent$ProfileEvent$Create", playerImport);
// Effect events (for player context)
registerBuiltInDefinition(ctx, EnumScriptType.ON_EFFECT_ADD.function,
"noppes.npcs.api.event.IPlayerEvent$EffectEvent$Added", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.ON_EFFECT_TICK.function,
"noppes.npcs.api.event.IPlayerEvent$EffectEvent$Ticked", playerImport);
registerBuiltInDefinition(ctx, EnumScriptType.ON_EFFECT_REMOVE.function,
"noppes.npcs.api.event.IPlayerEvent$EffectEvent$Removed", playerImport);
}
private void initializeBlockHooks() {
String ctx = ScriptContext.BLOCK.hookContext;
String blockImport = "noppes.npcs.api.event.IBlockEvent";
registerBuiltInDefinition(ctx, EnumScriptType.INIT.function,
"noppes.npcs.api.event.IBlockEvent$InitEvent", blockImport);
registerBuiltInDefinition(ctx, EnumScriptType.TICK.function,
"noppes.npcs.api.event.IBlockEvent$UpdateEvent", blockImport);
registerBuiltInDefinition(ctx, EnumScriptType.INTERACT.function,
"noppes.npcs.api.event.IBlockEvent$InteractEvent", blockImport);
registerBuiltInDefinition(ctx, EnumScriptType.FALLEN_UPON.function,
"noppes.npcs.api.event.IBlockEvent$EntityFallenUponEvent", blockImport);
registerBuiltInDefinition(ctx, EnumScriptType.REDSTONE.function,
"noppes.npcs.api.event.IBlockEvent$RedstoneEvent", blockImport);
registerBuiltInDefinition(ctx, EnumScriptType.BROKEN.function,
"noppes.npcs.api.event.IBlockEvent$BreakEvent", blockImport);
registerBuiltInDefinition(ctx, EnumScriptType.EXPLODED.function,
"noppes.npcs.api.event.IBlockEvent$ExplodedEvent", blockImport);
registerBuiltInDefinition(ctx, EnumScriptType.RAIN_FILLED.function,
"noppes.npcs.api.event.IBlockEvent$RainFillEvent", blockImport);
registerBuiltInDefinition(ctx, EnumScriptType.NEIGHBOR_CHANGED.function,
"noppes.npcs.api.event.IBlockEvent$NeighborChangedEvent", blockImport);
registerBuiltInDefinition(ctx, EnumScriptType.CLICKED.function,
"noppes.npcs.api.event.IBlockEvent$ClickedEvent", blockImport);
registerBuiltInDefinition(ctx, EnumScriptType.HARVESTED.function,
"noppes.npcs.api.event.IBlockEvent$HarvestedEvent", blockImport);
registerBuiltInDefinition(ctx, EnumScriptType.COLLIDE.function,
"noppes.npcs.api.event.IBlockEvent$CollidedEvent", blockImport);
registerBuiltInDefinition(ctx, EnumScriptType.TIMER.function,
"noppes.npcs.api.event.IBlockEvent$TimerEvent", blockImport);
}
private void initializeItemHooks() {
String ctx = ScriptContext.ITEM.hookContext;
String itemImport = "noppes.npcs.api.event.IItemEvent";
registerBuiltInDefinition(ctx, EnumScriptType.INIT.function,
"noppes.npcs.api.event.IItemEvent$InitEvent", itemImport);
registerBuiltInDefinition(ctx, EnumScriptType.TICK.function,
"noppes.npcs.api.event.IItemEvent$UpdateEvent", itemImport);
registerBuiltInDefinition(ctx, EnumScriptType.TOSSED.function,
"noppes.npcs.api.event.IItemEvent$TossedEvent", itemImport);
registerBuiltInDefinition(ctx, EnumScriptType.PICKEDUP.function,
"noppes.npcs.api.event.IItemEvent$PickedUpEvent", itemImport);
registerBuiltInDefinition(ctx, EnumScriptType.SPAWN.function,
"noppes.npcs.api.event.IItemEvent$SpawnEvent", itemImport);
registerBuiltInDefinition(ctx, EnumScriptType.INTERACT.function,
"noppes.npcs.api.event.IItemEvent$InteractEvent", itemImport);
registerBuiltInDefinition(ctx, EnumScriptType.RIGHT_CLICK.function,
"noppes.npcs.api.event.IItemEvent$RightClickEvent", itemImport);
registerBuiltInDefinition(ctx, EnumScriptType.ATTACK.function,
"noppes.npcs.api.event.IItemEvent$AttackEvent", itemImport);
registerBuiltInDefinition(ctx, EnumScriptType.START_USING_ITEM.function,
"noppes.npcs.api.event.IItemEvent$StartUsingItem", itemImport);
registerBuiltInDefinition(ctx, EnumScriptType.USING_ITEM.function,
"noppes.npcs.api.event.IItemEvent$UsingItem", itemImport);
registerBuiltInDefinition(ctx, EnumScriptType.STOP_USING_ITEM.function,
"noppes.npcs.api.event.IItemEvent$StopUsingItem", itemImport);
registerBuiltInDefinition(ctx, EnumScriptType.FINISH_USING_ITEM.function,
"noppes.npcs.api.event.IItemEvent$FinishUsingItem", itemImport);
}
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
registerBuiltInDefinition(ctx, EnumScriptType.LINKED_ITEM_BUILD.function,
"noppes.npcs.api.event.ILinkedItemEvent$BuildEvent", linkedImport);
registerBuiltInDefinition(ctx, EnumScriptType.LINKED_ITEM_VERSION.function,
"noppes.npcs.api.event.ILinkedItemEvent$VersionChangeEvent", linkedImport);
// Standard item hooks
registerBuiltInDefinition(ctx, EnumScriptType.INIT.function,
"noppes.npcs.api.event.IItemEvent$InitEvent", itemImport);
registerBuiltInDefinition(ctx, EnumScriptType.TICK.function,
"noppes.npcs.api.event.IItemEvent$UpdateEvent", itemImport);
registerBuiltInDefinition(ctx, EnumScriptType.TOSSED.function,
"noppes.npcs.api.event.IItemEvent$TossedEvent", itemImport);
registerBuiltInDefinition(ctx, EnumScriptType.PICKEDUP.function,
"noppes.npcs.api.event.IItemEvent$PickedUpEvent", itemImport);
registerBuiltInDefinition(ctx, EnumScriptType.SPAWN.function,
"noppes.npcs.api.event.IItemEvent$SpawnEvent", itemImport);
registerBuiltInDefinition(ctx, EnumScriptType.INTERACT.function,
"noppes.npcs.api.event.IItemEvent$InteractEvent", itemImport);
registerBuiltInDefinition(ctx, EnumScriptType.RIGHT_CLICK.function,
"noppes.npcs.api.event.IItemEvent$RightClickEvent", itemImport);
registerBuiltInDefinition(ctx, EnumScriptType.ATTACK.function,
"noppes.npcs.api.event.IItemEvent$AttackEvent", itemImport);
registerBuiltInDefinition(ctx, EnumScriptType.START_USING_ITEM.function,
"noppes.npcs.api.event.IItemEvent$StartUsingItem", itemImport);
registerBuiltInDefinition(ctx, EnumScriptType.USING_ITEM.function,
"noppes.npcs.api.event.IItemEvent$UsingItem", itemImport);
registerBuiltInDefinition(ctx, EnumScriptType.STOP_USING_ITEM.function,
"noppes.npcs.api.event.IItemEvent$StopUsingItem", itemImport);
registerBuiltInDefinition(ctx, EnumScriptType.FINISH_USING_ITEM.function,
"noppes.npcs.api.event.IItemEvent$FinishUsingItem", itemImport);
}
/**
* Register a built-in hook with full definition metadata.
*/
private void registerBuiltInDefinition(String context, String hookName, String eventClassName, String... imports) {
HookDefinition def = HookDefinition.builder(hookName)
.eventClass(eventClassName)
.requiredImports(imports)
.build();
Map<String, HookDefinition> contextDefs = hookDefinitions.get(context);
if (contextDefs == null) {
contextDefs = new HashMap<>();
hookDefinitions.put(context, contextDefs);
}
contextDefs.put(hookName, def);
// Also register in legacy string list
registerBuiltIn(context, hookName);
}
/**
* Register built-in hooks for a ScriptContext.
*/
@@ -229,6 +476,72 @@ public class ScriptHookController implements IScriptHookHandler {
}
}
// ==================== RICH HOOK REGISTRATION (NEW API) ====================
@Override
public void registerHookDefinition(String context, IHookDefinition definition) {
if (context == null || definition == null || definition.hookName() == null) {
return;
}
Map<String, HookDefinition> contextDefs = hookDefinitions.get(context);
if (contextDefs == null) {
contextDefs = new HashMap<>();
hookDefinitions.put(context, contextDefs);
}
// Store as HookDefinition (implementation)
if (definition instanceof HookDefinition) {
contextDefs.put(definition.hookName(), (HookDefinition) definition);
} else {
// Convert foreign IHookDefinition to HookDefinition
HookDefinition def = HookDefinition.builder(definition.hookName())
.eventClass(definition.eventClassName())
.paramNames(definition.paramNames())
.requiredImports(definition.requiredImports())
.cancelable(definition.isCancelable())
.build();
contextDefs.put(definition.hookName(), def);
}
// Also add to legacy addon hooks list for backward compatibility
registerHook(context, definition.hookName());
// Increment revision for cache invalidation
hookRevision++;
}
@Override
public IHookDefinition getHookDefinition(String context, String hookName) {
if (context == null || hookName == null) {
return null;
}
Map<String, HookDefinition> contextDefs = hookDefinitions.get(context);
return contextDefs != null ? contextDefs.get(hookName) : null;
}
@Override
public List<IHookDefinition> getAllHookDefinitions(String context) {
if (context == null) {
return Collections.emptyList();
}
Map<String, HookDefinition> contextDefs = hookDefinitions.get(context);
if (contextDefs == null || contextDefs.isEmpty()) {
return Collections.emptyList();
}
return new ArrayList<>(contextDefs.values());
}
@Override
public int getHookRevision() {
return hookRevision;
}
// ==================== CONVENIENCE METHODS ====================
/**
* Convenience method to register a hook using ScriptContext.
* @param context The script context
@@ -248,7 +561,18 @@ public class ScriptHookController implements IScriptHookHandler {
registerHook(context.hookContext, fn);
}
}
/**
* Convenience method to register a hook definition using ScriptContext.
* @param context The script context
* @param definition The hook definition
*/
public void registerHookDefinition(ScriptContext context, IHookDefinition definition) {
registerHookDefinition(context.hookContext, definition);
}
// ==================== LEGACY HOOK REGISTRATION ====================
@Override
public void registerHook(String context, String functionName) {
if (context == null || functionName == null || functionName.isEmpty()) {
@@ -6,9 +6,11 @@ import io.github.somehussar.janinoloader.api.script.IScriptClassBody;
import net.minecraft.nbt.NBTTagCompound;
import noppes.npcs.CustomNpcs;
import noppes.npcs.NBTTags;
import noppes.npcs.api.handler.IHookDefinition;
import noppes.npcs.config.ConfigScript;
import noppes.npcs.constants.EnumScriptType;
import noppes.npcs.controllers.ScriptController;
import noppes.npcs.controllers.ScriptHookController;
import noppes.npcs.controllers.data.IScriptUnit;
import noppes.npcs.janino.annotations.ParamName;
import org.codehaus.commons.compiler.InternalCompilerException;
@@ -52,6 +54,10 @@ public abstract class JaninoScript<T> implements IScriptUnit {
private final String[] defaultImports;
// Hook definition cache for fast lookup (NEW)
private Map<String, IHookDefinition> hookDefinitionCache;
private int lastHookRevision = -1;
protected JaninoScript(Class<T> type, String[] defaultImports, boolean isClient) {
this.type = type;
this.defaultImports = defaultImports != null ? defaultImports : new String[0];
@@ -71,6 +77,79 @@ public abstract class JaninoScript<T> implements IScriptUnit {
this(type, defaultImports, false);
}
// ==================== HOOK CONTEXT (Abstract) ====================
/**
* Get the hook context identifier for this script type.
* Used to look up hook definitions from the registry.
*
* @return Context string, e.g., "npc", "player", "block"
*/
protected abstract String getHookContext();
// ==================== HOOK DEFINITION CACHE ====================
/**
* Get hook definition from cache, rebuilding cache if revision changed.
*
* @param hookName The hook name to look up
* @return The hook definition, or null if not found
*/
protected IHookDefinition getHookDefinition(String hookName) {
if (ScriptHookController.Instance == null) {
return null;
}
int currentRevision = ScriptHookController.Instance.getHookRevision();
if (hookDefinitionCache == null || currentRevision != lastHookRevision) {
rebuildHookDefinitionCache();
lastHookRevision = currentRevision;
}
return hookDefinitionCache.get(hookName);
}
/**
* Rebuild the hook definition cache from the registry.
*/
private void rebuildHookDefinitionCache() {
hookDefinitionCache = new HashMap<>();
String context = getHookContext();
if (context == null || context.isEmpty() || ScriptHookController.Instance == null) {
return;
}
for (IHookDefinition def : ScriptHookController.Instance.getAllHookDefinitions(context)) {
hookDefinitionCache.put(def.hookName(), def);
}
}
/**
* Get all required imports from hook definitions plus default imports.
*
* @return Array of all import statements needed
*/
protected String[] collectAllImports() {
Set<String> allImports = new LinkedHashSet<>();
// Add default imports first
Collections.addAll(allImports, defaultImports);
// Add imports from all hook definitions for this context
String context = getHookContext();
if (context != null && !context.isEmpty() && ScriptHookController.Instance != null) {
for (IHookDefinition def : ScriptHookController.Instance.getAllHookDefinitions(context)) {
String[] imports = def.requiredImports();
if (imports != null) {
Collections.addAll(allImports, imports);
}
}
}
return allImports.toArray(new String[0]);
}
protected T getUnsafe() {
return scriptBody.get();
}
@@ -119,11 +198,16 @@ public abstract class JaninoScript<T> implements IScriptUnit {
}
/**
* Feed the code into the engine and compile it
* Feed the code into the engine and compile it.
* Rebuilds imports from hook definitions before compilation.
*/
public void compileScript(String code) {
try {
// Rebuild imports from current hook definitions + defaults
String[] allImports = collectAllImports();
builder.setDefaultImports(allImports);
scriptBody.setScript(code);
} catch (InternalCompilerException e ) {
Throwable parentCause = null;
@@ -330,65 +414,43 @@ public abstract class JaninoScript<T> implements IScriptUnit {
return script != null && !script.isEmpty();
}
@Override
public String generateHookStub(String hookName, Object hookData) {
hookResolver.clearResolutionCaches();
@Override
public String generateHookStub(String hookName, Object hookData) {
hookResolver.clearResolutionCaches();
Method method = hookResolver.getMethodForDisplayName(hookName);
if (method != null)
return generateMethodStub(method);
// 1. Try interface method (existing @ScriptHook annotated methods)
Method method = hookResolver.getMethodForDisplayName(hookName);
if (method != null)
return generateMethodStub(method);
// Fallback: try to find the event class by naming convention
String eventType = findEventTypeForHook(hookName);
if (eventType != null) {
return String.format("public void %s(%s event) {\n \n}\n", hookName, eventType);
// 2. Look up in hook definition registry (addon hooks, dynamic hooks)
IHookDefinition def = getHookDefinition(hookName);
if (def != null) {
return generateStubFromDefinition(def);
}
// Last resort: generic Object parameter
// 3. Last resort: generic Object parameter
return String.format("public void %s(Object event) {\n \n}\n", hookName);
}
/**
* Tries to find the event type for a hook by searching API event interfaces.
* Uses naming convention: hookName "questStart" -> event class "QuestStartEvent"
* Generate a method stub from a hook definition.
*
* @param hookName The hook name (e.g., "questStart", "dialogOpen")
* @return The usable type name (e.g., "IQuestEvent.QuestStartEvent") or null if not found
* @param def The hook definition containing type info
* @return Generated stub code
*/
private String findEventTypeForHook(String hookName) {
if (hookName == null || hookName.isEmpty())
return null;
private String generateStubFromDefinition(IHookDefinition def) {
String typeName = def.getUsableTypeName();
String[] params = def.paramNames();
String paramName = (params != null && params.length > 0) ? params[0] : "event";
// Convert hook name to expected event class name
// questStart -> QuestStartEvent
String expectedEventName = Character.toUpperCase(hookName.charAt(0)) + hookName.substring(1) + "Event";
// Search in known API event interfaces
Class<?>[] eventInterfaces = {
noppes.npcs.api.event.IPlayerEvent.class,
noppes.npcs.api.event.INpcEvent.class,
noppes.npcs.api.event.IQuestEvent.class,
noppes.npcs.api.event.IDialogEvent.class,
noppes.npcs.api.event.IFactionEvent.class,
noppes.npcs.api.event.IPartyEvent.class,
noppes.npcs.api.event.ICustomGuiEvent.class,
noppes.npcs.api.event.IAnimationEvent.class,
noppes.npcs.api.event.IBlockEvent.class,
noppes.npcs.api.event.IItemEvent.class,
noppes.npcs.api.event.IProjectileEvent.class,
noppes.npcs.api.event.IAbilityEvent.class,
noppes.npcs.api.event.IForgeEvent.class
};
for (Class<?> eventInterface : eventInterfaces) {
for (Class<?> nested : eventInterface.getDeclaredClasses()) {
if (nested.getSimpleName().equals(expectedEventName)) {
return getUsableTypeName(nested);
}
}
if (typeName == null || typeName.isEmpty()) {
return String.format("public void %s(Object %s) {\n \n}\n",
def.hookName(), paramName);
}
return null;
return String.format("public void %s(%s %s) {\n \n}\n",
def.hookName(), typeName, paramName);
}
/**
@@ -17,6 +17,7 @@ public class JaninoNpcScript extends JaninoScript<JaninoNpcScript.Functions> {
/**
* Default imports for NPC scripts.
* These are automatically available in user code without explicit import statements.
* Additional imports from registered hook definitions are added at compile time.
*/
private static final String[] DEFAULT_IMPORTS = {
"noppes.npcs.api.event.INpcEvent",
@@ -31,6 +32,11 @@ public class JaninoNpcScript extends JaninoScript<JaninoNpcScript.Functions> {
super(Functions.class, DEFAULT_IMPORTS);
}
@Override
protected String getHookContext() {
return "npc";
}
/**
* Interface defining the hook methods available for NPC scripts.
* Each method corresponds to an NPC event from ScriptHookController CONTEXT_NPC.
@@ -43,6 +43,7 @@ public class JaninoPlayerScript extends JaninoScript<JaninoPlayerScript.Function
/**
* Default imports for player scripts.
* These are automatically available in user code without explicit import statements.
* Additional imports from registered hook definitions are added at compile time.
*/
private static final String[] DEFAULT_IMPORTS = {
"noppes.npcs.api.event.IPlayerEvent",
@@ -61,6 +62,11 @@ public class JaninoPlayerScript extends JaninoScript<JaninoPlayerScript.Function
super(Functions.class, DEFAULT_IMPORTS, false);
}
@Override
protected String getHookContext() {
return "player";
}
/**
* Core hook methods for player scripts.
*