Files
Grasscutter/src/main/java/emu/grasscutter/scripts/ScriptLoader.java
2022-10-13 13:32:10 +00:00

148 lines
5.2 KiB
Java

package emu.grasscutter.scripts;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.props.EntityType;
import emu.grasscutter.game.quest.enums.QuestState;
import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.constants.ScriptGadgetState;
import emu.grasscutter.scripts.constants.ScriptRegionShape;
import emu.grasscutter.scripts.data.SceneMeta;
import emu.grasscutter.scripts.serializer.LuaSerializer;
import emu.grasscutter.scripts.serializer.Serializer;
import emu.grasscutter.utils.FileUtils;
import lombok.Getter;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.OneArgFunction;
import org.luaj.vm2.lib.jse.CoerceJavaToLua;
import org.luaj.vm2.script.LuajContext;
import javax.script.*;
import java.io.File;
import java.io.FileReader;
import java.lang.ref.SoftReference;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
public class ScriptLoader {
private static ScriptEngineManager sm;
@Getter private static ScriptEngine engine;
private static ScriptEngineFactory factory;
@Getter private static Serializer serializer;
@Getter private static ScriptLib scriptLib;
@Getter private static LuaValue scriptLibLua;
/**
* suggest GC to remove it if the memory is less
*/
private static Map<String, SoftReference<CompiledScript>> scriptsCache = new ConcurrentHashMap<>();
/**
* sceneId - SceneMeta
*/
private static Map<Integer, SoftReference<SceneMeta>> sceneMetaCache = new ConcurrentHashMap<>();
public synchronized static void init() throws Exception {
if (sm != null) {
throw new Exception("Script loader already initialized");
}
// Create script engine
sm = new ScriptEngineManager();
engine = sm.getEngineByName("luaj");
factory = getEngine().getFactory();
// Lua stuff
serializer = new LuaSerializer();
// Set engine to replace require as a temporary fix to missing scripts
LuajContext ctx = (LuajContext) engine.getContext();
ctx.globals.set("require", new OneArgFunction() {
@Override
public LuaValue call(LuaValue arg0) {
return LuaValue.ZERO;
}
});
LuaTable table = new LuaTable();
Arrays.stream(EntityType.values()).forEach(e -> table.set(e.name().toUpperCase(), e.getValue()));
ctx.globals.set("EntityType", table);
LuaTable table1 = new LuaTable();
Arrays.stream(QuestState.values()).forEach(e -> table1.set(e.name().toUpperCase(), e.getValue()));
ctx.globals.set("QuestState", table1);
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 <T> Optional<T> tryGet(SoftReference<T> softReference) {
try {
return Optional.ofNullable(softReference.get());
}catch (NullPointerException npe) {
return Optional.empty();
}
}
@Deprecated(forRemoval = true)
public static CompiledScript getScriptByPath(String path) {
var sc = tryGet(scriptsCache.get(path));
if (sc.isPresent()) {
return sc.get();
}
Grasscutter.getLogger().debug("Loading script " + path);
File file = new File(path);
if (!file.exists()) return null;
try (FileReader fr = new FileReader(file)) {
var script = ((Compilable) getEngine()).compile(fr);
scriptsCache.put(path, new SoftReference<>(script));
return script;
} catch (Exception e) {
Grasscutter.getLogger().error("Loading script {} failed!", path, e);
return null;
}
}
public static CompiledScript getScript(String path) {
var sc = tryGet(scriptsCache.get(path));
if (sc.isPresent()) {
return sc.get();
}
Grasscutter.getLogger().debug("Loading script " + path);
final Path scriptPath = FileUtils.getScriptPath(path);
if (!Files.exists(scriptPath)) return null;
try {
var script = ((Compilable) getEngine()).compile(Files.newBufferedReader(scriptPath));
scriptsCache.put(path, new SoftReference<>(script));
return script;
} catch (Exception e) {
Grasscutter.getLogger().error("Loading script {} failed! - {}", path, e.getLocalizedMessage());
return null;
}
}
public static SceneMeta getSceneMeta(int sceneId) {
return tryGet(sceneMetaCache.get(sceneId)).orElseGet(() -> {
var instance = SceneMeta.of(sceneId);
sceneMetaCache.put(sceneId, new SoftReference<>(instance));
return instance;
});
}
}