mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-05-12 06:56:02 +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 ServerRunMode runMode = ServerRunMode.HYBRID;
|
||||||
public boolean logCommands = false;
|
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 HTTP http = new HTTP();
|
||||||
public Game game = new Game();
|
public Game game = new Game();
|
||||||
|
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
package emu.grasscutter.config;
|
package emu.grasscutter.config;
|
||||||
|
|
||||||
import static emu.grasscutter.Grasscutter.config;
|
|
||||||
|
|
||||||
import emu.grasscutter.utils.FileUtils;
|
import emu.grasscutter.utils.FileUtils;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import static emu.grasscutter.Grasscutter.config;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A data container for the server's configuration.
|
* A data container for the server's configuration.
|
||||||
*
|
*
|
||||||
@ -38,6 +39,7 @@ public final class Configuration extends ConfigContainer {
|
|||||||
config.server.game.gameOptions.inventoryLimits;
|
config.server.game.gameOptions.inventoryLimits;
|
||||||
public static final GameOptions.HandbookOptions HANDBOOK =
|
public static final GameOptions.HandbookOptions HANDBOOK =
|
||||||
config.server.game.gameOptions.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 DATA_FOLDER = config.folderStructure.data;
|
||||||
private static final String PLUGINS_FOLDER = config.folderStructure.plugins;
|
private static final String PLUGINS_FOLDER = config.folderStructure.plugins;
|
||||||
private static final String SCRIPTS_FOLDER = config.folderStructure.scripts;
|
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.ability.Ability;
|
||||||
import emu.grasscutter.game.entity.GameEntity;
|
import emu.grasscutter.game.entity.GameEntity;
|
||||||
import emu.grasscutter.scripts.ScriptLoader;
|
import emu.grasscutter.scripts.ScriptLoader;
|
||||||
import javax.script.Bindings;
|
|
||||||
import org.luaj.vm2.LuaFunction;
|
import org.luaj.vm2.LuaFunction;
|
||||||
|
|
||||||
|
import javax.script.Bindings;
|
||||||
|
|
||||||
@AbilityAction(AbilityModifierAction.Type.ServerLuaCall)
|
@AbilityAction(AbilityModifierAction.Type.ServerLuaCall)
|
||||||
public final class ActionServerLuaCall extends AbilityActionHandler {
|
public final class ActionServerLuaCall extends AbilityActionHandler {
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package emu.grasscutter.scripts;
|
package emu.grasscutter.scripts;
|
||||||
|
|
||||||
import emu.grasscutter.*;
|
import emu.grasscutter.*;
|
||||||
|
import emu.grasscutter.config.Configuration;
|
||||||
import emu.grasscutter.game.dungeons.challenge.enums.*;
|
import emu.grasscutter.game.dungeons.challenge.enums.*;
|
||||||
import emu.grasscutter.game.props.*;
|
import emu.grasscutter.game.props.*;
|
||||||
import emu.grasscutter.game.quest.enums.QuestState;
|
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.data.SceneMeta;
|
||||||
import emu.grasscutter.scripts.serializer.*;
|
import emu.grasscutter.scripts.serializer.*;
|
||||||
import emu.grasscutter.utils.FileUtils;
|
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.lang.ref.SoftReference;
|
||||||
import java.nio.file.*;
|
import java.nio.file.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
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 {
|
public class ScriptLoader {
|
||||||
private static ScriptEngineManager sm;
|
private static ScriptEngineManager sm;
|
||||||
@Getter private static ScriptEngine engine;
|
@Getter private static LuaScriptEngine engine;
|
||||||
private static ScriptEngineFactory factory;
|
|
||||||
@Getter private static Serializer serializer;
|
@Getter private static Serializer serializer;
|
||||||
@Getter private static ScriptLib scriptLib;
|
@Getter private static ScriptLib scriptLib;
|
||||||
@Getter private static LuaValue scriptLibLua;
|
@Getter private static LuaValue scriptLibLua;
|
||||||
/** suggest GC to remove it if the memory is less */
|
/** 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 =
|
private static Map<String, SoftReference<CompiledScript>> scriptsCache =
|
||||||
new ConcurrentHashMap<>();
|
new ConcurrentHashMap<>();
|
||||||
/** sceneId - SceneMeta */
|
/** sceneId - SceneMeta */
|
||||||
private static Map<Integer, SoftReference<SceneMeta>> sceneMetaCache = new ConcurrentHashMap<>();
|
private static Map<Integer, SoftReference<SceneMeta>> sceneMetaCache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private static final AtomicReference<Bindings> currentBindings = new AtomicReference<>(null);
|
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 {
|
public static synchronized void init() throws Exception {
|
||||||
if (sm != null) {
|
if (sm != null) {
|
||||||
throw new Exception("Script loader already initialized");
|
throw new Exception("Script loader already initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create script engine
|
// Create script engine
|
||||||
sm = new ScriptEngineManager();
|
ScriptLoader.sm = new ScriptEngineManager();
|
||||||
engine = sm.getEngineByName("luaj");
|
var engine = ScriptLoader.engine = (LuaScriptEngine) sm.getEngineByName("luaj");
|
||||||
factory = getEngine().getFactory();
|
ScriptLoader.serializer = new LuaSerializer();
|
||||||
|
|
||||||
// Lua stuff
|
// Set the Lua context.
|
||||||
serializer = new LuaSerializer();
|
var ctx = new LuajContext(true, false);
|
||||||
var ctx = (LuajContext) engine.getContext();
|
ctx.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
|
||||||
|
engine.setContext(ctx);
|
||||||
|
|
||||||
// Set the 'require' function handler.
|
// Set the 'require' function handler.
|
||||||
ctx.globals.set("require", new RequireFunction());
|
ctx.globals.set("require", new RequireFunction());
|
||||||
@ -141,6 +150,7 @@ public class ScriptLoader {
|
|||||||
// Append the script to the context.
|
// Append the script to the context.
|
||||||
try {
|
try {
|
||||||
var bindings = currentBindings.get();
|
var bindings = currentBindings.get();
|
||||||
|
|
||||||
if (bindings != null) {
|
if (bindings != null) {
|
||||||
ScriptLoader.eval(script, bindings);
|
ScriptLoader.eval(script, bindings);
|
||||||
} else {
|
} 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) {
|
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()) {
|
if (sc.isPresent()) {
|
||||||
return sc.get();
|
return sc.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grasscutter.getLogger().debug("Loading script " + path);
|
try {
|
||||||
final Path scriptPath = FileUtils.getScriptPath(path);
|
CompiledScript script;
|
||||||
|
if (Configuration.FAST_REQUIRE) {
|
||||||
|
// Attempt to load the script.
|
||||||
|
var scriptPath = FileUtils.getScriptPath(path);
|
||||||
if (!Files.exists(scriptPath)) return null;
|
if (!Files.exists(scriptPath)) return null;
|
||||||
|
|
||||||
try {
|
// Compile the script from the file.
|
||||||
var script = ((Compilable) getEngine()).compile(Files.newBufferedReader(scriptPath));
|
var source = Files.newBufferedReader(scriptPath);
|
||||||
scriptsCache.put(path, new SoftReference<>(script));
|
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;
|
return script;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Grasscutter.getLogger()
|
Grasscutter.getLogger()
|
||||||
|
@ -3,12 +3,13 @@ package emu.grasscutter.scripts.data;
|
|||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.game.world.Position;
|
import emu.grasscutter.game.world.Position;
|
||||||
import emu.grasscutter.scripts.ScriptLoader;
|
import emu.grasscutter.scripts.ScriptLoader;
|
||||||
import java.util.*;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import javax.script.*;
|
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import org.luaj.vm2.*;
|
import org.luaj.vm2.*;
|
||||||
|
|
||||||
|
import javax.script.*;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@ToString
|
@ToString
|
||||||
@Setter
|
@Setter
|
||||||
public final class SceneGroup {
|
public final class SceneGroup {
|
||||||
@ -35,7 +36,8 @@ public final class SceneGroup {
|
|||||||
|
|
||||||
public SceneReplaceable is_replaceable;
|
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 CompiledScript script;
|
||||||
private transient Bindings bindings;
|
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.
|
// Set flag here so if there is no script, we don't call this function over and over again.
|
||||||
this.setLoaded(true);
|
this.setLoaded(true);
|
||||||
|
// Create the bindings.
|
||||||
this.bindings = ScriptLoader.getEngine().createBindings();
|
this.bindings = ScriptLoader.getEngine().createBindings();
|
||||||
|
|
||||||
var cs =
|
var cs =
|
||||||
|
Loading…
x
Reference in New Issue
Block a user