From aae2fdb23a3bff11efdfca9a35028f28c109e257 Mon Sep 17 00:00:00 2001 From: bigguy345 Date: Mon, 26 Jan 2026 03:49:38 +0200 Subject: [PATCH] Added DataScript global definitions i.e. npc, world, event to syntax highlighter as global vars --- .../noppes/npcs/client/gui/GuiScript.java | 20 +++- .../client/gui/util/GuiScriptTextArea.java | 4 + .../script/interpreter/ScriptDocument.java | 44 +++++++++ .../interpreter/ScriptTextContainer.java | 20 +++- .../npcs/controllers/data/DataScript.java | 91 +++++++++++++++---- 5 files changed, 155 insertions(+), 24 deletions(-) diff --git a/src/main/java/noppes/npcs/client/gui/GuiScript.java b/src/main/java/noppes/npcs/client/gui/GuiScript.java index 8d960e142..bbbe4e1bf 100644 --- a/src/main/java/noppes/npcs/client/gui/GuiScript.java +++ b/src/main/java/noppes/npcs/client/gui/GuiScript.java @@ -16,6 +16,8 @@ import noppes.npcs.client.gui.util.GuiNpcButton; import noppes.npcs.client.gui.util.GuiNpcLabel; import noppes.npcs.client.gui.util.GuiNpcTextArea; import noppes.npcs.client.gui.util.GuiScriptTextArea; +import noppes.npcs.client.gui.util.script.interpreter.ScriptTextContainer; +import noppes.npcs.constants.EnumScriptType; import noppes.npcs.constants.ScriptContext; import noppes.npcs.controllers.ScriptContainer; import noppes.npcs.controllers.ScriptController; @@ -160,6 +162,10 @@ public class GuiScript extends GuiScriptInterface { activeArea.setLanguage(script.getLanguage()); activeArea.setScriptContext(getScriptContext()); + + // Set editor globals based on the active NPC hook + String hookName = EnumScriptType.values()[activeTab].function; + applyEditorGlobals(activeArea, hookName); // Setup fullscreen key binding GuiScriptTextArea.KEYS.FULLSCREEN.setTask(e -> { @@ -225,6 +231,19 @@ public class GuiScript extends GuiScriptInterface { addButton(new GuiNpcButton(106, guiLeft + 232, guiTop + 71, 150, 20, "script.openfolder")); } + // Apply editor globals for the active NPC hook. + private void applyEditorGlobals(GuiScriptTextArea activeArea, String hookName) { + if (activeArea == null) + return; + + ScriptTextContainer textContainer = activeArea.getContainer(); + if (textContainer == null) + return; + + if (script != null) + textContainer.setEditorGlobalsMap(script.getEditorGlobals(hookName)); + } + @Override protected String getConsoleText() { Map map = this.script.getOldConsoleText(); @@ -371,4 +390,3 @@ public class GuiScript extends GuiScriptInterface { super.mouseClicked(mouseX, mouseY, mouseButton); } } - diff --git a/src/main/java/noppes/npcs/client/gui/util/GuiScriptTextArea.java b/src/main/java/noppes/npcs/client/gui/util/GuiScriptTextArea.java index c8e3af3de..da994eb4f 100644 --- a/src/main/java/noppes/npcs/client/gui/util/GuiScriptTextArea.java +++ b/src/main/java/noppes/npcs/client/gui/util/GuiScriptTextArea.java @@ -2652,6 +2652,10 @@ public class GuiScriptTextArea extends GuiNpcTextField { return this.container != null ? this.container.getScriptContext() : ScriptContext.GLOBAL; } + public ScriptTextContainer getContainer() { + return this.container; + } + /** * Add implicit imports that should be resolved without explicit import statements. * Used for JaninoScript default imports and hook parameter types. 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 5a5e29ef6..8f8002e56 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 @@ -24,6 +24,7 @@ import noppes.npcs.client.gui.util.script.interpreter.type.synthetic.SyntheticFi import noppes.npcs.client.gui.util.script.interpreter.type.synthetic.SyntheticMethod; import noppes.npcs.client.gui.util.script.interpreter.type.synthetic.SyntheticType; import noppes.npcs.constants.ScriptContext; +import noppes.npcs.controllers.data.DataScript; import java.util.*; import java.util.regex.Matcher; @@ -130,6 +131,9 @@ public class ScriptDocument { // Determines which hooks and event types are available private ScriptContext scriptContext = ScriptContext.GLOBAL; + // Editor-injected globals (name -> type name) provided by the handler + private Map editorGlobals = Collections.emptyMap(); + // Implicit imports from JaninoScript default imports and hook parameter types // These are types that should be resolved even without explicit import statements private final Set implicitImports = new HashSet<>(); @@ -198,6 +202,26 @@ public class ScriptDocument { return scriptContext; } + /** + * Set editor-injected globals (name -> type name). + * i.e. DataScript global definitions {@link DataScript#getEditorGlobals(String)} + * This is provided by the script handler at editor init time. + */ + public void setEditorGlobals(Map globals) { + if (globals == null || globals.isEmpty()) { + this.editorGlobals = Collections.emptyMap(); + return; + } + this.editorGlobals = new LinkedHashMap<>(globals); + } + + /** + * Get editor-injected globals (name -> type name). + */ + public Map getEditorGlobals() { + return Collections.unmodifiableMap(editorGlobals); + } + /** * Add implicit imports that should be resolved without explicit import statements. * Used for JaninoScript default imports and hook parameter types. @@ -4688,6 +4712,16 @@ public class ScriptDocument { continue; } } + + // Mark DataScript global variable definitions + if (!editorGlobals.isEmpty() && editorGlobals.containsKey(name)) { + FieldInfo fieldInfo = resolveVariable(name, position); + if (fieldInfo != null) { + 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 @@ -5664,6 +5698,16 @@ public class ScriptDocument { return FieldInfo.globalField(name, typeInfo, -1); } } + + // Check if is DataScript global variable definition + String editorGlobalType = editorGlobals.get(name); + if (editorGlobalType != null) { + TypeInfo typeInfo = resolveType(editorGlobalType); + if (typeInfo == null || !typeInfo.isResolved()) { + typeInfo = TypeInfo.ANY; + } + return FieldInfo.globalField(name, typeInfo, -1); + } } return null; diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptTextContainer.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptTextContainer.java index 4b2e89e26..05da45227 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptTextContainer.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptTextContainer.java @@ -2,12 +2,10 @@ package noppes.npcs.client.gui.util.script.interpreter; import noppes.npcs.client.ClientProxy; import noppes.npcs.client.gui.util.script.JavaTextContainer; -import noppes.npcs.client.gui.util.script.interpreter.field.FieldInfo; -import noppes.npcs.client.gui.util.script.interpreter.method.MethodInfo; import noppes.npcs.constants.ScriptContext; -import java.util.ArrayList; -import java.util.List; +import java.util.Collections; +import java.util.Map; /** * New interpreter-based text container that replaces JavaTextContainer. @@ -39,6 +37,9 @@ public class ScriptTextContainer extends JavaTextContainer { /** The script context: NPC, PLAYER, BLOCK, ITEM, etc. */ private ScriptContext scriptContext = ScriptContext.GLOBAL; + /** Editor-injected globals (name -> type name) */ + private Map editorGlobals = Collections.emptyMap(); + public ScriptTextContainer(String text) { super(text); if (USE_NEW_INTERPRETER) { @@ -94,6 +95,16 @@ public class ScriptTextContainer extends JavaTextContainer { return scriptContext; } + /** + * Set editor-injected globals (name -> type name). + */ + public void setEditorGlobalsMap(java.util.Map globals) { + this.editorGlobals = globals != null ? globals : java.util.Collections.emptyMap(); + if (document != null) { + document.setEditorGlobals(this.editorGlobals); + } + } + /** * Add implicit imports that should be resolved without explicit import statements. * Used for JaninoScript default imports and hook parameter types. @@ -143,6 +154,7 @@ public class ScriptTextContainer extends JavaTextContainer { if (document == null) { document = new ScriptDocument(this.text, this.language); document.setScriptContext(this.scriptContext); + document.setEditorGlobals(this.editorGlobals); } init(width, height); diff --git a/src/main/java/noppes/npcs/controllers/data/DataScript.java b/src/main/java/noppes/npcs/controllers/data/DataScript.java index 74806605f..fada88b04 100644 --- a/src/main/java/noppes/npcs/controllers/data/DataScript.java +++ b/src/main/java/noppes/npcs/controllers/data/DataScript.java @@ -20,6 +20,7 @@ import noppes.npcs.constants.EnumScriptType; import noppes.npcs.constants.ScriptContext; import noppes.npcs.controllers.ScriptContainer; import noppes.npcs.controllers.ScriptController; +import noppes.npcs.controllers.ScriptHookController; import noppes.npcs.entity.EntityNPCInterface; import noppes.npcs.janino.EventJaninoScript; import noppes.npcs.scripted.NpcAPI; @@ -28,15 +29,10 @@ import noppes.npcs.scripted.constants.EntityType; import noppes.npcs.scripted.constants.JobType; import noppes.npcs.scripted.constants.RoleType; import noppes.npcs.scripted.entity.ScriptNpc; +import noppes.npcs.api.handler.IHookDefinition; import javax.script.ScriptEngine; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; +import java.util.*; public class DataScript implements IScriptHandlerPacket { public List eventScripts = new ArrayList<>(); @@ -56,6 +52,17 @@ public class DataScript implements IScriptHandlerPacket { public boolean aiNeedsUpdate = false; public boolean hasInited = false; + // Editor/runtime globals descriptor used for a single source of truth. + private static final class GlobalDefinition { + private final String typeName; + private final Object value; + + private GlobalDefinition(String typeName, Object value) { + this.typeName = typeName; + this.value = value; + } + } + public DataScript(EntityNPCInterface npc) { for (int i = 0; i < 15; i++) { this.setNPCScript(i, new ScriptContainer(this)); @@ -180,21 +187,12 @@ public class DataScript implements IScriptHandlerPacket { LogWriter.postScriptLog(npc.field_110179_h, type, String.format("[%s] NPC %s (%s, %s, %s) | Objects: %s", ((String) type.function).toUpperCase(), npc.display.name, (int) npc.posX, (int) npc.posY, (int) npc.posZ, Arrays.toString(obs))); } } - return callScript(script, event); + return callScript(script, type.function, event); } - private boolean callScript(ScriptContainer script, Event event) { + private boolean callScript(ScriptContainer script, String hookName, Event event) { ScriptEngine engine = script.engine; - engine.put("npc", dummyNpc); - engine.put("world", dummyWorld); - engine.put("event", event); - engine.put("API", NpcAPI.Instance()); - engine.put("EntityType", entities); - engine.put("RoleType", roles); - engine.put("JobType", jobs); - for (Map.Entry engineObjects : NpcAPI.engineObjects.entrySet()) { - engine.put(engineObjects.getKey(), engineObjects.getValue()); - } + applyGlobalsToEngine(engine, hookName, event); script.run(engine); if (clientNeedsUpdate) { @@ -254,6 +252,61 @@ public class DataScript implements IScriptHandlerPacket { } } + public Map getEditorGlobals(String hookName) { + Map definitions = getGlobalDefinitions(hookName, null); + Map globals = new LinkedHashMap<>(); + for (Map.Entry entry : definitions.entrySet()) { + String typeName = entry.getValue().typeName; + if (typeName != null && !typeName.isEmpty()) { + globals.put(entry.getKey(), typeName); + } + } + return globals; + } + + // Apply runtime globals plus engine-only bindings. + private void applyGlobalsToEngine(ScriptEngine engine, String hookName, Event event) { + Map definitions = getGlobalDefinitions(hookName, event); + for (Map.Entry entry : definitions.entrySet()) { + engine.put(entry.getKey(), entry.getValue().value); + } + + engine.put("API", NpcAPI.Instance()); + for (Map.Entry engineObjects : NpcAPI.engineObjects.entrySet()) { + engine.put(engineObjects.getKey(), engineObjects.getValue()); + } + } + + // Build shared globals (editor types + runtime values). + private Map getGlobalDefinitions(String hookName, Event event) { + Map globals = new LinkedHashMap<>(); + globals.put("npc", new GlobalDefinition("ICustomNpc", dummyNpc)); + globals.put("world", new GlobalDefinition("IWorld", dummyWorld)); + globals.put("event", new GlobalDefinition(resolveEditorEventTypeName(hookName), event)); + globals.put("EntityType", new GlobalDefinition("noppes.npcs.scripted.constants.EntityType", entities)); + globals.put("RoleType", new GlobalDefinition("noppes.npcs.scripted.constants.RoleType", roles)); + globals.put("JobType", new GlobalDefinition("noppes.npcs.scripted.constants.JobType", jobs)); + + return globals; + } + + // Resolve editor-only event type from hook name with a single fallback. + private String resolveEditorEventTypeName(String hookName) { + final String fallback = "INpcEvent"; + if (hookName == null || hookName.isEmpty()) + return fallback; + + IHookDefinition definition = ScriptHookController.Instance.getHookDefinition(getContext().hookContext, hookName); + if (definition == null) + return fallback; + + String typeName = definition.getUsableTypeName(); + if (typeName == null || typeName.isEmpty()) + return fallback; + + return typeName; + } + public boolean isClient() { return this.npc.isRemote(); }