mirror of
https://github.com/Anime-Game-Servers/AnimeGamesLua.git
synced 2024-11-23 04:19:41 +00:00
[Refactoring/Fix] Some fixes and refactorings
* Fixed set parsing * Added a way to work around lua require differences between the lua standard and the game scripts usage * Added some more ScriptLibDefinitions * Started moving to kotlinx.io where possible
This commit is contained in:
parent
6c013a7557
commit
21b161f62b
@ -53,7 +53,7 @@ java {
|
||||
publishing {
|
||||
publications {
|
||||
create<MavenPublication>("maven") {
|
||||
from(components["kotlin"])
|
||||
from(components["java"])
|
||||
groupId = "org.anime_game_servers.lua"
|
||||
artifactId = "GIlua"
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ public class SceneGroupInfo {
|
||||
@Nullable @LuaNames("is_replaceable")
|
||||
private SceneReplaceable isReplaceable;
|
||||
@LuaNames("dynamic_load")
|
||||
private final boolean dynamicLoad = false;
|
||||
private boolean dynamicLoad = false;
|
||||
@Nullable
|
||||
private SceneBusiness business;
|
||||
@Nullable @LuaNames("life_cycle")
|
||||
|
@ -1,5 +1,6 @@
|
||||
package org.anime_game_servers.gi_lua.script_lib
|
||||
|
||||
import org.anime_game_servers.core.base.annotations.lua.LuaNames
|
||||
import org.anime_game_servers.gi_lua.models.ScriptArgs
|
||||
import org.anime_game_servers.gi_lua.models.scene.group.SceneGroup
|
||||
|
||||
@ -9,9 +10,11 @@ interface GroupEventLuaContext : LuaContext {
|
||||
|
||||
|
||||
// fields used by some scripts
|
||||
/*int uid();
|
||||
int source_entity_id();
|
||||
int target_entity_id();*/
|
||||
val uid: Int
|
||||
@LuaNames("source_entity_id")
|
||||
val sourceEntityId: Int
|
||||
@LuaNames("target_entity_id")
|
||||
val targetEntityId: Int
|
||||
/*override fun uid() = getArgs().uid
|
||||
override fun source_entity_id() = getArgs().source_eid
|
||||
override fun target_entity_id() = getArgs().target_eid*/
|
||||
|
@ -215,7 +215,7 @@ public class ScriptLib {
|
||||
return context.getScriptLibHandler().GetRegionEntityCount(context, regionId, entityTypeEnum);
|
||||
}
|
||||
|
||||
public int GetRegionConfigId(GroupEventLuaContext context, Object rawTable){
|
||||
public static int GetRegionConfigId(GroupEventLuaContext context, Object rawTable){
|
||||
val table = context.getEngine().getTable(rawTable);
|
||||
val regionEid = table.getInt("region_eid");
|
||||
return context.getScriptLibHandler().GetRegionConfigId(context, regionEid);
|
||||
@ -773,7 +773,11 @@ public class ScriptLib {
|
||||
return context.getScriptLibHandler().BeginCameraSceneLook(context, sceneLookParams);
|
||||
}
|
||||
|
||||
public int ClearPlayerEyePoint(GroupEventLuaContext context, int var1){
|
||||
public static int SetPlayerEyePointStream(GroupEventLuaContext context, int var1, int var2, boolean var3){
|
||||
return context.getScriptLibHandler().SetPlayerEyePointStream(context, var1, var2, var3);
|
||||
}
|
||||
|
||||
public static int ClearPlayerEyePoint(GroupEventLuaContext context, int var1){
|
||||
return context.getScriptLibHandler().ClearPlayerEyePoint(context, var1);
|
||||
}
|
||||
|
||||
@ -1073,6 +1077,10 @@ public class ScriptLib {
|
||||
return context.getScriptLibHandler().AssignPlayerUidOpNotify(context, param1);
|
||||
}
|
||||
|
||||
public static int CreateTreasureMapSpotRewardGadget(GroupEventLuaContext context, int gadgetCfgId){
|
||||
return context.getScriptLibHandler().CreateTreasureMapSpotRewardGadget(context, gadgetCfgId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* TODO better parameter handling and verify active handling
|
||||
@ -1199,12 +1207,21 @@ public class ScriptLib {
|
||||
return handler.GetContextGroupId(context);
|
||||
}
|
||||
|
||||
public static int SetGadgetEnableInteract(ControllerLuaContext<Object> context, int groupId, int configId, boolean enable) {
|
||||
val handler = context.getScriptLibHandlerProvider().getGadgetControllerHandler();
|
||||
if(handler == null){
|
||||
return NOT_IMPLEMENTED.getValue();
|
||||
public static int SetGadgetEnableInteract(LuaContext context, int groupId, int configId, boolean enable) {
|
||||
if(context instanceof ControllerLuaContext cContext){
|
||||
val handler = cContext.getScriptLibHandlerProvider().getGadgetControllerHandler();
|
||||
if(handler == null){
|
||||
return NOT_IMPLEMENTED.getValue();
|
||||
}
|
||||
return handler.SetGadgetEnableInteract(cContext, groupId, configId, enable);
|
||||
} else if(context instanceof GroupEventLuaContext gContext){
|
||||
val handler = gContext.getScriptLibHandlerProvider().getScriptLibHandler();
|
||||
if(handler == null){
|
||||
return NOT_IMPLEMENTED.getValue();
|
||||
}
|
||||
return handler.SetGadgetEnableInteract(gContext, groupId, configId, enable);
|
||||
}
|
||||
return handler.SetGadgetEnableInteract(context, groupId, configId, enable);
|
||||
return NOT_IMPLEMENTED.getValue();
|
||||
}
|
||||
|
||||
public static int DropSubfield(ControllerLuaContext<Object> context, Object paramsTable) {
|
||||
|
@ -22,6 +22,11 @@ public interface ScriptLibHandler<GroupEventContext extends GroupEventLuaContext
|
||||
void PrintGroupWarning(LuaContext context, String msg);
|
||||
int SetGadgetStateByConfigId(GroupEventContext context, int configId, int gadgetState);
|
||||
int SetGroupGadgetStateByConfigId(GroupEventContext context, int groupId, int configId, int gadgetState);
|
||||
|
||||
/**
|
||||
* Sets a gadget interacteable based on the config id and group id.
|
||||
*/
|
||||
int SetGadgetEnableInteract(GroupEventContext context, int groupId, int configId, boolean enable);
|
||||
int SetWorktopOptionsByGroupId(GroupEventContext context, int groupId, int configId, LuaTable options);
|
||||
int SetWorktopOptions(GroupEventContext context, LuaTable table);
|
||||
|
||||
@ -257,7 +262,8 @@ public interface ScriptLibHandler<GroupEventContext extends GroupEventLuaContext
|
||||
|
||||
int BeginCameraSceneLook(GroupEventContext context, LuaTable sceneLookParamsTable);
|
||||
|
||||
public int ClearPlayerEyePoint(GroupEventContext context, int var1);
|
||||
int SetPlayerEyePointStream(GroupEventContext context, int var1, int var2, boolean var3);
|
||||
int ClearPlayerEyePoint(GroupEventContext context, int var1);
|
||||
|
||||
int ShowReminderRadius(GroupEventContext context, int var1, LuaTable var2, int var3);
|
||||
int ShowClientGuide(GroupEventContext context, String guideName);
|
||||
@ -407,6 +413,8 @@ public interface ScriptLibHandler<GroupEventContext extends GroupEventLuaContext
|
||||
*/
|
||||
int AssignPlayerUidOpNotify(GroupEventContext context, LuaTable param1Table);
|
||||
|
||||
int CreateTreasureMapSpotRewardGadget(GroupEventContext context, int gadgetCfgId);
|
||||
|
||||
/**
|
||||
* TODO better parameter handling and verify active handling
|
||||
* Calls a lua function in the specified group if the group is active. The call parameters are passed to the called parameters like this:
|
||||
|
@ -24,6 +24,7 @@ dependencies {
|
||||
annotationProcessor(libs.jvm.lombok)
|
||||
implementation(libs.bundles.jvm.reflection)
|
||||
implementation(libs.jvm.logging)
|
||||
implementation(libs.jvm.kotlinx.io.core)
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
@ -46,7 +47,7 @@ java {
|
||||
publishing {
|
||||
publications {
|
||||
create<MavenPublication>("maven") {
|
||||
from(components["kotlin"])
|
||||
from(components["java"])
|
||||
artifactId = "JNLuaEngine"
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,9 @@ package org.anime_game_servers.jnlua_engine;
|
||||
|
||||
import io.github.oshai.kotlinlogging.KLogger;
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging;
|
||||
import kotlinx.io.SourcesJvmKt;
|
||||
import lombok.val;
|
||||
import org.anime_game_servers.lua.engine.RequireMode;
|
||||
import org.anime_game_servers.lua.engine.ScriptConfig;
|
||||
import org.terasology.jnlua.LuaState;
|
||||
import org.terasology.jnlua.NamedJavaFunction;
|
||||
@ -30,21 +32,26 @@ public class JNLuaRequireCommonFunction implements NamedJavaFunction {
|
||||
|
||||
@Override
|
||||
public int invoke(LuaState luaState) {
|
||||
if(scriptConfig.getEnableIncludeWorkaround() == RequireMode.DISABLED){
|
||||
return 0;
|
||||
}
|
||||
|
||||
val requiredName = luaState.checkString(1);
|
||||
luaState.remove(1);
|
||||
val params = scriptConfig.getScriptLoader().getRequireScriptParams(requiredName);
|
||||
val includeScript = scriptConfig.getScriptLoader().openScript(params);
|
||||
if (includeScript == null) {
|
||||
logger.error(() -> "Require script not found. " + params.getBasePath());
|
||||
return 1;
|
||||
}
|
||||
try {
|
||||
luaState.load(includeScript, requiredName, "t");
|
||||
try (val includeScript = scriptConfig.getScriptLoader().openScript(params);) {
|
||||
if (includeScript == null) {
|
||||
logger.error(() -> "Require script not found. " + params.getBasePath());
|
||||
return 1;
|
||||
}
|
||||
val stream = SourcesJvmKt.asInputStream(includeScript);
|
||||
luaState.load(stream, requiredName, "t");
|
||||
luaState.call(0, 0);
|
||||
} catch (IOException e) {
|
||||
logger.error(e, ()-> "Error on loading require script. " + params.getBasePath());
|
||||
return 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,16 @@
|
||||
package org.anime_game_servers.jnlua_engine;
|
||||
|
||||
import io.github.oshai.kotlinlogging.KLogger;
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging;
|
||||
import kotlin.Pair;
|
||||
import lombok.val;
|
||||
import org.anime_game_servers.lua.engine.LuaEngine;
|
||||
import org.anime_game_servers.lua.engine.LuaScript;
|
||||
import org.anime_game_servers.lua.engine.LuaValue;
|
||||
import org.anime_game_servers.lua.engine.RequireMode;
|
||||
import org.anime_game_servers.lua.models.BooleanLuaValue;
|
||||
import org.anime_game_servers.lua.models.IntLuaValue;
|
||||
import org.anime_game_servers.lua.models.MutableBoolean;
|
||||
import org.anime_game_servers.lua.models.ScriptType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -14,18 +19,23 @@ import org.terasology.jnlua.script.LuaBindings;
|
||||
import org.terasology.jnlua.script.LuaScriptEngine;
|
||||
|
||||
import javax.script.*;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class JNLuaScript implements LuaScript {
|
||||
private static KLogger logger = KotlinLogging.INSTANCE.logger(JNLuaScript.class.getName());
|
||||
|
||||
final Reader scriptReader;
|
||||
@Nullable
|
||||
private final CompiledLuaScript compiledScript;
|
||||
private final String modifiedScript;
|
||||
private final LuaBindings binding;
|
||||
private final JNLuaEngine engine;
|
||||
private final LuaScriptEngine scriptEngine;
|
||||
@ -44,14 +54,60 @@ public class JNLuaScript implements LuaScript {
|
||||
|
||||
val requireFunction = JNLuaRequireCommonFunction.getInstance(engine.getScriptConfig());
|
||||
binding.put(requireFunction.getName(), requireFunction);
|
||||
scriptReader = Files.newBufferedReader(scriptPath);
|
||||
val reader = Files.newBufferedReader(scriptPath);
|
||||
this.scriptReader = reader;
|
||||
if (engine.getScriptConfig().getEnableIncludeWorkaround() == RequireMode.ENABLED_WITH_WORKAROUND &&
|
||||
(scriptType == ScriptType.EXECUTABLE || scriptType == ScriptType.STATIC_EXECUTABLE || scriptType == ScriptType.ONE_TIME_EXECUTABLE)) {
|
||||
this.modifiedScript = compileScriptWithWorkaround(reader, scriptPath);
|
||||
} else {
|
||||
this.modifiedScript = null;
|
||||
}
|
||||
|
||||
if(scriptType.getPrecompile()) {
|
||||
this.compiledScript = (CompiledLuaScript) ((Compilable) scriptEngine).compile(scriptReader);
|
||||
if(modifiedScript != null) {
|
||||
this.compiledScript = (CompiledLuaScript) ((Compilable) scriptEngine).compile(modifiedScript);
|
||||
} else {
|
||||
this.compiledScript = (CompiledLuaScript) ((Compilable) scriptEngine).compile(scriptReader);
|
||||
}
|
||||
} else {
|
||||
this.compiledScript = null;
|
||||
}
|
||||
}
|
||||
|
||||
// todo maybe caching?
|
||||
private String compileScriptWithWorkaround(BufferedReader reader, Path path) throws IOException,ScriptException {
|
||||
val requireRegex = Pattern.compile("\\s*require\\s+\"(.*)\"");
|
||||
val changed = new MutableBoolean(false);
|
||||
try {
|
||||
val script = reader.lines().map(line -> {
|
||||
val result = requireRegex.matcher(line);
|
||||
if (result.matches()) {
|
||||
val requireBasePath = engine.getScriptConfig().getScriptLoader().getRequireScriptParams(result.group(1)).getBasePath();
|
||||
val requirePath = engine.getScriptConfig().getScriptLoader().getScriptPath(requireBasePath);
|
||||
if(requirePath == null){
|
||||
logger.warn(()->"Could not find require script "+result.group(1)+" for script "+ path);
|
||||
return line;
|
||||
}
|
||||
try {
|
||||
try (val requireReader = Files.newBufferedReader(requirePath)){
|
||||
val requireScript = requireReader.lines().reduce((a, b) -> a + "\n" + b).orElse(line);
|
||||
changed.setValue(true);
|
||||
return requireScript;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return line;
|
||||
}
|
||||
} else {
|
||||
return line;
|
||||
}
|
||||
}).reduce((a, b) -> a + "\n" + b).orElse("");
|
||||
return changed.getValue() ? script : null;
|
||||
}
|
||||
catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMethod(@NotNull String methodName) {
|
||||
return binding.containsKey(methodName);
|
||||
@ -75,7 +131,11 @@ public class JNLuaScript implements LuaScript {
|
||||
if(compiledScript != null) {
|
||||
compiledScript.eval(context);
|
||||
} else {
|
||||
scriptEngine.eval(scriptReader, context);
|
||||
if(modifiedScript != null) {
|
||||
scriptEngine.eval(modifiedScript, context);
|
||||
} else {
|
||||
scriptEngine.eval(scriptReader, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ package org.anime_game_servers.jnlua_engine;
|
||||
|
||||
import io.github.oshai.kotlinlogging.KLogger;
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging;
|
||||
import lombok.val;
|
||||
import org.anime_game_servers.lua.serialize.BaseSerializer;
|
||||
import org.terasology.jnlua.LuaValueProxy;
|
||||
import org.terasology.jnlua.util.AbstractTableMap;
|
||||
@ -10,10 +11,7 @@ import javax.annotation.Nullable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class JNLuaSerializer extends BaseSerializer {
|
||||
@ -52,11 +50,11 @@ public class JNLuaSerializer extends BaseSerializer {
|
||||
|
||||
@Override
|
||||
public <T> Map<String, T> toMap(Class<T> type, Object obj) {
|
||||
return serializeMap(type, (LuaValueProxy) obj);
|
||||
return serializeMap(String.class, type, (LuaValueProxy) obj);
|
||||
}
|
||||
|
||||
private <T> T objectToClass(Class<T> type, Object value) {
|
||||
T object = null;
|
||||
Object object = null;
|
||||
|
||||
if (value instanceof Integer) {
|
||||
object = (T) getInt(value);
|
||||
@ -69,14 +67,24 @@ public class JNLuaSerializer extends BaseSerializer {
|
||||
} else {
|
||||
object = serialize(type, null, (LuaValueProxy) value);
|
||||
}
|
||||
return object;
|
||||
if(String.class.isAssignableFrom(type)){
|
||||
return (T) String.valueOf(object);
|
||||
} else {
|
||||
return (T) object;
|
||||
}
|
||||
}
|
||||
|
||||
public <T> List<T> serializeList(Class<T> type, LuaValueProxy table) {
|
||||
List<T> list = new ArrayList<>();
|
||||
private <T> List<T> serializeList(Class<T> type, LuaValueProxy table) {
|
||||
return serializeCollection(type, new ArrayList<>(), table);
|
||||
}
|
||||
private <T> Set<T> serializeSet(Class<T> type, LuaValueProxy table) {
|
||||
return serializeCollection(type, new HashSet<>(), table);
|
||||
}
|
||||
|
||||
public <T, Y extends Collection<T>> Y serializeCollection(Class<T> type, Y target, LuaValueProxy table) {
|
||||
|
||||
if (table == null) {
|
||||
return list;
|
||||
return target;
|
||||
}
|
||||
|
||||
var tableObj = (Map<String, Object>) table;
|
||||
@ -88,7 +96,7 @@ public class JNLuaSerializer extends BaseSerializer {
|
||||
T object = objectToClass(type, keyValue);
|
||||
|
||||
if (object != null) {
|
||||
list.add(object);
|
||||
target.add(object);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
|
||||
@ -98,19 +106,7 @@ public class JNLuaSerializer extends BaseSerializer {
|
||||
logger.error(e, () -> "Exception serializing list");
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private Class<?> getListType(Class<?> type, @Nullable Field field){
|
||||
if(field == null){
|
||||
return type.getTypeParameters()[0].getClass();
|
||||
}
|
||||
Type fieldType = field.getGenericType();
|
||||
if(fieldType instanceof ParameterizedType){
|
||||
return (Class<?>) ((ParameterizedType) fieldType).getActualTypeArguments()[0];
|
||||
}
|
||||
|
||||
return null;
|
||||
return target;
|
||||
}
|
||||
|
||||
public <T> T serialize(Class<T> type, @Nullable Field field, LuaValueProxy table) {
|
||||
@ -118,13 +114,34 @@ public class JNLuaSerializer extends BaseSerializer {
|
||||
|
||||
if (type == List.class) {
|
||||
try {
|
||||
Class<?> listType = getListType(type, field);
|
||||
Class<?> listType = getCollectionType(type, field);
|
||||
return (T) serializeList(listType, table);
|
||||
} catch (Exception e) {
|
||||
logger.error(e, ()->"Exception serializing");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (type == Set.class) {
|
||||
try {
|
||||
Class<?> listType = getCollectionType(type, field);
|
||||
return (T) serializeSet(listType, table);
|
||||
} catch (Exception e) {
|
||||
logger.error("Exception while serializing {}", type.getName(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (type == Map.class) {
|
||||
try {
|
||||
val mapTypes = getMapTypes(type, field);
|
||||
if(mapTypes == null){
|
||||
return null;
|
||||
}
|
||||
return (T) serializeMap(mapTypes.component1(), mapTypes.component2(), table);
|
||||
} catch (Exception e) {
|
||||
logger.error("Exception while serializing {}", type.getName(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (!fieldMetaCache.containsKey(type)) {
|
||||
@ -161,7 +178,7 @@ public class JNLuaSerializer extends BaseSerializer {
|
||||
set(object, fieldMeta, methodAccess, (boolean) keyValue);
|
||||
} else if (fieldMeta.getType().equals(List.class)) {
|
||||
LuaValueProxy objTable = (LuaValueProxy) tableObj.get(k.getKey());
|
||||
Class<?> listType = getListType(type, fieldMeta.getField());
|
||||
Class<?> listType = getCollectionType(type, fieldMeta.getField());
|
||||
List<?> listObj = serializeList(listType, objTable);
|
||||
set(object, fieldMeta, methodAccess, listObj);
|
||||
} else {
|
||||
@ -178,25 +195,31 @@ public class JNLuaSerializer extends BaseSerializer {
|
||||
return object;
|
||||
}
|
||||
|
||||
public <T> Map<String, T> serializeMap(Class<T> type, LuaValueProxy table) {
|
||||
Map<String, T> map = new HashMap<>();
|
||||
public <K,V> Map<K, V> serializeMap(Class<K> typeKey, Class<V> typeValue, LuaValueProxy table) {
|
||||
Map<K, V> map = new HashMap<>();
|
||||
|
||||
if (table == null) {
|
||||
return map;
|
||||
}
|
||||
|
||||
var tableObj = (Map<String, Object>) table;
|
||||
var tableObj = (Map<Object, Object>) table;
|
||||
|
||||
try {
|
||||
for (var k : tableObj.entrySet()) {
|
||||
try {
|
||||
var keyValue = k.getValue();
|
||||
|
||||
T object = objectToClass(type, keyValue);
|
||||
|
||||
if (object != null) {
|
||||
map.put(k.getKey(), object);
|
||||
K key = objectToClass(typeKey, k.getKey());
|
||||
if(key == null){
|
||||
logger.warn(() -> "Can't serialize key: "+key+" to type: "+typeKey);
|
||||
continue;
|
||||
}
|
||||
|
||||
V value = objectToClass(typeValue, k.getValue());
|
||||
if(value == null){
|
||||
logger.warn(() -> "Can't serialize value: "+value+" to type: "+typeValue);
|
||||
continue;
|
||||
}
|
||||
|
||||
map.put(key, value);
|
||||
} catch (Exception ex) {
|
||||
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
package org.anime_game_servers.luaj_engine
|
||||
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging.logger
|
||||
import org.anime_game_servers.lua.engine.LuaEngine
|
||||
import org.anime_game_servers.lua.engine.LuaScript
|
||||
import org.anime_game_servers.lua.engine.LuaTable
|
||||
import org.anime_game_servers.lua.engine.ScriptConfig
|
||||
import kotlinx.io.asInputStream
|
||||
import kotlinx.io.buffered
|
||||
import org.anime_game_servers.lua.engine.*
|
||||
import org.anime_game_servers.lua.models.ScriptType
|
||||
import org.luaj.vm2.lib.ResourceFinder
|
||||
import org.luaj.vm2.lib.jse.CoerceJavaToLua
|
||||
@ -34,8 +33,11 @@ class LuaJEngine(override val scriptConfig: ScriptConfig) : LuaEngine {
|
||||
// Set engine to replace require as a temporary fix to missing scripts
|
||||
context.globals.finder = object : ResourceFinder {
|
||||
override fun findResource(filename: String): InputStream {
|
||||
if (scriptConfig.enableIncludeWorkaround == RequireMode.DISABLED)
|
||||
return ByteArrayInputStream(ByteArray(0))
|
||||
|
||||
val params = scriptConfig.scriptLoader.getRequireScriptParams(filename)
|
||||
val stream = scriptConfig.scriptLoader.openScript(params)
|
||||
val stream = scriptConfig.scriptLoader.openScript(params)?.buffered()?.asInputStream()
|
||||
return stream ?: ByteArrayInputStream(ByteArray(0))
|
||||
}
|
||||
|
||||
@ -82,7 +84,7 @@ class LuaJEngine(override val scriptConfig: ScriptConfig) : LuaEngine {
|
||||
if (!Files.exists(scriptPath)) return null
|
||||
|
||||
try {
|
||||
return LuaJScript(this, scriptPath)
|
||||
return LuaJScript(this, scriptPath, scriptType)
|
||||
} catch (e: IOException) {
|
||||
throw RuntimeException(e)
|
||||
} catch (e: ScriptException) {
|
||||
|
@ -2,11 +2,16 @@ package org.anime_game_servers.luaj_engine;
|
||||
|
||||
import io.github.oshai.kotlinlogging.KLogger;
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging;
|
||||
import kotlin.Pair;
|
||||
import kotlin.text.Regex;
|
||||
import lombok.val;
|
||||
import org.anime_game_servers.lua.engine.LuaEngine;
|
||||
import org.anime_game_servers.lua.engine.LuaScript;
|
||||
import org.anime_game_servers.lua.engine.LuaValue;
|
||||
import org.anime_game_servers.lua.engine.RequireMode;
|
||||
import org.anime_game_servers.lua.models.BooleanLuaValue;
|
||||
import org.anime_game_servers.lua.models.MutableBoolean;
|
||||
import org.anime_game_servers.lua.models.ScriptType;
|
||||
import org.luaj.vm2.lib.jse.CoerceJavaToLua;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
@ -19,19 +24,61 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class LuaJScript implements LuaScript {
|
||||
private static KLogger logger = KotlinLogging.INSTANCE.logger(LuaJScript.class.getName());
|
||||
private final CompiledScript compiledScript;
|
||||
private final String modifiedScript;
|
||||
private final Bindings binding;
|
||||
private final LuaJEngine engine;
|
||||
|
||||
public LuaJScript(LuaJEngine engine, Path scriptPath) throws IOException, ScriptException {
|
||||
public LuaJScript(LuaJEngine engine, Path scriptPath, ScriptType scriptType) throws IOException, ScriptException {
|
||||
this.engine = engine;
|
||||
this.compiledScript = ((Compilable) engine.getEngine()).compile(Files.newBufferedReader(scriptPath));
|
||||
if (engine.getScriptConfig().getEnableIncludeWorkaround() == RequireMode.ENABLED_WITH_WORKAROUND && (scriptType == ScriptType.EXECUTABLE || scriptType == ScriptType.STATIC_EXECUTABLE || scriptType == ScriptType.ONE_TIME_EXECUTABLE)) {
|
||||
val result = compileScriptWithWorkaround(scriptPath);
|
||||
this.compiledScript = result.getFirst();
|
||||
this.modifiedScript = result.getSecond();
|
||||
} else {
|
||||
this.compiledScript = ((Compilable) engine.getEngine()).compile(Files.newBufferedReader(scriptPath));
|
||||
this.modifiedScript = null;
|
||||
}
|
||||
this.binding = engine.getEngine().createBindings();
|
||||
}
|
||||
|
||||
// todo maybe caching?
|
||||
private Pair<CompiledScript, String> compileScriptWithWorkaround(Path path) throws IOException,ScriptException {
|
||||
val requireRegex = Pattern.compile("\\s*require\\s+\"(.*)\"");
|
||||
val changed = new MutableBoolean(false);
|
||||
try (val reader = Files.newBufferedReader(path)){
|
||||
val script = reader.lines().map(line -> {
|
||||
val result = requireRegex.matcher(line);
|
||||
if (result.matches()) {
|
||||
val requireBasePath = engine.getScriptConfig().getScriptLoader().getRequireScriptParams(result.group(1)).getBasePath();
|
||||
val requirePath = engine.getScriptConfig().getScriptLoader().getScriptPath(requireBasePath);
|
||||
if(requirePath == null){
|
||||
logger.warn(()->"Could not find require script "+result.group(1)+" for script "+path);
|
||||
return line;
|
||||
}
|
||||
try {
|
||||
try (val requireReader = Files.newBufferedReader(requirePath)){
|
||||
val requireScript = requireReader.lines().reduce((a, b) -> a + "\n" + b).orElse(line);
|
||||
changed.setValue(true);
|
||||
return requireScript;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return line;
|
||||
}
|
||||
} else {
|
||||
return line;
|
||||
}
|
||||
}).reduce((a, b) -> a + "\n" + b).orElse("");
|
||||
String modifiedScript = changed.getValue() ? script : null;
|
||||
return new Pair<>(((Compilable) engine.getEngine()).compile(script), modifiedScript);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMethod(@Nonnull String methodName) {
|
||||
return binding.containsKey(methodName) && ((org.luaj.vm2.LuaValue) binding.get(methodName)).isfunction();
|
||||
|
@ -13,10 +13,7 @@ import javax.annotation.Nullable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
public class LuaJSerializer extends BaseSerializer {
|
||||
private static KLogger logger = KotlinLogging.INSTANCE.logger(LuaJEngine.class.getName());
|
||||
@ -101,11 +98,19 @@ public class LuaJSerializer extends BaseSerializer {
|
||||
return map;
|
||||
}
|
||||
|
||||
public <T> List<T> serializeList(Class<T> type, LuaTable table) {
|
||||
List<T> list = new ArrayList<>();
|
||||
private <T> List<T> serializeList(Class<T> type, LuaTable table) {
|
||||
val startSize = table != null ? table.length() : 0;
|
||||
return serializeCollection(type, new ArrayList<>(startSize), table);
|
||||
}
|
||||
private <T> Set<T> serializeSet(Class<T> type, LuaTable table) {
|
||||
val startSize = table != null ? table.length() : 0;
|
||||
return serializeCollection(type, new HashSet<>(startSize), table);
|
||||
}
|
||||
|
||||
public <T, Y extends Collection<T>> Y serializeCollection(Class<T> type, Y target, LuaTable table) {
|
||||
|
||||
if (table == null) {
|
||||
return list;
|
||||
return target;
|
||||
}
|
||||
|
||||
try {
|
||||
@ -117,7 +122,7 @@ public class LuaJSerializer extends BaseSerializer {
|
||||
T object = valueToType(type, keyValue);
|
||||
|
||||
if (object != null) {
|
||||
list.add(object);
|
||||
target.add(object);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
|
||||
@ -127,47 +132,7 @@ public class LuaJSerializer extends BaseSerializer {
|
||||
logger.error(e, () -> "Exception while serializing list");
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private Class<?> getListType(Class<?> type, @Nullable Field field) {
|
||||
if (field == null) {
|
||||
return type.getTypeParameters()[0].getClass();
|
||||
}
|
||||
Type fieldType = field.getGenericType();
|
||||
if (fieldType instanceof ParameterizedType) {
|
||||
return (Class<?>) ((ParameterizedType) fieldType).getActualTypeArguments()[0];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
private Pair<Class<?>, Class<?>> getMapTypes(Class<?> type, @Nullable Field field) {
|
||||
Class<?> key = null;
|
||||
Class<?> value = null;
|
||||
if (field == null) {
|
||||
val types = type.getTypeParameters();
|
||||
if(types.length < 2){
|
||||
return null;
|
||||
}
|
||||
key = types.getClass();
|
||||
value = types.getClass();
|
||||
}
|
||||
else {
|
||||
Type fieldType = field.getGenericType();
|
||||
if (fieldType instanceof ParameterizedType) {
|
||||
val types = ((ParameterizedType) fieldType).getActualTypeArguments();
|
||||
if(types.length < 2){
|
||||
return null;
|
||||
}
|
||||
key = (Class<?>) types[0];
|
||||
value = (Class<?>) types[1];
|
||||
}
|
||||
}
|
||||
if(key!=null && value!=null){
|
||||
return new Pair<>(key, value);
|
||||
}
|
||||
|
||||
return null;
|
||||
return target;
|
||||
}
|
||||
|
||||
public <T> T serialize(Class<T> type, @Nullable Field field, LuaTable table) {
|
||||
@ -175,13 +140,22 @@ public class LuaJSerializer extends BaseSerializer {
|
||||
|
||||
if (type == List.class) {
|
||||
try {
|
||||
Class<?> listType = getListType(type, field);
|
||||
Class<?> listType = getCollectionType(type, field);
|
||||
return (T) serializeList(listType, table);
|
||||
} catch (Exception e) {
|
||||
logger.error("Exception while serializing {}", type.getName(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (type == Set.class) {
|
||||
try {
|
||||
Class<?> listType = getCollectionType(type, field);
|
||||
return (T) serializeSet(listType, table);
|
||||
} catch (Exception e) {
|
||||
logger.error("Exception while serializing {}", type.getName(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (type == Map.class) {
|
||||
try {
|
||||
val mapTypes = getMapTypes(type, field);
|
||||
|
@ -47,6 +47,7 @@ kotlin {
|
||||
implementation(libs.ags.core.base)
|
||||
implementation(libs.kotlin.reflect)
|
||||
implementation(libs.logging)
|
||||
api(libs.kotlinx.io.core)
|
||||
}
|
||||
}
|
||||
val commonTest by getting {
|
||||
@ -57,6 +58,7 @@ kotlin {
|
||||
implementation(libs.bundles.jvm.reflection)
|
||||
implementation(libs.findbugs.jsr305)
|
||||
implementation(libs.jvm.logging)
|
||||
api(libs.kotlinx.io.core)
|
||||
}
|
||||
}
|
||||
val jvmTest by getting
|
||||
|
@ -0,0 +1,11 @@
|
||||
package org.anime_game_servers.lua.models
|
||||
|
||||
class MutableBoolean(private var value: Boolean){
|
||||
fun getValue(): Boolean {
|
||||
return value
|
||||
}
|
||||
|
||||
fun setValue(value: Boolean) {
|
||||
this.value = value
|
||||
}
|
||||
}
|
@ -2,8 +2,8 @@ package org.anime_game_servers.lua.engine
|
||||
|
||||
import io.github.oshai.kotlinlogging.KLogger
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||
import kotlinx.io.Source
|
||||
import org.anime_game_servers.lua.models.ScriptType
|
||||
import java.io.InputStream
|
||||
import java.nio.file.Path
|
||||
import javax.script.ScriptException
|
||||
|
||||
@ -11,7 +11,7 @@ import javax.script.ScriptException
|
||||
interface BaseScriptLoader {
|
||||
fun getScriptPath(scriptName: String): Path?
|
||||
fun getRequireScriptParams(scriptName: String): ScriptLoadParams
|
||||
fun openScript(params: ScriptLoadParams): InputStream?
|
||||
fun openScript(params: ScriptLoadParams): Source?
|
||||
fun getScript(
|
||||
scriptLoadParams: ScriptLoadParams
|
||||
): LuaScript?
|
||||
|
@ -2,5 +2,22 @@ package org.anime_game_servers.lua.engine
|
||||
|
||||
data class ScriptConfig(
|
||||
val scriptLoader: BaseScriptLoader,
|
||||
val enableIncludeWorkaround: Boolean = false,
|
||||
val enableIncludeWorkaround: RequireMode = RequireMode.DISABLED,
|
||||
)
|
||||
|
||||
enum class RequireMode {
|
||||
/**
|
||||
* The include/require function is disabled.
|
||||
*/
|
||||
DISABLED,
|
||||
|
||||
/**
|
||||
* The include/require function is enabled.
|
||||
*/
|
||||
ENABLED,
|
||||
|
||||
/**
|
||||
* The include/require function is enabled and the workaround is enabled.
|
||||
*/
|
||||
ENABLED_WITH_WORKAROUND,
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ import com.esotericsoftware.reflectasm.MethodAccess
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging.logger
|
||||
import org.anime_game_servers.core.base.annotations.lua.LuaNames
|
||||
import java.lang.reflect.Field
|
||||
import java.lang.reflect.ParameterizedType
|
||||
import java.lang.reflect.TypeVariable
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import javax.annotation.Nonnull
|
||||
@ -163,6 +165,47 @@ abstract class BaseSerializer : Serializer {
|
||||
}
|
||||
}
|
||||
|
||||
protected fun getCollectionType(type: Class<*>, field: Field?): Class<*>? {
|
||||
if (field == null) {
|
||||
return type.typeParameters[0].javaClass
|
||||
}
|
||||
val fieldType = field.genericType
|
||||
if (fieldType is ParameterizedType) {
|
||||
return (fieldType as ParameterizedType).actualTypeArguments[0] as Class<*>
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
protected fun getMapTypes(type: Class<*>, field: Field?): Pair<Class<*>, Class<*>>? {
|
||||
var key: Class<*>? = null
|
||||
var value: Class<*>? = null
|
||||
if (field == null) {
|
||||
val types = type.getTypeParameters()
|
||||
if (types.size < 2) {
|
||||
return null
|
||||
}
|
||||
key = types.javaClass
|
||||
value = types.javaClass
|
||||
} else {
|
||||
val fieldType = field.genericType
|
||||
if (fieldType is ParameterizedType) {
|
||||
val types = fieldType.actualTypeArguments
|
||||
if (types.size < 2) {
|
||||
return null
|
||||
}
|
||||
key = types[0] as Class<*>
|
||||
value = types[1] as Class<*>
|
||||
}
|
||||
}
|
||||
|
||||
if (key != null && value != null) {
|
||||
return Pair(key, value)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = logger(BaseSerializer::class.java.name)
|
||||
|
||||
|
@ -0,0 +1,19 @@
|
||||
package org.anime_game_servers.lua.utils
|
||||
|
||||
import kotlinx.io.Source
|
||||
import kotlinx.io.asSource
|
||||
import kotlinx.io.buffered
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
|
||||
fun InputStream.asSource(): Source {
|
||||
return this.asSource().buffered()
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun Path.asSource(): Source {
|
||||
val stream = Files.newInputStream(this)
|
||||
return stream.asSource().buffered()
|
||||
}
|
@ -35,6 +35,7 @@ anime_game_lua = "0.1"
|
||||
kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" }
|
||||
jvm-kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" }
|
||||
kotlinx-io-core = { module = "org.jetbrains.kotlinx:kotlinx-io-core", version.ref = "kx_io" }
|
||||
jvm-kotlinx-io-core = { module = "org.jetbrains.kotlinx:kotlinx-io-core-jvm", version.ref = "kx_io" }
|
||||
logging = { module = "io.github.oshai:kotlin-logging", version.ref = "logger" }
|
||||
jvm-logging = { module = "io.github.oshai:kotlin-logging-jvm", version.ref = "logger" }
|
||||
reflectasm = { module = "com.esotericsoftware:reflectasm", version.ref = "reflectasm" }
|
||||
|
Loading…
Reference in New Issue
Block a user