From 32fd6af04fc83c14c95643dc6a95239064c3302d Mon Sep 17 00:00:00 2001 From: bigguy345 Date: Mon, 2 Mar 2026 02:28:01 +0200 Subject: [PATCH] Fix hook signatures to follow runtime hook registrations in ScriptHookController. This fixes broken unresolved DBC addon hook event types --- .../client/gui/script/GuiScriptInterface.java | 7 ++ .../script/interpreter/ScriptDocument.java | 37 ++++++--- .../interpreter/js_parser/JSTypeRegistry.java | 77 +++++++++++++++++++ .../js_parser/TypeScriptDefinitionParser.java | 3 +- .../script/interpreter/type/TypeResolver.java | 1 + 5 files changed, 115 insertions(+), 10 deletions(-) diff --git a/src/main/java/noppes/npcs/client/gui/script/GuiScriptInterface.java b/src/main/java/noppes/npcs/client/gui/script/GuiScriptInterface.java index f461c66f6..688d9f65d 100644 --- a/src/main/java/noppes/npcs/client/gui/script/GuiScriptInterface.java +++ b/src/main/java/noppes/npcs/client/gui/script/GuiScriptInterface.java @@ -25,6 +25,7 @@ import noppes.npcs.client.gui.util.IGuiData; import noppes.npcs.client.gui.util.IJTextAreaListener; import noppes.npcs.client.gui.util.ITextChangeListener; import noppes.npcs.client.gui.util.ITextfieldListener; +import noppes.npcs.client.gui.util.script.interpreter.js_parser.JSTypeRegistry; import noppes.npcs.constants.ScriptContext; import noppes.npcs.controllers.ScriptContainer; import noppes.npcs.controllers.ScriptController; @@ -115,6 +116,12 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb // Initialize hooks from handler gui.hookList = new ArrayList<>(handler.getHooks()); + JSTypeRegistry registry = JSTypeRegistry.getInstance(); + if (!registry.isInitialized()) { + registry.initializeFromResources(); + } + registry.syncHooksFromScriptHookControllerIfNeeded(); + // Request data from server if (handler instanceof IScriptHandlerPacket) ((IScriptHandlerPacket) handler).requestData(); diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptDocument.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptDocument.java index 8a9a6c44d..4b7a72c61 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptDocument.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptDocument.java @@ -26,6 +26,7 @@ import noppes.npcs.client.gui.util.script.interpreter.type.synthetic.SyntheticTy import noppes.npcs.client.gui.util.script.ScopeInfo; import noppes.npcs.constants.ScriptContext; import noppes.npcs.controllers.data.DataScript; +import noppes.npcs.controllers.ScriptHookController; import java.util.*; import java.util.function.Function; @@ -925,20 +926,38 @@ public class ScriptDocument { TypeInfo returnType = TypeInfo.fromPrimitive("void"); List namespaces = scriptContext != null ? scriptContext.getNamespaces() : Collections.singletonList("Global"); - if (typeResolver.isJSHook(namespaces, funcName)) { - List sigs = typeResolver.getJSHookSignatures(namespaces, funcName); - if (!sigs.isEmpty()) { - JSTypeRegistry.HookSignature sig = sigs.get(0); - documentation = sig.doc; - - // Infer parameter types from hook signature + boolean runtimeHook = false; + if (ScriptHookController.Instance != null && scriptContext != null) { + String hookContext = scriptContext.hookContext; + if (hookContext != null && !hookContext.isEmpty()) { + runtimeHook = ScriptHookController.Instance.hasHook(hookContext, funcName); + } + } + + if (runtimeHook) { + if (typeResolver.isJSHook(namespaces, funcName)) { + List sigs = typeResolver.getJSHookSignatures(namespaces, funcName); + if (!sigs.isEmpty()) { + JSTypeRegistry.HookSignature sig = sigs.get(0); + documentation = sig.doc; + + if (paramList != null && !paramList.trim().isEmpty()) { + String[] paramNames = paramList.split(","); + if (paramNames.length > 0) { + String paramName = paramNames[0].trim(); + TypeInfo paramType = typeResolver.resolveJSType(sig.paramType); + int paramStart = m.start(2) + paramList.indexOf(paramName); + params.add(FieldInfo.parameter(paramName, paramType, paramStart, null)); + } + } + } + } else { if (paramList != null && !paramList.trim().isEmpty()) { String[] paramNames = paramList.split(","); if (paramNames.length > 0) { String paramName = paramNames[0].trim(); - TypeInfo paramType = typeResolver.resolveJSType(sig.paramType); int paramStart = m.start(2) + paramList.indexOf(paramName); - params.add(FieldInfo.parameter(paramName, paramType, paramStart, null)); + params.add(FieldInfo.parameter(paramName, TypeInfo.unresolved("any", "any"), paramStart, null)); } } } diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/JSTypeRegistry.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/JSTypeRegistry.java index 70ce25a74..68796adb2 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/JSTypeRegistry.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/JSTypeRegistry.java @@ -4,6 +4,8 @@ import net.minecraft.client.Minecraft; import net.minecraft.util.ResourceLocation; import noppes.npcs.scripted.NpcAPI; import noppes.npcs.client.gui.util.script.interpreter.bridge.DtsJavaBridge; +import noppes.npcs.api.handler.IHookDefinition; +import noppes.npcs.controllers.ScriptHookController; import java.io.*; import java.net.URL; @@ -59,6 +61,7 @@ public class JSTypeRegistry { private boolean initialized = false; private boolean initializationAttempted = false; + private int lastSyncedHookRevision = -1; private String currentSource = null; @@ -845,6 +848,80 @@ public class JSTypeRegistry { initialized = false; initializationAttempted = false; currentSource = null; + lastSyncedHookRevision = -1; + } + + /** + * Sync hook signatures from ScriptHookController into this registry. + * Uses revision-gating so repeated calls are no-ops when nothing changed. + */ + public void syncHooksFromScriptHookControllerIfNeeded() { + try { + if (ScriptHookController.Instance == null) { + return; + } + + int currentRevision = ScriptHookController.Instance.getHookRevision(); + if (currentRevision == lastSyncedHookRevision) { + return; + } + + hooks.clear(); + contextHooks.clear(); + + Set seen = new HashSet<>(); + + String[] contexts = ScriptHookController.Instance.getContexts(); + if (contexts != null) { + for (String context : contexts) { + if (context == null || context.isEmpty()) { + continue; + } + List defs = ScriptHookController.Instance.getAllHookDefinitions(context); + if (defs == null || defs.isEmpty()) { + continue; + } + + for (IHookDefinition def : defs) { + if (def == null) { + continue; + } + String hookName = def.hookName(); + if (hookName == null || hookName.isEmpty()) { + continue; + } + + String usableTypeName = def.getUsableTypeName(); + String namespace; + String paramType; + + if (usableTypeName != null && usableTypeName.contains(".")) { + namespace = usableTypeName.substring(0, usableTypeName.indexOf('.')); + } else { + namespace = GLOBAL_NAMESPACE; + } + + paramType = usableTypeName != null ? usableTypeName : "any"; + + String[] paramNames = def.paramNames(); + String paramName = (paramNames != null && paramNames.length > 0 && paramNames[0] != null && !paramNames[0].isEmpty()) + ? paramNames[0] + : "event"; + + String dedupKey = namespace + "|" + hookName + "|" + paramType + "|" + paramName; + if (!seen.add(dedupKey)) { + continue; + } + registerHook(namespace, hookName, paramName, paramType); + } + } + } + + lastSyncedHookRevision = currentRevision; + } catch (Throwable t) { + System.err.println("[JSTypeRegistry] Failed to sync runtime hooks: " + t.getMessage()); + t.printStackTrace(); + } } private void setCurrentSource(String source) { diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/TypeScriptDefinitionParser.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/TypeScriptDefinitionParser.java index 3b3a1d1d2..a5c668454 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/TypeScriptDefinitionParser.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/TypeScriptDefinitionParser.java @@ -123,7 +123,8 @@ public class TypeScriptDefinitionParser { public void parseDefinitionFile(String content, String fileName) { // Special handling for hooks.d.ts - extract function signatures if (fileName.contains("hooks.d.ts")) { - parseHooksFile(content); + // Commented out as hooks are now parsed through ScriptHookController which is the universal source of truth for registered hooks. + // parseHooksFile(content); return; } diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeResolver.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeResolver.java index 579a7fc3e..15e825ff2 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeResolver.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeResolver.java @@ -141,6 +141,7 @@ public class TypeResolver { if (!jsTypeRegistry.isInitialized()) { jsTypeRegistry.initializeFromResources(); } + jsTypeRegistry.syncHooksFromScriptHookControllerIfNeeded(); } return jsTypeRegistry; }