ScriptHandler refactor:

- ScriptHandler now holds a list of containers

- Delegated single container handling to SingleScriptHandler

IScriptHandler refactor:

- Moved ScriptContext/Hook logic to it

- More default methods + hooked them to GuIScriptInterface
This commit is contained in:
bigguy345 committed 2026-01-18 17:50:04 +02:00
1 parent 44f71ddb81
commit eecb5b0b3b
4 files changed
+294 -51

No files matched your search

@@ -27,6 +27,7 @@ import noppes.npcs.constants.ScriptContext;
import noppes.npcs.controllers.data.ForgeDataScript;
import noppes.npcs.controllers.data.IScriptHandler;
import noppes.npcs.controllers.data.IScriptUnit;
import noppes.npcs.controllers.data.IScriptHandlerPacket;
import noppes.npcs.scripted.item.ScriptCustomItem;
import org.lwjgl.opengl.Display;
@@ -49,7 +50,6 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb
protected Map<Integer, GuiScriptTextArea> textAreas = new HashMap<>();
protected GuiScreen parent;
public boolean singleContainer = false;
/** If true, uses scroll-based tab selection instead of top buttons */
protected boolean useScrollTabs = false;
@@ -97,6 +97,12 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb
gui.parent = parent;
gui.singleContainer = true;
gui.displayGuiScreen(gui);
// Initialize hooks from handler
gui.hookList = new ArrayList<>(handler.getHooks());
Minecraft.getMinecraft().displayGuiScreen(gui);
return gui;
}
@@ -113,6 +119,9 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb
this.guiTop += 10;
int yoffset = (int) ((double) this.ySize * 0.02D);
if (hookList.isEmpty())
hookList = new ArrayList<>(handler.getHooks());
// ==================== TAB BUTTONS ====================
// Skip standard tab creation for GUIs that use custom toggle/scroll tabs
if (!useSettingsToggle) {
@@ -123,7 +132,8 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb
int topXoffset = 0;
int topYoffset = 0;
if(!singleContainer) {
boolean isSingle = handler.isSingleContainer();
if (!isSingle) {
for (int ta = 0; ta < this.handler.getScripts().size(); ++ta) {
if (ta % 20 == 0 && ta > 0) {
topYoffset -= 20;
@@ -137,7 +147,7 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb
}
}
if (singleContainer && getFirst() != null)
if (isSingle && handler.getSingleScript() != null)
this.addTopButton(
top = new GuiMenuTopButton(1, top.xPosition + top.width + topXoffset, top.yPosition + topYoffset,
"Script"));
@@ -233,10 +243,7 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb
// Set the scripting language for proper syntax highlighting
// Use the container's language if available, otherwise fall back to handler's language
String language = (container != null) ? container.getLanguage() : this.handler.getLanguage();
activeArea.setLanguage(language);
// Set the script context for context-aware hook autocomplete
activeArea.setScriptContext(getScriptContext());
activeArea.setLanguage(language);
// Set the script context for context-aware hook autocomplete
activeArea.setScriptContext(getScriptContext());
@@ -320,6 +327,16 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb
return this;
}
/**
* Get the script context for this GUI.
* Override in subclasses to return a different context.
*
* @return The script context (default: GLOBAL, or handler's context if available)
*/
protected ScriptContext getScriptContext() {
return handler.getContext();
}
// ==================== RENDERING ====================
@Override
@@ -418,10 +435,7 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb
this.openLink("http://www.minecraftforge.net/forum/index.php/board,122.0.html");
}
if (i == 10) {
if (singleContainer)
setHandlerUnit(null);
else
this.handler.getScripts().remove(this.activeTab - 1);
handler.removeScriptUnit(this.activeTab - 1);
this.activeTab = 0;
}
if (i == 101) {
@@ -482,11 +496,6 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb
return super.getTextField(id);
}
public IScriptUnit getFirst() {
if (this.handler.getScripts().isEmpty())
return null;
return this.handler.getScripts().get(0);
}
@Override
protected void actionPerformed(GuiButton guibutton) {
if (guibutton.id >= 0 && guibutton.id < scriptLimit) {
@@ -501,14 +510,15 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb
}
if (guibutton.id == scriptLimit) {
if (singleContainer) {
if (getFirst() == null)
setHandlerUnit(new ScriptContainer(handler));
else
if (handler.isSingleContainer()) {
if (handler.getSingleScript() == null) {
handler.addScriptUnit(new ScriptContainer(handler));
} else {
this.setScript();
}
this.activeTab = 1;
} else {
this.handler.getScripts().add(new ScriptContainer(this.handler));
handler.addScriptUnit(new ScriptContainer(this.handler));
this.activeTab = this.handler.getScripts().size();
}
this.initGui();
@@ -567,10 +577,7 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb
IScriptUnit container = getCurrentContainer();
if (container == null) {
container = new ScriptContainer(this.handler);
if (singleContainer)
setHandlerUnit(container);
else
this.handler.getScripts().add(container);
handler.addScriptUnit(container);
}
String language = container != null ? container.getLanguage() : this.handler.getLanguage();
@@ -600,12 +607,7 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb
}
}
if (singleContainer) {
setHandlerUnit(newUnit);
} else {
// Replace the script unit in the list
handler.getScripts().set(idx, newUnit);
}
handler.replaceScriptUnit(idx, newUnit);
// Clear the cached text area so it recreates with new language
textAreas.remove(idx);
@@ -621,10 +623,7 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb
if (container == null) {
container = new ScriptContainer(this.handler);
if (singleContainer)
setHandlerUnit(container);
else
this.handler.getScripts().add(container);
handler.addScriptUnit(container);
}
if (container != null) {
@@ -3,7 +3,10 @@ package noppes.npcs.controllers.data;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.eventhandler.Event;
import noppes.npcs.constants.EnumScriptType;
import noppes.npcs.constants.ScriptContext;
import noppes.npcs.controllers.ScriptHookController;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
@@ -74,4 +77,89 @@ public interface IScriptHandler {
default boolean supportsJanino() {
return createJaninoScriptUnit() != null;
}
/**
* Get the script context for autocomplete and type resolution.
*
* @return The script context (default: GLOBAL)
*/
default ScriptContext getContext() {
return ScriptContext.GLOBAL;
}
/**
* Get the hook context identifier for this handler.
* Used by ScriptHookController to look up available hooks.
*
* @return The context string (e.g., IScriptHookHandler.CONTEXT_EFFECT), or empty if none
*/
default String getHookContext() {
return "";
}
/**
* Get the list of available hooks for this handler.
* Uses getHookContext() to look up hooks from ScriptHookController.
*
* @return List of hook names, or empty list if no context
*/
default List<String> getHooks() {
String context = getHookContext();
if (context == null || context.isEmpty())
return Collections.emptyList();
return ScriptHookController.Instance.getAllHooks(context);
}
/**
* Check if this handler manages only a single script container.
* Single-container handlers use simplified UI (no numbered tabs).
*
* @return true if this handler uses a single container
*/
default boolean isSingleContainer() {
return false;
}
/**
* Get the single script unit for single-container handlers.
* Convenience method that returns the first script or null.
*
* @return The single script unit, or null if none exists
*/
default IScriptUnit getSingleScript() {
List<IScriptUnit> scripts = getScripts();
return (scripts != null && !scripts.isEmpty()) ? scripts.get(0) : null;
}
/**
* Add a script unit to this handler.
* For single-container handlers, replaces the current unit.
*/
default void addScriptUnit(IScriptUnit unit) {
if (unit != null)
getScripts().add(unit);
}
/**
* Replace a script unit at the given index.
* For single-container handlers, replaces the current unit.
*/
default void replaceScriptUnit(int index, IScriptUnit unit) {
List<IScriptUnit> scripts = getScripts();
if (index < 0 || index >= scripts.size())
scripts.add(unit);
else
scripts.set(index, unit);
}
/**
* Remove a script unit at the given index.
* For single-container handlers, clears the unit.
*/
default void removeScriptUnit(int index) {
List<IScriptUnit> scripts = getScripts();
if (index >= 0 && index < scripts.size())
scripts.remove(index);
}
}
@@ -5,24 +5,36 @@ import noppes.npcs.config.ConfigScript;
import noppes.npcs.controllers.ScriptController;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public abstract class ScriptHandler implements IScriptHandler {
public IScriptUnit container;
public List<IScriptUnit> scripts = new ArrayList<>();
protected String scriptLanguage = "ECMAScript";
protected boolean enabled = false;
protected long lastInited = -1;
protected boolean canRunScripts() {
return enabled && ScriptController.HasStart && ConfigScript.ScriptingEnabled && container != null;
return enabled && ScriptController.HasStart && ConfigScript.ScriptingEnabled && scripts != null && !scripts.isEmpty();
}
@Override
public void callScript(String hookName, Event event) {
if (!canRunScripts()) {
if (!canRunScripts())
return;
if (ScriptController.Instance.lastLoaded > lastInited) {
lastInited = ScriptController.Instance.lastLoaded;
for (IScriptUnit script : scripts) {
if (script != null)
script.setErrored(false);
}
}
for (IScriptUnit script : scripts) {
if (script == null || script.hasErrored() || !script.hasCode())
continue;
script.run(hookName, event);
}
container.run(hookName, event);
}
@Override
@@ -45,20 +57,11 @@ public abstract class ScriptHandler implements IScriptHandler {
this.scriptLanguage = language;
}
@Override
public void setScripts(List<IScriptUnit> list) {
if (list == null || list.isEmpty()) {
container = null;
return;
}
container = list.get(0);
this.scripts = list;
}
@Override
public List<IScriptUnit> getScripts() {
if (container == null) {
return new ArrayList<>();
}
return Collections.singletonList(container);
return this.scripts;
}
}
@@ -0,0 +1,153 @@
package noppes.npcs.controllers.data;
import cpw.mods.fml.common.eventhandler.Event;
import io.netty.buffer.ByteBuf;
import kamkeel.npcs.util.ByteBufUtils;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraftforge.common.util.Constants;
import noppes.npcs.config.ConfigScript;
import noppes.npcs.controllers.ScriptController;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Base class for script handlers that manage a single script container.
* Provides shared NBT/ByteBuf serialization and packet handling.
*
* Subclasses should:
* - Store their parent object (e.g., CustomEffect, LinkedItem)
* - Override getHookContext() to return the appropriate context string
* - Override getContext() if needed (default is GLOBAL)
* - Override requestData() and sendSavePacket() for packet communication
*/
public abstract class SingleScriptHandler extends ScriptHandler {
public IScriptUnit container;
@Override
public boolean isSingleContainer() {
return true;
}
protected boolean canRunScripts() {
return enabled && ScriptController.HasStart && ConfigScript.ScriptingEnabled && container != null;
}
@Override
public void callScript(String hookName, Event event) {
if (!canRunScripts()) {
return;
}
container.run(hookName, event);
}
@Override
public void setScripts(List<IScriptUnit> list) {
if (list == null || list.isEmpty()) {
container = null;
this.scripts = new ArrayList<>();
} else {
container = list.get(0);
this.scripts = new ArrayList<>(1);
this.scripts.add(container);
}
}
@Override
public List<IScriptUnit> getScripts() {
if(container == null)
return new ArrayList<>();
return Collections.singletonList(container);
}
public void addScriptUnit(IScriptUnit unit) {
this.container = unit;
this.scripts.clear();
if (unit != null)
this.scripts.add(unit);
}
public void replaceScriptUnit(int index, IScriptUnit unit) {
this.container = unit;
this.scripts.clear();
if (unit != null)
this.scripts.add(unit);
}
public void removeScriptUnit(int index) {
this.container = null;
this.scripts.clear();
}
/**
* Write script data to NBT.
* Uses standard keys: ScriptLanguage, ScriptEnabled, ScriptContent
*/
public NBTTagCompound writeToNBT(NBTTagCompound compound) {
compound.setString("ScriptLanguage", scriptLanguage);
compound.setBoolean("ScriptEnabled", enabled);
if (container != null) {
compound.setTag("ScriptContent", container.writeToNBT(new NBTTagCompound()));
}
return compound;
}
/**
* Read script data from NBT.
* Uses standard keys: ScriptLanguage, ScriptEnabled, ScriptContent
*/
public void readFromNBT(NBTTagCompound compound) {
scriptLanguage = compound.getString("ScriptLanguage");
enabled = compound.getBoolean("ScriptEnabled");
if (compound.hasKey("ScriptContent", Constants.NBT.TAG_COMPOUND)) {
container = IScriptUnit.createFromNBT(compound.getCompoundTag("ScriptContent"), this);
}
}
/**
* Save script data from a ByteBuf packet.
* Handles the tab/totalScripts protocol used by script packets.
*
* Protocol:
* - tab == 0: Script content NBT follows
* - tab != 0 (typically -1): Script metadata (language, enabled) follows
*/
public void saveScript(ByteBuf buffer) throws IOException {
int tab = buffer.readInt();
int totalScripts = buffer.readInt();
if (totalScripts == 0) {
this.container = null;
}
if (tab == 0) {
// Script content
NBTTagCompound tabCompound = ByteBufUtils.readNBT(buffer);
this.container = IScriptUnit.createFromNBT(tabCompound, this);
} else {
// Metadata (language, enabled)
NBTTagCompound compound = ByteBufUtils.readNBT(buffer);
this.setLanguage(compound.getString("ScriptLanguage"));
normalizeLanguage();
this.setEnabled(compound.getBoolean("ScriptEnabled"));
}
}
/**
* Normalize the language to a valid one if the current language is not available.
*/
protected void normalizeLanguage() {
if (!ScriptController.Instance.languages.containsKey(this.getLanguage())) {
if (!ScriptController.Instance.languages.isEmpty()) {
this.setLanguage((String) ScriptController.Instance.languages.keySet().toArray()[0]);
} else {
this.setLanguage("ECMAScript");
}
}
}
}