mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-13 06:37:34 +00:00
Merge branch 'dev' into syntax-highlighter-unified
This commit is contained in:
commit
9e68943b7e
14 files changed
+229
-45
No files matched your search
@@ -220,6 +220,7 @@ public class AbilityController implements IAbilityHandler {
|
||||
public void load() {
|
||||
customAbilities.clear();
|
||||
customAbilitiesById.clear();
|
||||
abilityScriptHandlers.clear();
|
||||
|
||||
File dir = getDir();
|
||||
File[] files = dir.exists() ? dir.listFiles() : null;
|
||||
@@ -470,6 +471,7 @@ public class AbilityController implements IAbilityHandler {
|
||||
private void loadChainedAbilities() {
|
||||
chainedAbilities.clear();
|
||||
chainedAbilitiesById.clear();
|
||||
chainedAbilityScriptHandlers.clear();
|
||||
|
||||
File dir = getChainedDir();
|
||||
File[] files = dir.exists() ? dir.listFiles() : null;
|
||||
|
||||
@@ -45,6 +45,7 @@ import noppes.npcs.client.gui.advanced.SubGuiAbilityConfig;
|
||||
import noppes.npcs.client.gui.builder.FieldDef;
|
||||
import noppes.npcs.client.gui.util.IAbilityConfigCallback;
|
||||
import noppes.npcs.controllers.AnimationController;
|
||||
import noppes.npcs.controllers.ScriptContainer;
|
||||
import noppes.npcs.controllers.data.Animation;
|
||||
import noppes.npcs.controllers.data.Frame;
|
||||
import noppes.npcs.controllers.data.AbilityScript;
|
||||
@@ -237,6 +238,9 @@ public abstract class Ability implements IAbility, IAbilityAction {
|
||||
// Burst execution state
|
||||
protected transient int burstIndex = 0;
|
||||
protected transient List<Entity> burstEntities = new ArrayList<>();
|
||||
|
||||
// Per-instance script (isolated per execution, not saved to NBT)
|
||||
protected transient AbilityScript instanceScript;
|
||||
protected transient float damageMultiplier = 1.0f; // Ability-internal damage scaling (e.g., Slam height)
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
@@ -1335,6 +1339,7 @@ public abstract class Ability implements IAbility, IAbilityAction {
|
||||
telegraphInstances.clear();
|
||||
previewMode = false;
|
||||
previewEntityHandler = null;
|
||||
instanceScript = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1647,10 +1652,14 @@ public abstract class Ability implements IAbility, IAbilityAction {
|
||||
}
|
||||
|
||||
// Script handler data (matching CustomEffect.readFromNBT pattern)
|
||||
// Only create a new handler if one doesn't already exist in the global map,
|
||||
// to avoid clobbering a live handler's script state during deepCopy/resolve.
|
||||
if (nbt.hasKey("ScriptData", 10)) {
|
||||
AbilityScript handler = new AbilityScript(this.id);
|
||||
handler.readFromNBT(nbt.getCompoundTag("ScriptData"));
|
||||
setScriptHandler(handler);
|
||||
if (getScriptHandler() == null) {
|
||||
AbilityScript handler = new AbilityScript(this.id);
|
||||
handler.readFromNBT(nbt.getCompoundTag("ScriptData"));
|
||||
setScriptHandler(handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1753,7 +1762,7 @@ public abstract class Ability implements IAbility, IAbilityAction {
|
||||
}
|
||||
|
||||
public Ability deepCopy() {
|
||||
return AbilityController.Instance.fromNBT(this.writeNBT(true));
|
||||
return AbilityController.Instance.fromNBT(this.writeNBT(false));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2734,4 +2743,25 @@ public abstract class Ability implements IAbility, IAbilityAction {
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create a per-instance script handler for this ability execution.
|
||||
* Shares the template's engine but creates isolated Bindings for variable state.
|
||||
* Returns null if no script is configured on the template.
|
||||
*/
|
||||
public AbilityScript getOrCreateInstanceScript() {
|
||||
if (instanceScript != null) return instanceScript;
|
||||
|
||||
AbilityScript template = getScriptHandler();
|
||||
if (template == null || !template.getEnabled() || template.container == null) return null;
|
||||
|
||||
instanceScript = new AbilityScript(this.id);
|
||||
instanceScript.setLanguage(template.getLanguage());
|
||||
instanceScript.setEnabled(true);
|
||||
|
||||
ScriptContainer clone = ((ScriptContainer) template.container).createInstanceScope(instanceScript);
|
||||
instanceScript.addScriptUnit(clone);
|
||||
|
||||
return instanceScript;
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
import noppes.npcs.api.ability.IChainedAbility;
|
||||
import noppes.npcs.controllers.ScriptContainer;
|
||||
import kamkeel.npcs.util.FileNameHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -75,6 +76,9 @@ public class ChainedAbility implements IChainedAbility, IAbilityAction {
|
||||
*/
|
||||
private NBTTagCompound customData = new NBTTagCompound();
|
||||
|
||||
// Per-instance script (isolated per execution, not saved to NBT)
|
||||
private transient ChainedAbilityScript instanceScript;
|
||||
|
||||
public ChainedAbility() {
|
||||
}
|
||||
|
||||
@@ -371,7 +375,7 @@ public class ChainedAbility implements IChainedAbility, IAbilityAction {
|
||||
*/
|
||||
public ChainedAbility deepCopy() {
|
||||
ChainedAbility copy = new ChainedAbility();
|
||||
copy.readNBT(this.writeNBT(true));
|
||||
copy.readNBT(this.writeNBT(false));
|
||||
return copy;
|
||||
}
|
||||
|
||||
@@ -468,9 +472,11 @@ public class ChainedAbility implements IChainedAbility, IAbilityAction {
|
||||
|
||||
// Script handler data (matching CustomEffect.readFromNBT pattern)
|
||||
if (nbt.hasKey("ScriptData", 10)) {
|
||||
ChainedAbilityScript handler = new ChainedAbilityScript(this.id);
|
||||
handler.readFromNBT(nbt.getCompoundTag("ScriptData"));
|
||||
setScriptHandler(handler);
|
||||
if (getScriptHandler() == null) {
|
||||
ChainedAbilityScript handler = new ChainedAbilityScript(this.id);
|
||||
handler.readFromNBT(nbt.getCompoundTag("ScriptData"));
|
||||
setScriptHandler(handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -494,4 +500,32 @@ public class ChainedAbility implements IChainedAbility, IAbilityAction {
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create a per-instance script handler for this chain execution.
|
||||
* Shares the template's engine but creates isolated Bindings for variable state.
|
||||
* Returns null if no script is configured on the template.
|
||||
*/
|
||||
public ChainedAbilityScript getOrCreateInstanceScript() {
|
||||
if (instanceScript != null) return instanceScript;
|
||||
|
||||
ChainedAbilityScript template = getScriptHandler();
|
||||
if (template == null || !template.getEnabled() || template.container == null) return null;
|
||||
|
||||
instanceScript = new ChainedAbilityScript(this.id);
|
||||
instanceScript.setLanguage(template.getLanguage());
|
||||
instanceScript.setEnabled(true);
|
||||
|
||||
ScriptContainer clone = ((ScriptContainer) template.container).createInstanceScope(instanceScript);
|
||||
instanceScript.addScriptUnit(clone);
|
||||
|
||||
return instanceScript;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the per-instance script handler (call on chain completion/reset).
|
||||
*/
|
||||
public void clearInstanceScript() {
|
||||
this.instanceScript = null;
|
||||
}
|
||||
}
|
||||
@@ -88,9 +88,17 @@ public final class AbilityScriptPacket extends AbstractPacket {
|
||||
|
||||
Ability ability = AbilityController.Instance.resolveAbility(id);
|
||||
if (ability == null) {
|
||||
// Ability not yet saved — send default empty script data so the GUI is usable
|
||||
// Ability not yet saved — store handler in global map so scripts persist
|
||||
// until the ability is saved via CustomAbilitySavePacket
|
||||
AbilityScript data = AbilityController.Instance.abilityScriptHandlers.get(id);
|
||||
if (data == null) {
|
||||
data = new AbilityScript(id);
|
||||
AbilityController.Instance.abilityScriptHandlers.put(id, data);
|
||||
}
|
||||
if (requestedAction == Action.GET) {
|
||||
PacketUtil.getScripts((IScriptHandler) new AbilityScript(id), (EntityPlayerMP) player);
|
||||
PacketUtil.getScripts((IScriptHandler) data, (EntityPlayerMP) player);
|
||||
} else {
|
||||
data.saveScript(in, player);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
+10
-2
@@ -88,9 +88,17 @@ public final class ChainedAbilityScriptPacket extends AbstractPacket {
|
||||
|
||||
ChainedAbility chain = AbilityController.Instance.resolveChainedAbility(id);
|
||||
if (chain == null) {
|
||||
// Chain not yet saved — send default empty script data so the GUI is usable
|
||||
// Chain not yet saved — store handler in global map so scripts persist
|
||||
// until the chain is saved via ChainedAbilitySavePacket
|
||||
ChainedAbilityScript data = AbilityController.Instance.chainedAbilityScriptHandlers.get(id);
|
||||
if (data == null) {
|
||||
data = new ChainedAbilityScript(id);
|
||||
AbilityController.Instance.chainedAbilityScriptHandlers.put(id, data);
|
||||
}
|
||||
if (requestedAction == Action.GET) {
|
||||
PacketUtil.getScripts((IScriptHandler) new ChainedAbilityScript(id), (EntityPlayerMP) player);
|
||||
PacketUtil.getScripts((IScriptHandler) data, (EntityPlayerMP) player);
|
||||
} else {
|
||||
data.saveScript(in, player);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1079,6 +1079,7 @@ public abstract class AbstractDataAbilities {
|
||||
rollChainCooldown(currentChain);
|
||||
interruptCooldownRolled = true;
|
||||
fireChainInterruptEvent(currentChain, chainEntryIndex, getTarget(), source, damage);
|
||||
currentChain.clearInstanceScript();
|
||||
currentChain = null;
|
||||
chainEntryIndex = -1;
|
||||
chainDelayRemaining = -1;
|
||||
@@ -1138,6 +1139,7 @@ public abstract class AbstractDataAbilities {
|
||||
// Roll cooldown
|
||||
if (currentChain != null) {
|
||||
rollChainCooldown(currentChain);
|
||||
currentChain.clearInstanceScript();
|
||||
currentChain = null;
|
||||
chainEntryIndex = -1;
|
||||
chainDelayRemaining = -1;
|
||||
@@ -1336,6 +1338,7 @@ public abstract class AbstractDataAbilities {
|
||||
if (currentChain != null) {
|
||||
rollChainCooldown(currentChain);
|
||||
fireChainCompleteEvent(currentChain, chainEntryIndex, getTarget());
|
||||
currentChain.clearInstanceScript();
|
||||
}
|
||||
currentChain = null;
|
||||
chainEntryIndex = -1;
|
||||
|
||||
@@ -299,6 +299,7 @@ public class DataAbilities extends AbstractDataAbilities {
|
||||
if (positionLocked) releaseLockedPosition();
|
||||
}
|
||||
// Clear chain and concurrent state
|
||||
if (currentChain != null) currentChain.clearInstanceScript();
|
||||
currentChain = null;
|
||||
chainEntryIndex = -1;
|
||||
chainDelayRemaining = -1;
|
||||
@@ -588,6 +589,7 @@ public class DataAbilities extends AbstractDataAbilities {
|
||||
lastTarget = null;
|
||||
}
|
||||
// Also clear chain state
|
||||
if (currentChain != null) currentChain.clearInstanceScript();
|
||||
currentChain = null;
|
||||
chainEntryIndex = -1;
|
||||
chainDelayRemaining = -1;
|
||||
|
||||
@@ -887,7 +887,8 @@ public class EventHooks {
|
||||
IAbilityEvent abilityEvent = (IAbilityEvent) event;
|
||||
|
||||
// 1. Internal ability script (fires for ALL — both NPC and Player)
|
||||
AbilityScript abScript = ability.getScriptHandler();
|
||||
// Use per-instance script so each execution has isolated variable state
|
||||
AbilityScript abScript = ability.getOrCreateInstanceScript();
|
||||
if (abScript != null) {
|
||||
abScript.callScript(abilityEvent.getHookName(), event);
|
||||
}
|
||||
@@ -916,7 +917,8 @@ public class EventHooks {
|
||||
IChainEvent chainEvent = (IChainEvent) event;
|
||||
|
||||
// 1. Internal chain script (fires first)
|
||||
ChainedAbilityScript chainScript = chain.getScriptHandler();
|
||||
// Use per-instance script so each execution has isolated variable state
|
||||
ChainedAbilityScript chainScript = chain.getOrCreateInstanceScript();
|
||||
if (chainScript != null) {
|
||||
chainScript.callScript(chainEvent.getHookName(), event);
|
||||
}
|
||||
|
||||
@@ -794,11 +794,10 @@ public class NoppesUtilServer {
|
||||
HashMap<String, Integer> map = new HashMap<String, Integer>();
|
||||
AbilityController controller = AbilityController.Instance;
|
||||
if (controller != null) {
|
||||
int index = 0;
|
||||
for (String name : controller.getAbilityNames()) {
|
||||
Ability ability = controller.getAbility(name);
|
||||
if (ability != null) {
|
||||
map.put(ability.getName(), index++);
|
||||
map.put(ability.getName(), ability.getAllowedBy().ordinal());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -809,11 +808,10 @@ public class NoppesUtilServer {
|
||||
HashMap<String, Integer> map = new HashMap<String, Integer>();
|
||||
AbilityController controller = AbilityController.Instance;
|
||||
if (controller != null) {
|
||||
int index = 0;
|
||||
for (String name : controller.getCustomAbilityNames()) {
|
||||
Ability ability = controller.getCustomAbility(name);
|
||||
if (ability == null || !ability.getAllowedBy().allowsNpc()) continue;
|
||||
map.put(name, index++);
|
||||
if (ability == null) continue;
|
||||
map.put(name, ability.getAllowedBy().ordinal());
|
||||
}
|
||||
}
|
||||
sendScrollData(player, map, EnumScrollData.CUSTOM_ABILITIES);
|
||||
|
||||
@@ -2,6 +2,7 @@ package noppes.npcs.client.gui.advanced;
|
||||
|
||||
import kamkeel.npcs.controllers.AbilityController;
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.UserType;
|
||||
import kamkeel.npcs.network.PacketClient;
|
||||
import kamkeel.npcs.network.packets.request.ability.CustomAbilitiesGetPacket;
|
||||
import kamkeel.npcs.network.packets.request.ability.CustomAbilityGetPacket;
|
||||
@@ -21,6 +22,7 @@ import noppes.npcs.constants.EnumScrollData;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
@@ -98,14 +100,18 @@ public class SubGuiAbilityLoad extends SubGuiInterface implements ICustomScrollL
|
||||
}
|
||||
|
||||
private List<String> getFilteredList() {
|
||||
if (search.isEmpty()) {
|
||||
return new ArrayList<>(rawData.keySet());
|
||||
}
|
||||
List<String> list = new ArrayList<>();
|
||||
for (String name : rawData.keySet()) {
|
||||
if (name.toLowerCase().contains(search.toLowerCase())) {
|
||||
list.add(name);
|
||||
for (Map.Entry<String, Integer> entry : rawData.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
|
||||
// Only show NPC-valid abilities (NPC_ONLY or BOTH)
|
||||
UserType ut = UserType.fromOrdinal(entry.getValue());
|
||||
if (!ut.allowsNpc()) continue;
|
||||
|
||||
if (!search.isEmpty() && !name.toLowerCase().contains(search.toLowerCase())) {
|
||||
continue;
|
||||
}
|
||||
list.add(name);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package noppes.npcs.client.gui.advanced;
|
||||
|
||||
import kamkeel.npcs.controllers.data.ability.enums.UserType;
|
||||
import kamkeel.npcs.network.PacketClient;
|
||||
import kamkeel.npcs.network.packets.request.ability.CustomAbilitiesGetPacket;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
@@ -15,6 +16,7 @@ import noppes.npcs.constants.EnumScrollData;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
@@ -93,14 +95,18 @@ public class SubGuiAbilitySelect extends SubGuiInterface implements ICustomScrol
|
||||
merged.putAll(builtInData);
|
||||
}
|
||||
|
||||
if (search.isEmpty()) {
|
||||
return new ArrayList<>(merged.keySet());
|
||||
}
|
||||
List<String> list = new ArrayList<>();
|
||||
for (String name : merged.keySet()) {
|
||||
if (name.toLowerCase().contains(search.toLowerCase())) {
|
||||
list.add(name);
|
||||
for (Map.Entry<String, Integer> entry : merged.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
|
||||
// Only show NPC-valid abilities (NPC_ONLY or BOTH)
|
||||
UserType ut = UserType.fromOrdinal(entry.getValue());
|
||||
if (!ut.allowsNpc()) continue;
|
||||
|
||||
if (!search.isEmpty() && !name.toLowerCase().contains(search.toLowerCase())) {
|
||||
continue;
|
||||
}
|
||||
list.add(name);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import kamkeel.npcs.controllers.AbilityController;
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityVariant;
|
||||
import kamkeel.npcs.controllers.data.ability.data.ChainedAbility;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.UserType;
|
||||
import kamkeel.npcs.network.PacketClient;
|
||||
|
||||
import java.util.UUID;
|
||||
@@ -48,6 +49,7 @@ import noppes.npcs.entity.EntityNPCInterface;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
@@ -72,6 +74,7 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface
|
||||
private static final int BTN_PREVIEW_PLAY = 91;
|
||||
private static final int BTN_PREVIEW_PAUSE = 92;
|
||||
private static final int BTN_PREVIEW_STOP = 93;
|
||||
private static final int BTN_USER_TYPE_FILTER = 11;
|
||||
|
||||
// ── TextField / Label IDs ─────────────────────────────────────────────────
|
||||
private static final int TF_SEARCH = 55;
|
||||
@@ -99,6 +102,11 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface
|
||||
private boolean showingChained = false;
|
||||
private boolean currentIsBuiltIn = false;
|
||||
|
||||
// ==================== USER TYPE FILTER ====================
|
||||
// 0 = Both, 1 = NPC Only, 2 = Player Only
|
||||
private int userTypeFilter = 0;
|
||||
private static final String[] USER_TYPE_FILTER_LABELS = {"gui.both", "gui.npcs", "gui.players"};
|
||||
|
||||
// ==================== CHAINED DATA ====================
|
||||
private HashMap<String, Integer> chainedData = new HashMap<>();
|
||||
private ChainedAbility selectedChain = null;
|
||||
@@ -176,6 +184,11 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface
|
||||
// Search bar
|
||||
addTextField(new GuiNpcTextField(TF_SEARCH, this, fontRendererObj, guiLeft + 220, guiTop + 192, 143, 20, search));
|
||||
|
||||
// User type filter button (only for Custom and Built-in views)
|
||||
if (!showingChained) {
|
||||
addButton(new GuiNpcButton(BTN_USER_TYPE_FILTER, guiLeft + 368, guiTop + 192, 45, 20, USER_TYPE_FILTER_LABELS[userTypeFilter]));
|
||||
}
|
||||
|
||||
// Check if we have something to preview
|
||||
if (showingChained) {
|
||||
if (selectedChain == null || selected == null || selectedChain.getEntries().isEmpty()) {
|
||||
@@ -213,14 +226,23 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface
|
||||
|
||||
private List<String> getSearchList() {
|
||||
HashMap<String, Integer> data = getCurrentData();
|
||||
if (search.isEmpty()) {
|
||||
return new ArrayList<>(data.keySet());
|
||||
}
|
||||
List<String> list = new ArrayList<>();
|
||||
for (String name : data.keySet()) {
|
||||
if (name.toLowerCase().contains(search.toLowerCase())) {
|
||||
list.add(name);
|
||||
for (Map.Entry<String, Integer> entry : data.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
|
||||
// Apply search filter
|
||||
if (!search.isEmpty() && !name.toLowerCase().contains(search.toLowerCase())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Apply UserType filter (only for Custom and Built-in views)
|
||||
if (!showingChained && userTypeFilter != 0) {
|
||||
UserType ut = UserType.fromOrdinal(entry.getValue());
|
||||
if (userTypeFilter == 1 && !ut.allowsNpc()) continue;
|
||||
if (userTypeFilter == 2 && !ut.allowsPlayer()) continue;
|
||||
}
|
||||
|
||||
list.add(name);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
@@ -278,6 +300,16 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface
|
||||
|
||||
int id = guibutton.id;
|
||||
|
||||
// User type filter: Both → NPC → Player → Both
|
||||
if (id == BTN_USER_TYPE_FILTER) {
|
||||
userTypeFilter = (userTypeFilter + 1) % 3;
|
||||
if (scroll != null) {
|
||||
scroll.setList(getSearchList());
|
||||
}
|
||||
initGui();
|
||||
return;
|
||||
}
|
||||
|
||||
// Toggle: Custom → Built-in → Chained → Custom
|
||||
if (id == BTN_TOGGLE_VIEW) {
|
||||
if (!showingBuiltIn && !showingChained) {
|
||||
|
||||
@@ -11,6 +11,7 @@ import noppes.npcs.controllers.data.IScriptHandler;
|
||||
import noppes.npcs.controllers.data.IScriptUnit;
|
||||
import noppes.npcs.scripted.NpcAPI;
|
||||
|
||||
import javax.script.Bindings;
|
||||
import javax.script.Compilable;
|
||||
import javax.script.CompiledScript;
|
||||
import javax.script.ScriptContext;
|
||||
@@ -52,10 +53,39 @@ public class ScriptContainer implements IScriptUnit {
|
||||
*/
|
||||
private String language = null;
|
||||
|
||||
/**
|
||||
* Per-instance bindings for variable isolation. When non-null, this container
|
||||
* is an instance scope sharing a template's engine. All eval/lookup uses these
|
||||
* bindings instead of the engine's default ENGINE_SCOPE.
|
||||
*/
|
||||
private Bindings instanceBindings = null;
|
||||
|
||||
public ScriptContainer(IScriptHandler handler) {
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a lightweight instance scope that shares this container's engine
|
||||
* but has its own isolated Bindings for variable state.
|
||||
* The template's engine is initialized if needed.
|
||||
*
|
||||
* @param instanceHandler the script handler for the instance
|
||||
*/
|
||||
public ScriptContainer createInstanceScope(IScriptHandler instanceHandler) {
|
||||
if (this.engine == null) {
|
||||
this.setEngine(this.getLanguage());
|
||||
}
|
||||
ScriptContainer instance = new ScriptContainer(instanceHandler);
|
||||
instance.script = this.script;
|
||||
instance.scripts = new ArrayList<>(this.scripts);
|
||||
instance.console = this.console;
|
||||
instance.language = this.language;
|
||||
instance.engine = this.engine;
|
||||
instance.currentScriptLanguage = this.currentScriptLanguage;
|
||||
instance.instanceBindings = this.engine.createBindings();
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void readFromNBT(NBTTagCompound compound) {
|
||||
String prevScript = this.script;
|
||||
this.script = compound.getString("Script");
|
||||
@@ -205,20 +235,33 @@ public class ScriptContainer implements IScriptUnit {
|
||||
engine.getContext().setWriter(pw);
|
||||
engine.getContext().setErrorWriter(pw);
|
||||
|
||||
engine.put("API", NpcAPI.Instance());
|
||||
HashMap<String, Object> engineEntries = new HashMap<>(NpcAPI.engineObjects);
|
||||
for (Map.Entry<String, Object> objectEntry : engineEntries.entrySet()) {
|
||||
engine.put(objectEntry.getKey(), objectEntry.getValue());
|
||||
// Populate bindings with API objects
|
||||
if (instanceBindings != null) {
|
||||
instanceBindings.put("API", NpcAPI.Instance());
|
||||
HashMap<String, Object> engineEntries = new HashMap<>(NpcAPI.engineObjects);
|
||||
for (Map.Entry<String, Object> objectEntry : engineEntries.entrySet()) {
|
||||
instanceBindings.put(objectEntry.getKey(), objectEntry.getValue());
|
||||
}
|
||||
} else {
|
||||
engine.put("API", NpcAPI.Instance());
|
||||
HashMap<String, Object> engineEntries = new HashMap<>(NpcAPI.engineObjects);
|
||||
for (Map.Entry<String, Object> objectEntry : engineEntries.entrySet()) {
|
||||
engine.put(objectEntry.getKey(), objectEntry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (!evaluated) {
|
||||
this.cachedFunctions.clear();
|
||||
engine.eval(getFullCode());
|
||||
if (instanceBindings != null) {
|
||||
engine.eval(getFullCode(), instanceBindings);
|
||||
} else {
|
||||
engine.eval(getFullCode());
|
||||
}
|
||||
evaluated = true;
|
||||
}
|
||||
if (engine.getFactory().getLanguageName().equals("lua")) {
|
||||
Object ob = engine.get(type);
|
||||
Object ob = instanceBindings != null ? instanceBindings.get(type) : engine.get(type);
|
||||
if (ob != null) {
|
||||
if (luaCoerce == null) {
|
||||
luaCoerce = Class.forName("org.luaj.vm2.lib.jse.CoerceJavaToLua").getMethod("coerce", Object.class);
|
||||
@@ -230,8 +273,14 @@ public class ScriptContainer implements IScriptUnit {
|
||||
}
|
||||
} else {
|
||||
if (!this.cachedFunctions.containsKey(type)) {
|
||||
ScriptObjectMirror global = (ScriptObjectMirror) engine.getBindings(ScriptContext.ENGINE_SCOPE);
|
||||
ScriptObjectMirror func = (ScriptObjectMirror) global.get(type);
|
||||
ScriptObjectMirror func;
|
||||
if (instanceBindings != null) {
|
||||
Object val = instanceBindings.get(type);
|
||||
func = val instanceof ScriptObjectMirror ? (ScriptObjectMirror) val : null;
|
||||
} else {
|
||||
ScriptObjectMirror global = (ScriptObjectMirror) engine.getBindings(ScriptContext.ENGINE_SCOPE);
|
||||
func = (ScriptObjectMirror) global.get(type);
|
||||
}
|
||||
this.cachedFunctions.put(type, func);
|
||||
}
|
||||
ScriptObjectMirror func = this.cachedFunctions.get(type);
|
||||
@@ -280,6 +329,8 @@ public class ScriptContainer implements IScriptUnit {
|
||||
|
||||
public void setEngine(String scriptLanguage) {
|
||||
if (!Objects.equals(scriptLanguage, this.currentScriptLanguage)) {
|
||||
// Instance scopes share the template's engine — never create a new one
|
||||
if (instanceBindings != null) return;
|
||||
this.currentScriptLanguage = scriptLanguage;
|
||||
if (ConfigScript.ScriptingECMA6 && scriptLanguage.equals("ECMAScript")) {
|
||||
System.setProperty("nashorn.args", "--language=es6");
|
||||
|
||||
@@ -696,6 +696,7 @@ public class PlayerAbilityData extends AbstractDataAbilities implements IPlayerA
|
||||
}
|
||||
|
||||
// Clear chain and concurrent state
|
||||
if (currentChain != null) currentChain.clearInstanceScript();
|
||||
currentChain = null;
|
||||
chainEntryIndex = -1;
|
||||
chainDelayRemaining = -1;
|
||||
@@ -778,6 +779,7 @@ public class PlayerAbilityData extends AbstractDataAbilities implements IPlayerA
|
||||
}
|
||||
|
||||
// Clear chain and concurrent state
|
||||
if (currentChain != null) currentChain.clearInstanceScript();
|
||||
currentChain = null;
|
||||
chainEntryIndex = -1;
|
||||
chainDelayRemaining = -1;
|
||||
|
||||
Reference in new issue
Block a user