ScriptLoader.java 4.05 KB
Newer Older
1
2
3
package emu.grasscutter.scripts;

import emu.grasscutter.Grasscutter;
Melledy's avatar
Melledy committed
4
5
6
7
import emu.grasscutter.game.props.EntityType;
import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.constants.ScriptGadgetState;
import emu.grasscutter.scripts.constants.ScriptRegionShape;
Akka's avatar
Akka committed
8
import emu.grasscutter.scripts.data.SceneMeta;
9
10
import emu.grasscutter.scripts.serializer.LuaSerializer;
import emu.grasscutter.scripts.serializer.Serializer;
Akka's avatar
Akka committed
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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.util.Arrays;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
25
26
27
28
29
30
31

public class ScriptLoader {
	private static ScriptEngineManager sm;
	private static ScriptEngine engine;
	private static ScriptEngineFactory factory;
	private static String fileType;
	private static Serializer serializer;
32
33
	private static ScriptLib scriptLib;
	private static LuaValue scriptLibLua;
Akka's avatar
Akka committed
34
35
36
37
38
39
40
41
42
	/**
	 * 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<>();

43
44
45
46
47
	public synchronized static void init() throws Exception {
		if (sm != null) {
			throw new Exception("Script loader already initialized");
		}
		
48
		// Create script engine
49
50
51
		sm = new ScriptEngineManager();
        engine = sm.getEngineByName("luaj");
        factory = getEngine().getFactory();
52
53
        
        // Lua stuff
54
55
        fileType = "lua";
        serializer = new LuaSerializer();
56
57
58
59
60
61
62
63
64
        
        // 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;
		    }
		});
Melledy's avatar
Melledy committed
65
66
67
68
69
70
71
72
		
		LuaTable table = new LuaTable();
		Arrays.stream(EntityType.values()).forEach(e -> table.set(e.name().toUpperCase(), e.getValue()));
		ctx.globals.set("EntityType", table);
		
		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()));
73
74
75
76

		scriptLib = new ScriptLib();
		scriptLibLua = CoerceJavaToLua.coerce(scriptLib);
		ctx.globals.set("ScriptLib", scriptLibLua);
77
78
79
80
81
82
83
84
85
86
87
88
89
90
	}
	
	public static ScriptEngine getEngine() {
		return engine;
	}
	
	public static String getScriptType() {
		return fileType;
	}

	public static Serializer getSerializer() {
		return serializer;
	}

91
92
93
94
95
96
97
98
	public static ScriptLib getScriptLib() {
		return scriptLib;
	}

	public static LuaValue getScriptLibLua() {
		return scriptLibLua;
	}

Akka's avatar
Akka committed
99
100
101
102
103
104
105
	public static <T> Optional<T> tryGet(SoftReference<T> softReference){
		try{
			return Optional.ofNullable(softReference.get());
		}catch (NullPointerException npe){
			return Optional.empty();
		}
	}
106
	public static CompiledScript getScriptByPath(String path) {
Akka's avatar
Akka committed
107
108
109
		var sc = tryGet(scriptsCache.get(path));
		if (sc.isPresent()) {
			return sc.get();
110
		}
Akka's avatar
Akka committed
111

Melledy's avatar
Melledy committed
112
		Grasscutter.getLogger().info("Loading script " + path);
Akka's avatar
Akka committed
113
114
115
116
117
118
119
120
121
122

		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) {
Melledy's avatar
Melledy committed
123
			Grasscutter.getLogger().error("Loading script {} failed!", path, e);
Akka's avatar
Akka committed
124
125
126
			return null;
		}

127
	}
Akka's avatar
Akka committed
128
129
130
131
132
133
134
135
136

	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;
		});
	}

137
}