diff --git a/src/main/java/emu/grasscutter/game/dungeons/DungeonChallenge.java b/src/main/java/emu/grasscutter/game/dungeons/DungeonChallenge.java index a13f1a611..2b1e992f4 100644 --- a/src/main/java/emu/grasscutter/game/dungeons/DungeonChallenge.java +++ b/src/main/java/emu/grasscutter/game/dungeons/DungeonChallenge.java @@ -138,7 +138,9 @@ public class DungeonChallenge { getScene().getDungeonSettleObservers().forEach(o -> o.onDungeonSettle(getScene())); if(!stage){ - getScene().getScriptManager().callEvent(EventType.EVENT_DUNGEON_SETTLE, new ScriptArgs(this.isSuccess() ? 1 : 0)); + getScene().getScriptManager().callEvent(EventType.EVENT_DUNGEON_SETTLE, + // TODO record the time in PARAM2 and used in action + new ScriptArgs(this.isSuccess() ? 1 : 0, 100)); } } diff --git a/src/main/java/emu/grasscutter/game/world/WorldDataManager.java b/src/main/java/emu/grasscutter/game/world/WorldDataManager.java index 3809dce6d..ab3ea40c1 100644 --- a/src/main/java/emu/grasscutter/game/world/WorldDataManager.java +++ b/src/main/java/emu/grasscutter/game/world/WorldDataManager.java @@ -26,7 +26,7 @@ public class WorldDataManager { } public synchronized void load(){ - try(InputStream is = DataLoader.load("ChestReward.json", false); InputStreamReader isr = new InputStreamReader(is)) { + try(InputStream is = DataLoader.load("ChestReward.json"); InputStreamReader isr = new InputStreamReader(is)) { List chestReward = Grasscutter.getGsonFactory().fromJson( isr, TypeToken.getParameterized(List.class, ChestReward.class).getType()); diff --git a/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java b/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java index 2bc42bace..bbb475717 100644 --- a/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java +++ b/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java @@ -20,16 +20,11 @@ import org.luaj.vm2.LuaError; import org.luaj.vm2.LuaValue; import org.luaj.vm2.lib.jse.CoerceJavaToLua; -import javax.script.Bindings; -import javax.script.ScriptException; import java.util.*; public class SceneScriptManager { private final Scene scene; - private final ScriptLib scriptLib; - private final LuaValue scriptLibLua; private final Map variables; - private Bindings bindings; private SceneMeta meta; private boolean isInit; /** @@ -50,8 +45,6 @@ public class SceneScriptManager { private Int2ObjectMap> loadedGroupSetPerBlock; public SceneScriptManager(Scene scene) { this.scene = scene; - this.scriptLib = new ScriptLib(this); - this.scriptLibLua = CoerceJavaToLua.coerce(this.scriptLib); this.triggers = new HashMap<>(); this.currentTriggers = new Int2ObjectOpenHashMap<>(); @@ -74,18 +67,6 @@ public class SceneScriptManager { return scene; } - public ScriptLib getScriptLib() { - return scriptLib; - } - - public LuaValue getScriptLibLua() { - return scriptLibLua; - } - - public Bindings getBindings() { - return bindings; - } - public SceneConfig getConfig() { if(!isInit){ return null; @@ -162,11 +143,6 @@ public class SceneScriptManager { } private void init() { - // Create bindings - bindings = ScriptLoader.getEngine().createBindings(); - // Set variables - bindings.put("ScriptLib", getScriptLib()); - var meta = ScriptLoader.getSceneMeta(getScene().getId()); if (meta == null){ return; @@ -186,14 +162,7 @@ public class SceneScriptManager { } public void loadGroupFromScript(SceneGroup group) { - group.load(getScene().getId(), meta.context); - - try { - // build the trigger for this scene - group.getScript().eval(getBindings()); - } catch (ScriptException e) { - Grasscutter.getLogger().error("Could not build the trigger for this scene", e); - } + group.load(getScene().getId()); group.variables.forEach(var -> this.getVariables().put(var.name, var.value)); this.sceneGroups.put(group.id, group); @@ -284,47 +253,55 @@ public class SceneScriptManager { // Events public void callEvent(int eventType, ScriptArgs params) { - for (SceneTrigger trigger : this.getTriggersByEvent(eventType)) { - scriptLib.setCurrentGroup(trigger.currentGroup); - LuaValue condition = null; - - if (trigger.condition != null && !trigger.condition.isEmpty()) { - condition = (LuaValue) this.getBindings().get(trigger.condition); - } - - LuaValue ret = LuaValue.TRUE; - - if (condition != null) { - LuaValue args = LuaValue.NIL; - - if (params != null) { - args = CoerceJavaToLua.coerce(params); - } + try{ + ScriptLoader.getScriptLib().setSceneScriptManager(this); + for (SceneTrigger trigger : this.getTriggersByEvent(eventType)) { + try{ + ScriptLoader.getScriptLib().setCurrentGroup(trigger.currentGroup); - ScriptLib.logger.trace("Call Condition Trigger {}", trigger); - ret = safetyCall(trigger.condition, condition, args); - } - - if (ret.isboolean() && ret.checkboolean()) { - if(trigger.action == null || trigger.action.isEmpty()){ - return; + LuaValue ret = callScriptFunc(trigger.condition, trigger.currentGroup, params); + Grasscutter.getLogger().trace("Call Condition Trigger {}", trigger.condition); + + if (ret.isboolean() && ret.checkboolean()) { + // the SetGroupVariableValueByGroup in tower need the param to record the first stage time + callScriptFunc(trigger.action, trigger.currentGroup, params); + Grasscutter.getLogger().trace("Call Action Trigger {}", trigger.action); + } + //TODO some ret may not bool + + }finally { + ScriptLoader.getScriptLib().removeCurrentGroup(); } - ScriptLib.logger.trace("Call Action Trigger {}", trigger); - LuaValue action = (LuaValue) this.getBindings().get(trigger.action); - // TODO impl the param of SetGroupVariableValueByGroup - var arg = new ScriptArgs(); - arg.param2 = 100; - var args = CoerceJavaToLua.coerce(arg); - safetyCall(trigger.action, action, args); } - //TODO some ret may not bool - scriptLib.removeCurrentGroup(); + }finally { + // make sure it is removed + ScriptLoader.getScriptLib().removeSceneScriptManager(); } } + public LuaValue callScriptFunc(String funcName, SceneGroup group, ScriptArgs params){ + LuaValue funcLua = null; + if (funcName != null && !funcName.isEmpty()) { + funcLua = (LuaValue) group.getBindings().get(funcName); + } + + LuaValue ret = LuaValue.TRUE; + + if (funcLua != null) { + LuaValue args = LuaValue.NIL; + + if (params != null) { + args = CoerceJavaToLua.coerce(params); + } + + ret = safetyCall(funcName, funcLua, args); + } + return ret; + } + public LuaValue safetyCall(String name, LuaValue func, LuaValue args){ try{ - return func.call(this.getScriptLibLua(), args); + return func.call(ScriptLoader.getScriptLibLua(), args); }catch (LuaError error){ ScriptLib.logger.error("[LUA] call trigger failed {},{},{}",name,args,error.getMessage()); return LuaValue.valueOf(-1); diff --git a/src/main/java/emu/grasscutter/scripts/ScriptLib.java b/src/main/java/emu/grasscutter/scripts/ScriptLib.java index bea84d8b3..e33deb860 100644 --- a/src/main/java/emu/grasscutter/scripts/ScriptLib.java +++ b/src/main/java/emu/grasscutter/scripts/ScriptLib.java @@ -10,6 +10,7 @@ import emu.grasscutter.scripts.data.SceneRegion; import emu.grasscutter.server.packet.send.PacketCanUseSkillNotify; import emu.grasscutter.server.packet.send.PacketGadgetStateNotify; import emu.grasscutter.server.packet.send.PacketWorktopOptionNotify; +import io.netty.util.concurrent.FastThreadLocal; import org.luaj.vm2.LuaTable; import org.luaj.vm2.LuaValue; import org.slf4j.Logger; @@ -20,15 +21,24 @@ import java.util.Optional; public class ScriptLib { public static final Logger logger = LoggerFactory.getLogger(ScriptLib.class); - private final SceneScriptManager sceneScriptManager; - - public ScriptLib(SceneScriptManager sceneScriptManager) { - this.sceneScriptManager = sceneScriptManager; - this.currentGroup = new ThreadLocal<>(); + private final FastThreadLocal sceneScriptManager; + private final FastThreadLocal currentGroup; + public ScriptLib() { + this.sceneScriptManager = new FastThreadLocal<>(); + this.currentGroup = new FastThreadLocal<>(); + } + + public void setSceneScriptManager(SceneScriptManager sceneScriptManager){ + this.sceneScriptManager.set(sceneScriptManager); + } + + public void removeSceneScriptManager(){ + this.sceneScriptManager.remove(); } public SceneScriptManager getSceneScriptManager() { - return sceneScriptManager; + // normally not null + return Optional.of(sceneScriptManager.get()).get(); } private String printTable(LuaTable table){ @@ -40,13 +50,11 @@ public class ScriptLib { sb.append("}"); return sb.toString(); } - private final ThreadLocal currentGroup; public void setCurrentGroup(SceneGroup currentGroup){ - logger.debug("current {}", currentGroup); this.currentGroup.set(currentGroup); } public Optional getCurrentGroup(){ - return Optional.ofNullable(this.currentGroup.get()); + return Optional.of(this.currentGroup.get()); } public void removeCurrentGroup(){ this.currentGroup.remove(); diff --git a/src/main/java/emu/grasscutter/scripts/ScriptLoader.java b/src/main/java/emu/grasscutter/scripts/ScriptLoader.java index 6795de575..c55a76251 100644 --- a/src/main/java/emu/grasscutter/scripts/ScriptLoader.java +++ b/src/main/java/emu/grasscutter/scripts/ScriptLoader.java @@ -29,6 +29,8 @@ public class ScriptLoader { private static ScriptEngineFactory factory; private static String fileType; private static Serializer serializer; + private static ScriptLib scriptLib; + private static LuaValue scriptLibLua; /** * suggest GC to remove it if the memory is less */ @@ -68,6 +70,10 @@ public class ScriptLoader { ctx.globals.set("EventType", CoerceJavaToLua.coerce(new EventType())); // TODO - make static class to avoid instantiating a new class every scene ctx.globals.set("GadgetState", CoerceJavaToLua.coerce(new ScriptGadgetState())); ctx.globals.set("RegionShape", CoerceJavaToLua.coerce(new ScriptRegionShape())); + + scriptLib = new ScriptLib(); + scriptLibLua = CoerceJavaToLua.coerce(scriptLib); + ctx.globals.set("ScriptLib", scriptLibLua); } public static ScriptEngine getEngine() { @@ -82,6 +88,14 @@ public class ScriptLoader { return serializer; } + public static ScriptLib getScriptLib() { + return scriptLib; + } + + public static LuaValue getScriptLibLua() { + return scriptLibLua; + } + public static Optional tryGet(SoftReference softReference){ try{ return Optional.ofNullable(softReference.get()); diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneGroup.java b/src/main/java/emu/grasscutter/scripts/data/SceneGroup.java index d43961f7b..fb67c518f 100644 --- a/src/main/java/emu/grasscutter/scripts/data/SceneGroup.java +++ b/src/main/java/emu/grasscutter/scripts/data/SceneGroup.java @@ -10,12 +10,11 @@ import javax.script.Bindings; import javax.script.CompiledScript; import javax.script.ScriptException; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import static emu.grasscutter.Configuration.*; +import static emu.grasscutter.Configuration.SCRIPT; @ToString @Setter @@ -40,6 +39,7 @@ public class SceneGroup { private transient boolean loaded; // Not an actual variable in the scripts either private transient CompiledScript script; + private transient Bindings bindings; public boolean isLoaded() { return loaded; @@ -65,13 +65,19 @@ public class SceneGroup { return suites.get(index - 1); } - public SceneGroup load(int sceneId, Bindings bindings){ + public Bindings getBindings() { + return bindings; + } + + public SceneGroup load(int sceneId){ if(loaded){ return this; } // Set flag here so if there is no script, we dont call this function over and over again. setLoaded(true); + this.bindings = ScriptLoader.getEngine().createBindings(); + CompiledScript cs = ScriptLoader.getScriptByPath( SCRIPT("Scene/" + sceneId + "/scene" + sceneId + "_group" + id + "." + ScriptLoader.getScriptType())); diff --git a/src/main/java/emu/grasscutter/scripts/serializer/LuaSerializer.java b/src/main/java/emu/grasscutter/scripts/serializer/LuaSerializer.java index b262079ee..948be1739 100644 --- a/src/main/java/emu/grasscutter/scripts/serializer/LuaSerializer.java +++ b/src/main/java/emu/grasscutter/scripts/serializer/LuaSerializer.java @@ -87,6 +87,9 @@ public class LuaSerializer implements Serializer { object = (T) constructorCache.get(type).newInstance(); + if(table == null){ + return object; + } LuaValue[] keys = table.keys(); for (LuaValue k : keys) { try {