From c10fa8e17ea71a3dc3e7ac75160b5affcfec31d3 Mon Sep 17 00:00:00 2001 From: bigguy345 Date: Tue, 6 Jan 2026 04:01:19 +0200 Subject: [PATCH] Added global engine objects to JSTypeRegistry and marked them properly as global fields --- .../script/interpreter/ScriptDocument.java | 33 ++++++- .../interpreter/js_parser/JSTypeRegistry.java | 93 +++++++++++++++++++ 2 files changed, 124 insertions(+), 2 deletions(-) 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 7d72fd632..a96c0195c 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 @@ -1787,11 +1787,11 @@ public class ScriptDocument { // Chained field accesses - UNIFIED (uses resolveVariable which handles both) markChainedFieldAccesses(marks); - + markImportedClassUsages(marks); + // Java-specific final passes if (!isJavaScript()) { markCastTypes(marks); - markImportedClassUsages(marks); markUnusedImports(marks); } @@ -4338,6 +4338,21 @@ public class ScriptDocument { } } + if (isJavaScript()) { + // check if a variable in global objects + JSTypeRegistry registry = JSTypeRegistry.getInstance(); + String globalObjectType = registry.getGlobalObjectType(name); + if (globalObjectType != null) { + // Resolve the type and create a FieldInfo for it + FieldInfo fieldInfo = resolveVariable(name, position); + if (fieldInfo != null && fieldInfo.isResolved()) { + Object metadata = callInfo != null ? new FieldInfo.ArgInfo(fieldInfo, callInfo) : fieldInfo; + marks.add(new ScriptLine.Mark(m.start(1), m.end(1), TokenType.GLOBAL_FIELD, metadata)); + continue; + } + } + } + // Skip uppercase if not a known field - type handling will deal with it if (isUppercase) continue; @@ -5259,6 +5274,20 @@ public class ScriptDocument { if (globalFields.containsKey(name)) { return globalFields.get(name); } + + // Check JS global objects from JSTypeRegistry (like API, DBCAPI) + if (isJavaScript()) { + JSTypeRegistry registry = JSTypeRegistry.getInstance(); + String globalObjectType = registry.getGlobalObjectType(name); + if (globalObjectType != null) { + // Resolve the type and create a FieldInfo for it + TypeInfo typeInfo = resolveType(globalObjectType); + if (typeInfo != null && typeInfo.isResolved()) { + // Create a global field for this object with GLOBAL scope + return FieldInfo.globalField(name, typeInfo, -1); + } + } + } return 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 77888054e..f0126243b 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 @@ -2,6 +2,7 @@ package noppes.npcs.client.gui.util.script.interpreter.js_parser; import net.minecraft.client.Minecraft; import net.minecraft.util.ResourceLocation; +import noppes.npcs.scripted.NpcAPI; import java.io.*; import java.net.URL; @@ -29,6 +30,10 @@ public class JSTypeRegistry { // Hook function signatures: functionName -> list of (paramName, paramType) pairs // Multiple entries for overloaded hooks private final Map> hooks = new LinkedHashMap<>(); + + // Global object instances: name -> type (e.g., "API" -> "AbstractNpcAPI") + // These are treated as instance objects, not static classes + private final Map globalEngineObjects = new LinkedHashMap<>(); // Primitive types private static final Set PRIMITIVES = new HashSet<>(Arrays.asList( @@ -79,6 +84,8 @@ public class JSTypeRegistry { } resolveInheritance(); + registerEngineGlobalObjects(); + initialized = true; System.out.println("[JSTypeRegistry] Loaded " + types.size() + " types, " + hooks.size() + " hooks from resources"); } catch (Exception e) { @@ -318,6 +325,12 @@ public class JSTypeRegistry { } } + // Check global engine objects + if (globalEngineObjects.containsKey(baseName)) { + String typeName = globalEngineObjects.get(baseName); + return getType(typeName, visited); + } + return null; } @@ -399,6 +412,85 @@ public class JSTypeRegistry { public Map> getAllHooks() { return hooks; } + + /** + * Register a global object instance (like API, DBCAPI from NpcAPI.engineObjects). + * These are treated as instance objects, not static classes. + * @param name The global variable name (e.g., "API") + * @param typeName The type name (e.g., "AbstractNpcAPI") + */ + public void registerGlobalObject(String name, String typeName) { + globalEngineObjects.put(name, typeName); + } + + /** + * Get the type name for a global object. + * @param name The global variable name + * @return The type name, or null if not a registered global object + */ + public String getGlobalObjectType(String name) { + return globalEngineObjects.get(name); + } + + /** + * Check if a name is a registered global object instance. + */ + public boolean isGlobalObject(String name) { + return globalEngineObjects.containsKey(name); + } + + /** + * Get all registered global objects. + */ + public Map getGlobalEngineObjects() { + return Collections.unmodifiableMap(globalEngineObjects); + } + + /** + * Registers global objects from NpcAPI.engineObjects into JSTypeRegistry. + * This allows the IDE to recognize API, DBCAPI, etc. as instance objects with autocomplete. + */ + private void registerEngineGlobalObjects() { + Map engineObjects = new HashMap<>(NpcAPI.engineObjects); + engineObjects.put("API", NpcAPI.Instance()); //default API object + + if (engineObjects != null) { + for (Map.Entry entry : engineObjects.entrySet()) { + String name = entry.getKey(); // e.g., "API", "DBCAPI" + Object obj = entry.getValue(); // e.g., AbstractNpcAPI instance + + if (obj != null) { + // Get the actual class name + String concreteClassName = obj.getClass().getSimpleName(); + + // Map concrete implementation classes to their abstract interface names + // (because .d.ts files define the abstract interfaces, not the implementations) + String typeName = mapConcreteToAbstractClassName(concreteClassName); + + // Register as global object (instance, not static) + registerGlobalObject(name, typeName); + } + } + } + } + + /** + * Maps concrete implementation class names to their abstract interface names. + * For example: "NpcAPI" -> "AbstractNpcAPI", "DBCAPI" -> "AbstractDBCAPI" + */ + private String mapConcreteToAbstractClassName(String concreteClassName) { + // Check if the type exists in the registry as-is + if (types.containsKey(concreteClassName)) + return concreteClassName; + + // Try prepending "Abstract" if it doesn't exist + String abstractName = "Abstract" + concreteClassName; + if (types.containsKey(abstractName)) + return abstractName; + + // Default: return the original name + return concreteClassName; + } /** * Check if initialized. @@ -414,6 +506,7 @@ public class JSTypeRegistry { types.clear(); typeAliases.clear(); hooks.clear(); + globalEngineObjects.clear(); initialized = false; }