mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-01-08 08:42:53 +08:00
Compile scripts to replace require
with script content
This commit is contained in:
parent
67c0e82dfb
commit
d43e5ca608
@ -111,6 +111,12 @@ public class ConfigContainer {
|
||||
public ServerRunMode runMode = ServerRunMode.HYBRID;
|
||||
public boolean logCommands = false;
|
||||
|
||||
/**
|
||||
* If enabled, the 'require' Lua function will load the script's compiled varient into the context. (faster; doesn't work as well)
|
||||
* If disabled, all 'require' calls will be replaced with the referenced script's source. (slower; works better)
|
||||
*/
|
||||
public boolean fastRequire = true;
|
||||
|
||||
public HTTP http = new HTTP();
|
||||
public Game game = new Game();
|
||||
|
||||
|
@ -1,11 +1,12 @@
|
||||
package emu.grasscutter.config;
|
||||
|
||||
import static emu.grasscutter.Grasscutter.config;
|
||||
|
||||
import emu.grasscutter.utils.FileUtils;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Locale;
|
||||
|
||||
import static emu.grasscutter.Grasscutter.config;
|
||||
|
||||
/**
|
||||
* A data container for the server's configuration.
|
||||
*
|
||||
@ -38,6 +39,7 @@ public final class Configuration extends ConfigContainer {
|
||||
config.server.game.gameOptions.inventoryLimits;
|
||||
public static final GameOptions.HandbookOptions HANDBOOK =
|
||||
config.server.game.gameOptions.handbook;
|
||||
public static final boolean FAST_REQUIRE = config.server.fastRequire;
|
||||
private static final String DATA_FOLDER = config.folderStructure.data;
|
||||
private static final String PLUGINS_FOLDER = config.folderStructure.plugins;
|
||||
private static final String SCRIPTS_FOLDER = config.folderStructure.scripts;
|
||||
|
@ -6,9 +6,10 @@ import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction;
|
||||
import emu.grasscutter.game.ability.Ability;
|
||||
import emu.grasscutter.game.entity.GameEntity;
|
||||
import emu.grasscutter.scripts.ScriptLoader;
|
||||
import javax.script.Bindings;
|
||||
import org.luaj.vm2.LuaFunction;
|
||||
|
||||
import javax.script.Bindings;
|
||||
|
||||
@AbilityAction(AbilityModifierAction.Type.ServerLuaCall)
|
||||
public final class ActionServerLuaCall extends AbilityActionHandler {
|
||||
@Override
|
||||
|
@ -1,6 +1,7 @@
|
||||
package emu.grasscutter.scripts;
|
||||
|
||||
import emu.grasscutter.*;
|
||||
import emu.grasscutter.config.Configuration;
|
||||
import emu.grasscutter.game.dungeons.challenge.enums.*;
|
||||
import emu.grasscutter.game.props.*;
|
||||
import emu.grasscutter.game.quest.enums.QuestState;
|
||||
@ -8,46 +9,54 @@ import emu.grasscutter.scripts.constants.*;
|
||||
import emu.grasscutter.scripts.data.SceneMeta;
|
||||
import emu.grasscutter.scripts.serializer.*;
|
||||
import emu.grasscutter.utils.FileUtils;
|
||||
import lombok.Getter;
|
||||
import org.luaj.vm2.*;
|
||||
import org.luaj.vm2.lib.*;
|
||||
import org.luaj.vm2.lib.jse.CoerceJavaToLua;
|
||||
import org.luaj.vm2.script.*;
|
||||
|
||||
import javax.script.*;
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import javax.script.*;
|
||||
import lombok.Getter;
|
||||
import org.luaj.vm2.*;
|
||||
import org.luaj.vm2.lib.OneArgFunction;
|
||||
import org.luaj.vm2.lib.jse.CoerceJavaToLua;
|
||||
import org.luaj.vm2.script.LuajContext;
|
||||
|
||||
public class ScriptLoader {
|
||||
private static ScriptEngineManager sm;
|
||||
@Getter private static ScriptEngine engine;
|
||||
private static ScriptEngineFactory factory;
|
||||
@Getter private static LuaScriptEngine engine;
|
||||
@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<String>> scriptSources =
|
||||
new ConcurrentHashMap<>();
|
||||
private static Map<String, SoftReference<CompiledScript>> scriptsCache =
|
||||
new ConcurrentHashMap<>();
|
||||
/** sceneId - SceneMeta */
|
||||
private static Map<Integer, SoftReference<SceneMeta>> sceneMetaCache = new ConcurrentHashMap<>();
|
||||
|
||||
private static final AtomicReference<Bindings> currentBindings = new AtomicReference<>(null);
|
||||
private static final AtomicReference<ScriptContext> currentContext = new AtomicReference<>(null);
|
||||
|
||||
/**
|
||||
* Initializes the script engine.
|
||||
*/
|
||||
public static synchronized 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();
|
||||
ScriptLoader.sm = new ScriptEngineManager();
|
||||
var engine = ScriptLoader.engine = (LuaScriptEngine) sm.getEngineByName("luaj");
|
||||
ScriptLoader.serializer = new LuaSerializer();
|
||||
|
||||
// Lua stuff
|
||||
serializer = new LuaSerializer();
|
||||
var ctx = (LuajContext) engine.getContext();
|
||||
// Set the Lua context.
|
||||
var ctx = new LuajContext(true, false);
|
||||
ctx.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
|
||||
engine.setContext(ctx);
|
||||
|
||||
// Set the 'require' function handler.
|
||||
ctx.globals.set("require", new RequireFunction());
|
||||
@ -141,6 +150,7 @@ public class ScriptLoader {
|
||||
// Append the script to the context.
|
||||
try {
|
||||
var bindings = currentBindings.get();
|
||||
|
||||
if (bindings != null) {
|
||||
ScriptLoader.eval(script, bindings);
|
||||
} else {
|
||||
@ -158,19 +168,96 @@ public class ScriptLoader {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the sources of a script.
|
||||
*
|
||||
* @param path The path of the script.
|
||||
* @return The sources of the script.
|
||||
*/
|
||||
public static String readScript(String path) {
|
||||
// Check if the path is cached.
|
||||
var cached = ScriptLoader.tryGet(
|
||||
ScriptLoader.scriptSources.get(path));
|
||||
if (cached.isPresent()) {
|
||||
return cached.get();
|
||||
}
|
||||
|
||||
// Attempt to load the script.
|
||||
var scriptPath = FileUtils.getScriptPath(path);
|
||||
if (!Files.exists(scriptPath)) return null;
|
||||
|
||||
try {
|
||||
var source = Files.readString(scriptPath);
|
||||
ScriptLoader.scriptSources.put(
|
||||
path, new SoftReference<>(source));
|
||||
|
||||
return source;
|
||||
} catch (IOException exception) {
|
||||
Grasscutter.getLogger()
|
||||
.error("Loading script {} failed! - {}", path, exception.getLocalizedMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a script and compiles it, or uses the cached varient.
|
||||
*
|
||||
* @param path The path of the script.
|
||||
* @return The compiled script.
|
||||
*/
|
||||
public static CompiledScript getScript(String path) {
|
||||
var sc = tryGet(scriptsCache.get(path));
|
||||
// Check if the script is cached.
|
||||
var sc = ScriptLoader.tryGet(
|
||||
ScriptLoader.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));
|
||||
CompiledScript script;
|
||||
if (Configuration.FAST_REQUIRE) {
|
||||
// Attempt to load the script.
|
||||
var scriptPath = FileUtils.getScriptPath(path);
|
||||
if (!Files.exists(scriptPath)) return null;
|
||||
|
||||
// Compile the script from the file.
|
||||
var source = Files.newBufferedReader(scriptPath);
|
||||
script = ScriptLoader.getEngine().compile(source);
|
||||
} else {
|
||||
// Load the script sources.
|
||||
var sources = ScriptLoader.readScript(path);
|
||||
if (sources == null) return null;
|
||||
|
||||
// Check to see if the script references other scripts.
|
||||
if (sources.contains("require")) {
|
||||
var lines = sources.split("\n");
|
||||
var output = new StringBuilder();
|
||||
for (var line : lines) {
|
||||
// Skip non-require lines.
|
||||
if (!line.startsWith("require")) {
|
||||
output.append(line).append("\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Extract the script name.
|
||||
var scriptName = line.substring(9, line.length() - 1);
|
||||
// Resolve the script path.
|
||||
var scriptPath = "Common/" + scriptName + ".lua";
|
||||
var scriptSource = ScriptLoader.readScript(scriptPath);
|
||||
if (scriptSource == null) continue;
|
||||
|
||||
// Append the script source.
|
||||
output.append(scriptSource).append("\n");
|
||||
}
|
||||
sources = output.toString();
|
||||
}
|
||||
|
||||
// Compile the script & cache it in memory.
|
||||
script = ScriptLoader.getEngine().compile(sources);
|
||||
}
|
||||
|
||||
// Cache the script.
|
||||
ScriptLoader.scriptsCache.put(path, new SoftReference<>(script));
|
||||
return script;
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger()
|
||||
|
@ -3,12 +3,13 @@ package emu.grasscutter.scripts.data;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.game.world.Position;
|
||||
import emu.grasscutter.scripts.ScriptLoader;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.script.*;
|
||||
import lombok.*;
|
||||
import org.luaj.vm2.*;
|
||||
|
||||
import javax.script.*;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ToString
|
||||
@Setter
|
||||
public final class SceneGroup {
|
||||
@ -35,7 +36,8 @@ public final class SceneGroup {
|
||||
|
||||
public SceneReplaceable is_replaceable;
|
||||
|
||||
private transient boolean loaded; // Not an actual variable in the scripts either
|
||||
/* These are not script variables. */
|
||||
private transient boolean loaded;
|
||||
private transient CompiledScript script;
|
||||
private transient Bindings bindings;
|
||||
|
||||
@ -82,7 +84,7 @@ public final class SceneGroup {
|
||||
}
|
||||
// Set flag here so if there is no script, we don't call this function over and over again.
|
||||
this.setLoaded(true);
|
||||
|
||||
// Create the bindings.
|
||||
this.bindings = ScriptLoader.getEngine().createBindings();
|
||||
|
||||
var cs =
|
||||
|
Loading…
Reference in New Issue
Block a user