mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-12 20:07:32 +00:00
Merge remote-tracking branch 'origin/syntax-highlighter-unified' into syntax-highlighter-unified
This commit is contained in:
commit
0d11db729b
13 files changed
+831
-67
No files matched your search
@@ -13,6 +13,7 @@ import noppes.npcs.NoppesStringUtils;
|
||||
import noppes.npcs.client.NoppesUtil;
|
||||
import noppes.npcs.client.gui.script.GuiNPCEventScripts;
|
||||
import noppes.npcs.client.gui.util.*;
|
||||
import noppes.npcs.constants.ScriptContext;
|
||||
import noppes.npcs.controllers.ScriptContainer;
|
||||
import noppes.npcs.controllers.ScriptController;
|
||||
import noppes.npcs.controllers.data.DataScript;
|
||||
@@ -100,6 +101,10 @@ public class GuiScript extends GuiNPCInterface implements IGuiData, GuiYesNoCall
|
||||
activeArea.init(guiLeft + 74, guiTop + 4, 239, 208, container == null ? "" : container.script);
|
||||
}
|
||||
|
||||
// Set language and script context for proper syntax highlighting and autocomplete
|
||||
activeArea.setLanguage(script.getLanguage());
|
||||
activeArea.setScriptContext(ScriptContext.NPC);
|
||||
|
||||
addButton(new GuiNpcButton(102, guiLeft + 315, guiTop + 4, 50, 20, "gui.clear"));
|
||||
addButton(new GuiNpcButton(101, guiLeft + 366, guiTop + 4, 50, 20, "gui.paste"));
|
||||
addButton(new GuiNpcButton(100, guiLeft + 315, guiTop + 25, 50, 20, "gui.copy"));
|
||||
|
||||
@@ -3,6 +3,7 @@ package noppes.npcs.client.gui.script;
|
||||
import kamkeel.npcs.network.packets.request.script.BlockScriptPacket;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import noppes.npcs.blocks.tiles.TileScripted;
|
||||
import noppes.npcs.constants.ScriptContext;
|
||||
|
||||
|
||||
public class GuiScriptBlock extends GuiScriptInterface {
|
||||
@@ -33,6 +34,11 @@ public class GuiScriptBlock extends GuiScriptInterface {
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ScriptContext getScriptContext() {
|
||||
return ScriptContext.BLOCK;
|
||||
}
|
||||
|
||||
public void save() {
|
||||
if (loaded) {
|
||||
super.save();
|
||||
|
||||
@@ -25,6 +25,7 @@ import noppes.npcs.client.gui.util.ITextChangeListener;
|
||||
import noppes.npcs.client.gui.util.ITextfieldListener;
|
||||
import noppes.npcs.controllers.ScriptContainer;
|
||||
import noppes.npcs.controllers.ScriptController;
|
||||
import noppes.npcs.constants.ScriptContext;
|
||||
import noppes.npcs.controllers.data.ForgeDataScript;
|
||||
import noppes.npcs.controllers.data.IScriptHandler;
|
||||
import noppes.npcs.scripted.item.ScriptCustomItem;
|
||||
@@ -197,6 +198,9 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb
|
||||
// Set the scripting language for proper syntax highlighting
|
||||
activeArea.setLanguage(this.handler.getLanguage());
|
||||
|
||||
// Set the script context for context-aware hook autocomplete
|
||||
activeArea.setScriptContext(getScriptContext());
|
||||
|
||||
// Setup fullscreen key binding
|
||||
GuiScriptTextArea.KEYS.FULLSCREEN.setTask(e -> {
|
||||
if (e.isPress()) {
|
||||
@@ -513,6 +517,16 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb
|
||||
this.initGui();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the script context for this GUI.
|
||||
* Override in subclasses to return the appropriate context.
|
||||
*
|
||||
* @return The script context (default: GLOBAL)
|
||||
*/
|
||||
protected ScriptContext getScriptContext() {
|
||||
return ScriptContext.GLOBAL;
|
||||
}
|
||||
|
||||
public void save() {
|
||||
if (loaded)
|
||||
this.setScript();
|
||||
|
||||
@@ -6,6 +6,7 @@ import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import noppes.npcs.CustomItems;
|
||||
import noppes.npcs.NBTTags;
|
||||
import noppes.npcs.constants.ScriptContext;
|
||||
import noppes.npcs.scripted.item.ScriptCustomItem;
|
||||
|
||||
import java.util.Date;
|
||||
@@ -48,6 +49,11 @@ public class GuiScriptItem extends GuiScriptInterface {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ScriptContext getScriptContext() {
|
||||
return ScriptContext.ITEM;
|
||||
}
|
||||
|
||||
public void save() {
|
||||
if (loaded) {
|
||||
super.save();
|
||||
|
||||
@@ -4,6 +4,7 @@ import kamkeel.npcs.network.packets.request.script.PlayerScriptPacket;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import noppes.npcs.NBTTags;
|
||||
import noppes.npcs.constants.ScriptContext;
|
||||
import noppes.npcs.constants.EnumScriptType;
|
||||
import noppes.npcs.controllers.ScriptContainer;
|
||||
import noppes.npcs.controllers.data.EffectScript;
|
||||
@@ -114,6 +115,11 @@ public class GuiScriptPlayers extends GuiScriptInterface {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ScriptContext getScriptContext() {
|
||||
return ScriptContext.PLAYER;
|
||||
}
|
||||
|
||||
public void save() {
|
||||
if (loaded) {
|
||||
super.save();
|
||||
|
||||
@@ -22,6 +22,7 @@ import noppes.npcs.client.gui.util.script.interpreter.hover.HoverState;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.hover.TokenHoverRenderer;
|
||||
import noppes.npcs.client.gui.util.script.autocomplete.AutocompleteManager;
|
||||
import noppes.npcs.client.key.impl.ScriptEditorKeys;
|
||||
import noppes.npcs.constants.ScriptContext;
|
||||
import noppes.npcs.util.ValueUtil;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
import org.lwjgl.input.Mouse;
|
||||
@@ -2629,6 +2630,27 @@ public class GuiScriptTextArea extends GuiNpcTextField {
|
||||
return this.container != null ? this.container.getLanguage() : "ECMAScript";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the script context (NPC, PLAYER, BLOCK, ITEM, etc.).
|
||||
* This determines which hooks and event types are available for autocomplete.
|
||||
*
|
||||
* @param context The script context
|
||||
*/
|
||||
public void setScriptContext(ScriptContext context) {
|
||||
if (this.container != null) {
|
||||
this.container.setScriptContext(context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current script context.
|
||||
*
|
||||
* @return The script context (NPC, PLAYER, BLOCK, ITEM, etc.)
|
||||
*/
|
||||
public ScriptContext getScriptContext() {
|
||||
return this.container != null ? this.container.getScriptContext() : ScriptContext.GLOBAL;
|
||||
}
|
||||
|
||||
public void setListener(ITextChangeListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import noppes.npcs.client.gui.util.script.interpreter.type.*;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.type.synthetic.SyntheticField;
|
||||
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 java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
@@ -125,6 +126,10 @@ public class ScriptDocument {
|
||||
// Script language: "ECMAScript", "Groovy", etc.
|
||||
private String language = "ECMAScript";
|
||||
|
||||
// Script context: NPC, PLAYER, BLOCK, ITEM, etc.
|
||||
// Determines which hooks and event types are available
|
||||
private ScriptContext scriptContext = ScriptContext.GLOBAL;
|
||||
|
||||
// Layout properties
|
||||
public int lineHeight = 13;
|
||||
public int totalHeight;
|
||||
@@ -170,6 +175,25 @@ public class ScriptDocument {
|
||||
return "ECMAScript".equalsIgnoreCase(language);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the script context (NPC, PLAYER, BLOCK, ITEM, etc.).
|
||||
* This determines which hooks and event types are available for autocomplete.
|
||||
*
|
||||
* @param context The script context
|
||||
*/
|
||||
public void setScriptContext(ScriptContext context) {
|
||||
this.scriptContext = context != null ? context : ScriptContext.GLOBAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current script context.
|
||||
*
|
||||
* @return The script context (NPC, PLAYER, BLOCK, ITEM, etc.)
|
||||
*/
|
||||
public ScriptContext getScriptContext() {
|
||||
return scriptContext;
|
||||
}
|
||||
|
||||
// ==================== TEXT MANAGEMENT ====================
|
||||
|
||||
public void setText(String text) {
|
||||
@@ -686,11 +710,13 @@ public class ScriptDocument {
|
||||
JSDocInfo jsDoc = jsDocParser.extractJSDocBefore(text, m.start());
|
||||
|
||||
// For JS hooks, infer parameter types from registry
|
||||
// Use the script context's namespaces (e.g., ["IPlayerEvent", "IAnimationEvent"]) for lookup
|
||||
List<FieldInfo> params = new ArrayList<>();
|
||||
TypeInfo returnType = TypeInfo.fromPrimitive("void");
|
||||
List<String> namespaces = scriptContext != null ? scriptContext.getNamespaces() : Collections.singletonList("Global");
|
||||
|
||||
if (typeResolver.isJSHook(funcName)) {
|
||||
List<JSTypeRegistry.HookSignature> sigs = typeResolver.getJSHookSignatures(funcName);
|
||||
if (typeResolver.isJSHook(namespaces, funcName)) {
|
||||
List<JSTypeRegistry.HookSignature> sigs = typeResolver.getJSHookSignatures(namespaces, funcName);
|
||||
if (!sigs.isEmpty()) {
|
||||
JSTypeRegistry.HookSignature sig = sigs.get(0);
|
||||
documentation = sig.doc;
|
||||
|
||||
+28
-1
@@ -4,6 +4,7 @@ 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;
|
||||
@@ -31,10 +32,13 @@ public class ScriptTextContainer extends JavaTextContainer {
|
||||
public static boolean USE_NEW_INTERPRETER = true;
|
||||
|
||||
private ScriptDocument document;
|
||||
|
||||
|
||||
/** The scripting language: "ECMAScript", "Groovy", etc. */
|
||||
private String language = "ECMAScript";
|
||||
|
||||
/** The script context: NPC, PLAYER, BLOCK, ITEM, etc. */
|
||||
private ScriptContext scriptContext = ScriptContext.GLOBAL;
|
||||
|
||||
public ScriptTextContainer(String text) {
|
||||
super(text);
|
||||
if (USE_NEW_INTERPRETER) {
|
||||
@@ -68,6 +72,28 @@ public class ScriptTextContainer extends JavaTextContainer {
|
||||
return language;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the script context (NPC, PLAYER, BLOCK, ITEM, etc.).
|
||||
* This determines which hooks and event types are available for autocomplete.
|
||||
*
|
||||
* @param context The script context
|
||||
*/
|
||||
public void setScriptContext(ScriptContext context) {
|
||||
this.scriptContext = context != null ? context : ScriptContext.GLOBAL;
|
||||
if (document != null) {
|
||||
document.setScriptContext(this.scriptContext);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current script context.
|
||||
*
|
||||
* @return The script context (NPC, PLAYER, BLOCK, ITEM, etc.)
|
||||
*/
|
||||
public ScriptContext getScriptContext() {
|
||||
return scriptContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(int width, int height) {
|
||||
if (!USE_NEW_INTERPRETER) {
|
||||
@@ -103,6 +129,7 @@ public class ScriptTextContainer extends JavaTextContainer {
|
||||
}
|
||||
|
||||
document = new ScriptDocument(this.text, this.language);
|
||||
document.setScriptContext(this.scriptContext);
|
||||
init(width, height);
|
||||
}
|
||||
|
||||
|
||||
+224
-14
@@ -27,10 +27,18 @@ public class JSTypeRegistry {
|
||||
// Type aliases (simple name -> full type name)
|
||||
private final Map<String, String> typeAliases = new HashMap<>();
|
||||
|
||||
// Hook function signatures: functionName -> list of (paramName, paramType) pairs
|
||||
// Multiple entries for overloaded hooks
|
||||
// Context-aware hook function signatures: namespace -> functionName -> list of signatures
|
||||
// The namespace is the event interface name (e.g., "INpcEvent", "IPlayerEvent")
|
||||
// This allows any mod to register hooks without modifying an enum
|
||||
private final Map<String, Map<String, List<HookSignature>>> contextHooks = new LinkedHashMap<>();
|
||||
|
||||
// Legacy hook storage: functionName -> list of (paramName, paramType) pairs
|
||||
// Kept for backward compatibility with code that doesn't use contexts
|
||||
private final Map<String, List<HookSignature>> hooks = new LinkedHashMap<>();
|
||||
|
||||
// Fallback namespace for hooks that don't match a specific context
|
||||
private static final String GLOBAL_NAMESPACE = "Global";
|
||||
|
||||
// Global object instances: name -> type (e.g., "API" -> "AbstractNpcAPI")
|
||||
// These are treated as instance objects, not static classes
|
||||
private final Map<String, String> globalEngineObjects = new LinkedHashMap<>();
|
||||
@@ -275,11 +283,23 @@ public class JSTypeRegistry {
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a hook function signature.
|
||||
* Register a hook function signature with a namespace.
|
||||
*
|
||||
* @param namespace The event interface namespace (e.g., "INpcEvent", "IPlayerEvent")
|
||||
* @param functionName The hook function name (e.g., "interact", "damaged")
|
||||
* @param paramName The parameter name (e.g., "event")
|
||||
* @param paramType The parameter type (e.g., "INpcEvent.InteractEvent")
|
||||
*/
|
||||
public void registerHook(String functionName, String paramName, String paramType) {
|
||||
hooks.computeIfAbsent(functionName, k -> new ArrayList<>())
|
||||
.add(new HookSignature(paramName, paramType));
|
||||
public void registerHook(String namespace, String functionName, String paramName, String paramType) {
|
||||
HookSignature sig = new HookSignature(paramName, paramType, null, namespace);
|
||||
|
||||
// Add to context-specific map
|
||||
contextHooks.computeIfAbsent(namespace, k -> new LinkedHashMap<>())
|
||||
.computeIfAbsent(functionName, k -> new ArrayList<>())
|
||||
.add(sig);
|
||||
|
||||
// Also add to legacy hooks map for backward compatibility
|
||||
hooks.computeIfAbsent(functionName, k -> new ArrayList<>()).add(sig);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -369,7 +389,190 @@ public class JSTypeRegistry {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// ==================== Context-Aware Hook Methods ====================
|
||||
|
||||
/**
|
||||
* Get hook signatures for a specific script context.
|
||||
* Falls back to GLOBAL context if not found in the specified context.
|
||||
*
|
||||
* @param namespace The event interface namespace (e.g., "INpcEvent", "IPlayerEvent")
|
||||
* @param functionName The hook function name
|
||||
* @return List of hook signatures, or empty list if not found
|
||||
*/
|
||||
public List<HookSignature> getHookSignatures(String namespace, String functionName) {
|
||||
// First try the specific namespace
|
||||
Map<String, List<HookSignature>> namespaceMap = contextHooks.get(namespace);
|
||||
if (namespaceMap != null) {
|
||||
List<HookSignature> sigs = namespaceMap.get(functionName);
|
||||
if (sigs != null && !sigs.isEmpty()) {
|
||||
return sigs;
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to GLOBAL namespace
|
||||
if (!GLOBAL_NAMESPACE.equals(namespace)) {
|
||||
Map<String, List<HookSignature>> globalMap = contextHooks.get(GLOBAL_NAMESPACE);
|
||||
if (globalMap != null) {
|
||||
List<HookSignature> sigs = globalMap.get(functionName);
|
||||
if (sigs != null && !sigs.isEmpty()) {
|
||||
return sigs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a function name is a known hook in a specific namespace.
|
||||
*
|
||||
* @param namespace The event interface namespace
|
||||
* @param functionName The hook function name
|
||||
* @return true if the hook exists in this namespace or GLOBAL
|
||||
*/
|
||||
public boolean isHook(String namespace, String functionName) {
|
||||
return !getHookSignatures(namespace, functionName).isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parameter type for a hook function in a specific namespace.
|
||||
* Falls back to GLOBAL namespace if not found.
|
||||
*
|
||||
* @param namespace The event interface namespace
|
||||
* @param functionName The hook function name
|
||||
* @return The parameter type, or null if not found
|
||||
*/
|
||||
public String getHookParameterType(String namespace, String functionName) {
|
||||
List<HookSignature> sigs = getHookSignatures(namespace, functionName);
|
||||
if (!sigs.isEmpty()) {
|
||||
return sigs.get(0).paramType;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all hook names for a specific namespace.
|
||||
*
|
||||
* @param namespace The event interface namespace
|
||||
* @return Set of hook function names available in this namespace
|
||||
*/
|
||||
public Set<String> getHookNames(String namespace) {
|
||||
Set<String> names = new HashSet<>();
|
||||
|
||||
// Add hooks from the specific namespace
|
||||
Map<String, List<HookSignature>> namespaceMap = contextHooks.get(namespace);
|
||||
if (namespaceMap != null) {
|
||||
names.addAll(namespaceMap.keySet());
|
||||
}
|
||||
|
||||
// Also add GLOBAL hooks
|
||||
if (!GLOBAL_NAMESPACE.equals(namespace)) {
|
||||
Map<String, List<HookSignature>> globalMap = contextHooks.get(GLOBAL_NAMESPACE);
|
||||
if (globalMap != null) {
|
||||
names.addAll(globalMap.keySet());
|
||||
}
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all hooks organized by namespace.
|
||||
*
|
||||
* @return Map of namespace -> hookName -> signatures
|
||||
*/
|
||||
public Map<String, Map<String, List<HookSignature>>> getAllContextHooks() {
|
||||
return Collections.unmodifiableMap(contextHooks);
|
||||
}
|
||||
|
||||
// ==================== MULTI-NAMESPACE LOOKUP (for ScriptContext) ====================
|
||||
|
||||
/**
|
||||
* Get hook signatures by searching through multiple namespaces.
|
||||
* This is used when a ScriptContext has multiple event types (e.g., Player has
|
||||
* IPlayerEvent, IAnimationEvent, IPartyEvent, etc.)
|
||||
*
|
||||
* @param namespaces List of namespaces to search (in priority order)
|
||||
* @param functionName The hook function name
|
||||
* @return List of hook signatures from the first matching namespace, or empty list
|
||||
*/
|
||||
public List<HookSignature> getHookSignatures(List<String> namespaces, String functionName) {
|
||||
// Search through all namespaces in order
|
||||
for (String namespace : namespaces) {
|
||||
Map<String, List<HookSignature>> namespaceMap = contextHooks.get(namespace);
|
||||
if (namespaceMap != null) {
|
||||
List<HookSignature> sigs = namespaceMap.get(functionName);
|
||||
if (sigs != null && !sigs.isEmpty()) {
|
||||
return sigs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to GLOBAL namespace
|
||||
Map<String, List<HookSignature>> globalMap = contextHooks.get(GLOBAL_NAMESPACE);
|
||||
if (globalMap != null) {
|
||||
List<HookSignature> sigs = globalMap.get(functionName);
|
||||
if (sigs != null && !sigs.isEmpty()) {
|
||||
return sigs;
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a function name is a known hook in any of the given namespaces.
|
||||
*
|
||||
* @param namespaces List of namespaces to search
|
||||
* @param functionName The hook function name
|
||||
* @return true if the hook exists in any namespace or GLOBAL
|
||||
*/
|
||||
public boolean isHook(List<String> namespaces, String functionName) {
|
||||
return !getHookSignatures(namespaces, functionName).isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parameter type for a hook function, searching through multiple namespaces.
|
||||
*
|
||||
* @param namespaces List of namespaces to search
|
||||
* @param functionName The hook function name
|
||||
* @return The parameter type, or null if not found
|
||||
*/
|
||||
public String getHookParameterType(List<String> namespaces, String functionName) {
|
||||
List<HookSignature> sigs = getHookSignatures(namespaces, functionName);
|
||||
if (!sigs.isEmpty()) {
|
||||
return sigs.get(0).paramType;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all hook names available in any of the given namespaces.
|
||||
*
|
||||
* @param namespaces List of namespaces to search
|
||||
* @return Set of hook function names available in these namespaces
|
||||
*/
|
||||
public Set<String> getHookNames(List<String> namespaces) {
|
||||
Set<String> names = new HashSet<>();
|
||||
|
||||
// Add hooks from all specified namespaces
|
||||
for (String namespace : namespaces) {
|
||||
Map<String, List<HookSignature>> namespaceMap = contextHooks.get(namespace);
|
||||
if (namespaceMap != null) {
|
||||
names.addAll(namespaceMap.keySet());
|
||||
}
|
||||
}
|
||||
|
||||
// Also add GLOBAL hooks
|
||||
Map<String, List<HookSignature>> globalMap = contextHooks.get(GLOBAL_NAMESPACE);
|
||||
if (globalMap != null) {
|
||||
names.addAll(globalMap.keySet());
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve all type parameters for all types.
|
||||
* Called after all .d.ts files are loaded (Phase 2).
|
||||
@@ -530,31 +733,38 @@ public class JSTypeRegistry {
|
||||
types.clear();
|
||||
typeAliases.clear();
|
||||
hooks.clear();
|
||||
contextHooks.clear();
|
||||
globalEngineObjects.clear();
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Represents a hook function signature.
|
||||
* Represents a hook function signature with its namespace.
|
||||
*/
|
||||
public static class HookSignature {
|
||||
public final String paramName;
|
||||
public final String paramType;
|
||||
public final String doc;
|
||||
|
||||
public final String namespace; // The event interface namespace (e.g., "INpcEvent")
|
||||
|
||||
public HookSignature(String paramName, String paramType) {
|
||||
this(paramName, paramType, null);
|
||||
this(paramName, paramType, null, GLOBAL_NAMESPACE);
|
||||
}
|
||||
|
||||
|
||||
public HookSignature(String paramName, String paramType, String doc) {
|
||||
this(paramName, paramType, doc, GLOBAL_NAMESPACE);
|
||||
}
|
||||
|
||||
public HookSignature(String paramName, String paramType, String doc, String namespace) {
|
||||
this.paramName = paramName;
|
||||
this.paramType = paramType;
|
||||
this.doc = doc;
|
||||
this.namespace = namespace != null ? namespace : GLOBAL_NAMESPACE;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return paramName + ": " + paramType;
|
||||
return paramName + ": " + paramType + " [" + namespace + "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
+55
-14
@@ -44,9 +44,15 @@ public class TypeScriptDefinitionParser {
|
||||
|
||||
private static final Pattern GLOBAL_FUNCTION_PATTERN = Pattern.compile(
|
||||
"function\\s+(\\w+)\\s*\\(([^)]*)\\)\\s*:\\s*([^;]+);");
|
||||
|
||||
|
||||
private static final Pattern GLOBAL_TYPE_ALIAS_PATTERN = Pattern.compile(
|
||||
"type\\s+(\\w+)\\s*=\\s*import\\(['\"]([^'\"]+)['\"]\\)\\.([\\w.]+);");
|
||||
|
||||
// Pattern for context-namespaced hooks like: declare namespace INpcEvent { ... }
|
||||
// Matches any "declare namespace <Name> {" where Name starts with I and contains Event,
|
||||
// or any other namespace pattern used for hooks
|
||||
private static final Pattern HOOKS_NAMESPACE_PATTERN = Pattern.compile(
|
||||
"declare\\s+namespace\\s+(I\\w*Event|\\w+)\\s*\\{", Pattern.MULTILINE);
|
||||
|
||||
private final JSTypeRegistry registry;
|
||||
|
||||
@@ -129,21 +135,56 @@ public class TypeScriptDefinitionParser {
|
||||
|
||||
/**
|
||||
* Parse hooks.d.ts to extract function signatures for JS hooks.
|
||||
*
|
||||
* Hooks are organized by their parent event interface, with the interface name
|
||||
* used directly as the namespace. Any mod can register its own event interfaces
|
||||
* and they will be automatically parsed:
|
||||
*
|
||||
* declare namespace INpcEvent {
|
||||
* function interact(event: INpcEvent.InteractEvent): void;
|
||||
* function init(event: INpcEvent.InitEvent): void;
|
||||
* }
|
||||
*
|
||||
* declare namespace IPlayerEvent {
|
||||
* function interact(event: IPlayerEvent.InteractEvent): void;
|
||||
* }
|
||||
*
|
||||
* declare namespace IDBCEvent {
|
||||
* function customHook(event: IDBCEvent.CustomEvent): void;
|
||||
* }
|
||||
*
|
||||
* The namespace name is stored as a string, allowing dynamic registration
|
||||
* without requiring enum modifications.
|
||||
*/
|
||||
private void parseHooksFile(String content) {
|
||||
Matcher m = GLOBAL_FUNCTION_PATTERN.matcher(content);
|
||||
while (m.find()) {
|
||||
String funcName = m.group(1);
|
||||
String params = m.group(2);
|
||||
String returnType = m.group(3).trim();
|
||||
|
||||
// Parse parameter - format is "paramName: TypeName"
|
||||
if (!params.isEmpty()) {
|
||||
String[] parts = params.split(":\\s*", 2);
|
||||
if (parts.length == 2) {
|
||||
String paramName = parts[0].trim();
|
||||
String paramType = parts[1].trim();
|
||||
registry.registerHook(funcName, paramName, paramType);
|
||||
// Parse namespaced hooks (e.g., declare namespace INpcEvent { ... })
|
||||
// The namespace name is used directly - any event interface can register hooks
|
||||
Matcher namespaceMatcher = HOOKS_NAMESPACE_PATTERN.matcher(content);
|
||||
while (namespaceMatcher.find()) {
|
||||
String namespace = namespaceMatcher.group(1); // e.g., "INpcEvent", "IPlayerEvent", "IDBCEvent"
|
||||
|
||||
// Find the body of this namespace
|
||||
int bodyStart = namespaceMatcher.end();
|
||||
int bodyEnd = findMatchingBrace(content, bodyStart - 1);
|
||||
if (bodyEnd > bodyStart) {
|
||||
String namespaceBody = content.substring(bodyStart, bodyEnd);
|
||||
|
||||
// Parse functions within this namespace
|
||||
Matcher funcMatcher = GLOBAL_FUNCTION_PATTERN.matcher(namespaceBody);
|
||||
while (funcMatcher.find()) {
|
||||
String funcName = funcMatcher.group(1);
|
||||
String params = funcMatcher.group(2);
|
||||
|
||||
// Parse parameter - format is "paramName: TypeName"
|
||||
if (!params.isEmpty()) {
|
||||
String[] parts = params.split(":\\s*", 2);
|
||||
if (parts.length == 2) {
|
||||
String paramName = parts[0].trim();
|
||||
String paramType = parts[1].trim();
|
||||
// Register hook with the namespace string directly
|
||||
registry.registerHook(namespace, funcName, paramName, paramType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,6 +166,106 @@ public class TypeResolver {
|
||||
return getJSTypeRegistry().getHookSignatures(functionName);
|
||||
}
|
||||
|
||||
// ==================== CONTEXT-AWARE HOOK METHODS ====================
|
||||
|
||||
/**
|
||||
* Check if a function name is a known JS hook in a specific script context.
|
||||
* Falls back to GLOBAL context if not found in the specified context.
|
||||
*
|
||||
* @param namespace The event interface namespace (e.g., "INpcEvent", "IPlayerEvent")
|
||||
* @param functionName The function name to check
|
||||
* @return true if it's a known hook in this namespace
|
||||
*/
|
||||
public boolean isJSHook(String namespace, String functionName) {
|
||||
return getJSTypeRegistry().isHook(namespace, functionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parameter type for a JS hook function in a specific namespace.
|
||||
* Falls back to "Global" namespace if not found in the specified namespace.
|
||||
*
|
||||
* @param namespace The event interface namespace (e.g., "INpcEvent", "IPlayerEvent")
|
||||
* @param functionName The hook function name
|
||||
* @return The parameter type, or null if not a hook
|
||||
*/
|
||||
public String getJSHookParameterType(String namespace, String functionName) {
|
||||
return getJSTypeRegistry().getHookParameterType(namespace, functionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get hook signatures for a JS function in a specific namespace.
|
||||
* Falls back to "Global" namespace if not found in the specified namespace.
|
||||
*
|
||||
* @param namespace The event interface namespace (e.g., "INpcEvent", "IPlayerEvent")
|
||||
* @param functionName The hook function name
|
||||
* @return List of hook signatures
|
||||
*/
|
||||
public List<JSTypeRegistry.HookSignature> getJSHookSignatures(String namespace, String functionName) {
|
||||
return getJSTypeRegistry().getHookSignatures(namespace, functionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all hook names available in a specific namespace.
|
||||
*
|
||||
* @param namespace The event interface namespace
|
||||
* @return Set of hook function names
|
||||
*/
|
||||
public Set<String> getJSHookNames(String namespace) {
|
||||
return getJSTypeRegistry().getHookNames(namespace);
|
||||
}
|
||||
|
||||
// ==================== MULTI-NAMESPACE HOOK METHODS ====================
|
||||
|
||||
/**
|
||||
* Check if a function name is a known JS hook in any of the provided namespaces.
|
||||
* Checks namespaces in order, returning true on first match.
|
||||
* Falls back to GLOBAL namespace if not found in any specified namespace.
|
||||
*
|
||||
* @param namespaces The list of event interface namespaces to search (e.g., ["IPlayerEvent", "IAnimationEvent"])
|
||||
* @param functionName The function name to check
|
||||
* @return true if it's a known hook in any of the namespaces
|
||||
*/
|
||||
public boolean isJSHook(List<String> namespaces, String functionName) {
|
||||
return getJSTypeRegistry().isHook(namespaces, functionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parameter type for a JS hook function, searching multiple namespaces.
|
||||
* Checks namespaces in order, returning the first match found.
|
||||
* Falls back to "Global" namespace if not found in any specified namespace.
|
||||
*
|
||||
* @param namespaces The list of event interface namespaces to search
|
||||
* @param functionName The hook function name
|
||||
* @return The parameter type, or null if not a hook
|
||||
*/
|
||||
public String getJSHookParameterType(List<String> namespaces, String functionName) {
|
||||
return getJSTypeRegistry().getHookParameterType(namespaces, functionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get hook signatures for a JS function, searching multiple namespaces.
|
||||
* Checks namespaces in order, returning signatures from the first matching namespace.
|
||||
* Falls back to "Global" namespace if not found in any specified namespace.
|
||||
*
|
||||
* @param namespaces The list of event interface namespaces to search
|
||||
* @param functionName The hook function name
|
||||
* @return List of hook signatures
|
||||
*/
|
||||
public List<JSTypeRegistry.HookSignature> getJSHookSignatures(List<String> namespaces, String functionName) {
|
||||
return getJSTypeRegistry().getHookSignatures(namespaces, functionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all hook names available in any of the provided namespaces.
|
||||
* Combines hook names from all specified namespaces.
|
||||
*
|
||||
* @param namespaces The list of event interface namespaces
|
||||
* @return Combined set of hook function names from all namespaces
|
||||
*/
|
||||
public Set<String> getJSHookNames(List<String> namespaces) {
|
||||
return getJSTypeRegistry().getHookNames(namespaces);
|
||||
}
|
||||
|
||||
// ==================== CACHE MANAGEMENT ====================
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
package noppes.npcs.constants;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Represents a script context that maps to one or more event interface namespaces.
|
||||
*
|
||||
* A script context can have multiple namespaces because script editors often
|
||||
* support hooks from multiple event types. For example, Player scripts support:
|
||||
* - IPlayerEvent (core player hooks)
|
||||
* - IAnimationEvent (animation hooks)
|
||||
* - IPartyEvent (party hooks)
|
||||
* - ICustomGuiEvent (custom GUI hooks)
|
||||
* - etc.
|
||||
*
|
||||
* This is a registerable system - any mod can register their own contexts:
|
||||
*
|
||||
* // Register a new context with multiple namespaces:
|
||||
* ScriptContext.register("DBC", "IDBCEvent", "IDBCPlayerEvent", "IDBCFormEvent");
|
||||
*
|
||||
* // Or add namespaces to an existing context:
|
||||
* ScriptContext.PLAYER.addNamespace("IDBCPlayerEvent");
|
||||
*/
|
||||
public class ScriptContext {
|
||||
|
||||
// Registry of all script contexts
|
||||
private static final Map<String, ScriptContext> REGISTRY = new ConcurrentHashMap<>();
|
||||
|
||||
// ==================== BUILT-IN CONTEXTS ====================
|
||||
|
||||
public static final ScriptContext NPC = register("NPC",
|
||||
"INpcEvent",
|
||||
"IProjectileEvent"
|
||||
);
|
||||
|
||||
public static final ScriptContext PLAYER = register("PLAYER",
|
||||
"IPlayerEvent",
|
||||
"IAnimationEvent",
|
||||
"IPartyEvent",
|
||||
"IDialogEvent",
|
||||
"IQuestEvent",
|
||||
"IFactionEvent",
|
||||
"ICustomGuiEvent",
|
||||
"IEffectEvent"
|
||||
);
|
||||
|
||||
public static final ScriptContext BLOCK = register("BLOCK",
|
||||
"IBlockEvent"
|
||||
);
|
||||
|
||||
public static final ScriptContext ITEM = register("ITEM",
|
||||
"IItemEvent"
|
||||
);
|
||||
|
||||
public static final ScriptContext FORGE = register("FORGE",
|
||||
"IForgeEvent"
|
||||
);
|
||||
|
||||
public static final ScriptContext GLOBAL = register("GLOBAL",
|
||||
"Global"
|
||||
);
|
||||
|
||||
// ==================== INSTANCE FIELDS ====================
|
||||
|
||||
/** Unique identifier for this context (e.g., "NPC", "PLAYER", "DBC") */
|
||||
public final String id;
|
||||
|
||||
/** The event interface namespaces this context supports */
|
||||
private final List<String> namespaces;
|
||||
|
||||
// ==================== CONSTRUCTOR ====================
|
||||
|
||||
private ScriptContext(String id, String... namespaces) {
|
||||
this.id = id;
|
||||
this.namespaces = new ArrayList<>(Arrays.asList(namespaces));
|
||||
}
|
||||
|
||||
// ==================== NAMESPACE ACCESS ====================
|
||||
|
||||
/**
|
||||
* Get all namespaces this context supports.
|
||||
*
|
||||
* @return Unmodifiable list of namespace strings
|
||||
*/
|
||||
public List<String> getNamespaces() {
|
||||
return Collections.unmodifiableList(namespaces);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this context includes a specific namespace.
|
||||
*
|
||||
* @param namespace The namespace to check (e.g., "IPlayerEvent")
|
||||
* @return true if this context supports hooks from that namespace
|
||||
*/
|
||||
public boolean hasNamespace(String namespace) {
|
||||
return namespaces.contains(namespace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a namespace to this context.
|
||||
* Mods can use this to extend built-in contexts with their own event types.
|
||||
*
|
||||
* @param namespace The namespace to add (e.g., "IDBCPlayerEvent")
|
||||
*/
|
||||
public void addNamespace(String namespace) {
|
||||
if (!namespaces.contains(namespace)) {
|
||||
namespaces.add(namespace);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the primary namespace (first one registered).
|
||||
* This is mainly for backward compatibility.
|
||||
*
|
||||
* @return The first namespace, or "Global" if none
|
||||
*/
|
||||
public String getPrimaryNamespace() {
|
||||
return namespaces.isEmpty() ? "Global" : namespaces.get(0);
|
||||
}
|
||||
|
||||
// ==================== REGISTRATION API ====================
|
||||
|
||||
/**
|
||||
* Register a new script context with one or more namespaces.
|
||||
*
|
||||
* @param id Unique identifier (e.g., "DBC", "CUSTOM")
|
||||
* @param namespaces The event interface names (e.g., "IDBCEvent", "IDBCPlayerEvent")
|
||||
* @return The registered ScriptContext
|
||||
*/
|
||||
public static ScriptContext register(String id, String... namespaces) {
|
||||
ScriptContext context = new ScriptContext(id, namespaces);
|
||||
REGISTRY.put(id, context);
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a script context by its ID.
|
||||
*
|
||||
* @param id The context ID (e.g., "NPC", "PLAYER", "DBC")
|
||||
* @return The ScriptContext, or GLOBAL if not found
|
||||
*/
|
||||
public static ScriptContext byId(String id) {
|
||||
return REGISTRY.getOrDefault(id, GLOBAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a script context that contains a specific namespace.
|
||||
*
|
||||
* @param namespace The event interface namespace (e.g., "INpcEvent")
|
||||
* @return The first ScriptContext that contains this namespace, or GLOBAL if not found
|
||||
*/
|
||||
public static ScriptContext byNamespace(String namespace) {
|
||||
if (namespace == null) return GLOBAL;
|
||||
for (ScriptContext ctx : REGISTRY.values()) {
|
||||
if (ctx.hasNamespace(namespace)) {
|
||||
return ctx;
|
||||
}
|
||||
}
|
||||
return GLOBAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registered script contexts.
|
||||
*
|
||||
* @return Unmodifiable collection of all registered contexts
|
||||
*/
|
||||
public static Collection<ScriptContext> values() {
|
||||
return Collections.unmodifiableCollection(REGISTRY.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a context with the given ID exists.
|
||||
*
|
||||
* @param id The context ID to check
|
||||
* @return true if registered
|
||||
*/
|
||||
public static boolean exists(String id) {
|
||||
return REGISTRY.containsKey(id);
|
||||
}
|
||||
|
||||
// ==================== OBJECT METHODS ====================
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return id + " -> " + namespaces;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (!(obj instanceof ScriptContext)) return false;
|
||||
ScriptContext other = (ScriptContext) obj;
|
||||
return id.equals(other.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id.hashCode();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user