[Refactoring] Made BaseLua multiplatform, renamed it to base and moved some annotations to the core repo

* Added LuaStatic annotation to allow auto loading of lua enums/models
* Added LuaNames annotation handling to allow alternate names in lua
* Moved some more Scriptlib handlers into its own model
* First draft of activity group handling
* Moved some engine classes to kotlin
This commit is contained in:
hartie95 2024-01-08 01:22:10 +01:00
parent 2d462753f2
commit 44caa6d85d
69 changed files with 1297 additions and 1013 deletions

1
.gitignore vendored
View File

@ -9,6 +9,7 @@ build/
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
.idea/sonarlint/
*.iws
*.iml
*.ipr

View File

@ -1,45 +0,0 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("java-library")
kotlin("jvm") version "1.9.10"
}
group = "org.anime_game_servers"
version = "0.1"
repositories {
mavenCentral()
}
dependencies {
testImplementation(platform("org.junit:junit-bom:5.9.1"))
testImplementation("org.junit.jupiter:junit-jupiter")
implementation("com.google.code.findbugs:jsr305:3.0.2")
implementation("com.esotericsoftware:reflectasm:1.11.9")
implementation(kotlin("stdlib-jdk8"))
implementation("io.github.oshai:kotlin-logging-jvm:5.1.0")
}
tasks.test {
useJUnitPlatform()
}
val compileKotlin: KotlinCompile by tasks
compileKotlin.kotlinOptions {
jvmTarget = "17"
}
val compileTestKotlin: KotlinCompile by tasks
compileTestKotlin.kotlinOptions {
jvmTarget = "17"
}
publishing {
publications {
create<MavenPublication>("maven") {
from(components["kotlin"])
artifactId = "BaseLua"
}
}
}

View File

@ -1,32 +0,0 @@
package org.anime_game_servers.lua.engine;
import org.anime_game_servers.lua.models.IntValueEnum;
import org.anime_game_servers.lua.models.ScriptType;
import org.anime_game_servers.lua.serialize.Serializer;
import java.nio.file.Path;
public interface LuaEngine {
ScriptConfig getScriptConfig();
<T extends Enum<T>> boolean addGlobalEnumByOrdinal(String name, T[] enumArray);
<T extends Enum<T> & IntValueEnum> boolean addGlobalEnumByIntValue(String name, T[] enumArray);
boolean addGlobalStaticClass(String name, Class<?> staticClass);
boolean addObject(String name, Object object);
Serializer getSerializer();
default LuaScript getScript(String scriptName, ScriptType scriptType){
final Path scriptPath = getScriptConfig().getScriptLoader().getScriptPath(scriptName);
return getScript(scriptPath, scriptType);
}
LuaScript getScript(Path scriptPath, ScriptType scriptType);
LuaTable getTable(Object table);
LuaTable createTable();
}

View File

@ -1,88 +0,0 @@
package org.anime_game_servers.lua.engine;
import javax.annotation.Nullable;
import java.util.Set;
public interface LuaTable {
boolean has(String key);
@Nullable
Object get(String key);
@Nullable
Object get(int key);
int getInt(String key);
int optInt(String key, int defaultValue);
int getInt(int key);
int optInt(int key, int defaultValue);
void set(int key, int value);
void set(String key, int value);
double getDouble(String key);
double optDouble(String key, double defaultValue);
double getDouble(int key);
double optDouble(int key, double defaultValue);
float getFloat(String key);
float optFloat(String key, float defaultValue);
float getFloat(int key);
float optFloat(int key, float defaultValue);
void set(int key, double value);
void set(String key, double value);
boolean getBoolean(String key);
boolean optBoolean(String key, boolean defaultValue);
boolean getBoolean(int key);
boolean optBoolean(int key, boolean defaultValue);
void set(int key, boolean value);
void set(String key, boolean value);
String getString(String key);
String optString(String key, String defaultValue);
String getString(int key);
String optString(int key, String defaultValue);
void set(int key, String value);
void set(String key, String value);
@Nullable
LuaTable getTable(String key);
@Nullable
LuaTable getTable(int key);
void set(int key, LuaTable value);
void set(String key, LuaTable value);
Set<String> getKeys();
int[] getAsIntArray();
int getSize();
Object getRawTable();
}

View File

@ -1,5 +0,0 @@
package org.anime_game_servers.lua.models
interface IntValueEnum {
fun getValue(): Int
}

View File

@ -1,145 +0,0 @@
package org.anime_game_servers.lua.serialize;
import com.esotericsoftware.reflectasm.ConstructorAccess;
import com.esotericsoftware.reflectasm.MethodAccess;
import io.github.oshai.kotlinlogging.KLogger;
import io.github.oshai.kotlinlogging.KotlinLogging;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public abstract class BaseSerializer implements Serializer {
private static KLogger logger = KotlinLogging.INSTANCE.logger(BaseSerializer.class.getName());
protected static final Map<Class<?>, MethodAccess> methodAccessCache = new ConcurrentHashMap<>();
protected static final Map<Class<?>, ConstructorAccess<?>> constructorCache = new ConcurrentHashMap<>();
protected static final Map<Class<?>, Map<String, FieldMeta>> fieldMetaCache = new ConcurrentHashMap<>();
public abstract <T> List<T> toList(Class<T> type, Object obj);
public abstract <T> T toObject(Class<T> type, Object obj);
public abstract <T> Map<String,T> toMap(Class<T> type, Object obj);
protected String getSetterName(String fieldName) {
if (fieldName == null || fieldName.length() == 0) {
return null;
}
if (fieldName.length() == 1) {
return "set" + fieldName.toUpperCase();
}
return "set" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
}
protected <T> Map<String, FieldMeta> cacheType(Class<T> type) {
if (fieldMetaCache.containsKey(type)) {
return fieldMetaCache.get(type);
}
if (!constructorCache.containsKey(type)) {
constructorCache.putIfAbsent(type, ConstructorAccess.get(type));
}
var methodAccess = Optional.ofNullable(methodAccessCache.get(type)).orElse(MethodAccess.get(type));
methodAccessCache.putIfAbsent(type, methodAccess);
var fieldMetaMap = new HashMap<String, FieldMeta>();
var methodNameSet = new HashSet<>(Arrays.stream(methodAccess.getMethodNames()).toList());
Class<?> classtype = type;
while(classtype!=null){
Arrays.stream(classtype.getDeclaredFields())
.forEach(field -> {
if(methodNameSet.contains(getSetterName(field.getName()))) {
var setter = getSetterName(field.getName());
var index = methodAccess.getIndex(setter);
fieldMetaMap.put(field.getName(), new FieldMeta(field.getName(), setter, index, field.getType(), field));
} else {
field.setAccessible(true);
fieldMetaMap.put(field.getName(), new FieldMeta(field.getName(), null, -1, field.getType(), field));
}
});
classtype = classtype.getSuperclass();
}
Arrays.stream(type.getFields())
.filter(field -> !fieldMetaMap.containsKey(field.getName()))
.filter(field -> methodNameSet.contains(getSetterName(field.getName())))
.forEach(field -> {
var setter = getSetterName(field.getName());
var index = methodAccess.getIndex(setter);
fieldMetaMap.put(field.getName(), new FieldMeta(field.getName(), setter, index, field.getType(), field));
});
fieldMetaCache.put(type, fieldMetaMap);
return fieldMetaMap;
}
protected void set(Object object, @Nonnull FieldMeta fieldMeta, @Nullable MethodAccess methodAccess, int value){
try {
if (methodAccess != null && fieldMeta.getSetter() != null) {
methodAccess.invoke(object, fieldMeta.getIndex(), value);
} else if(fieldMeta.getField() != null){
fieldMeta.getField().setInt(object, value);
}
} catch (Exception ex){
logger.warn(ex, () -> "Failed to set field "+fieldMeta.getName()+" of type "+fieldMeta.getType()+" to value "+ value);
}
}
protected void set(Object object, @Nonnull FieldMeta fieldMeta, @Nullable MethodAccess methodAccess, double value){
try {
if (methodAccess != null && fieldMeta.getSetter() != null) {
methodAccess.invoke(object, fieldMeta.getIndex(), value);
} else if(fieldMeta.getField() != null) {
fieldMeta.getField().setDouble(object, value);
}
} catch (Exception ex){
logger.warn(ex, () -> "Failed to set field "+fieldMeta.getName()+" of type "+fieldMeta.getType()+" to value "+ value);
}
}
protected void set(Object object, @Nonnull FieldMeta fieldMeta, @Nullable MethodAccess methodAccess, float value){
try {
if (methodAccess != null && fieldMeta.getSetter() != null) {
methodAccess.invoke(object, fieldMeta.getIndex(), value);
} else if(fieldMeta.getField() != null) {
fieldMeta.getField().setFloat(object, value);
}
} catch (Exception ex){
logger.warn(ex, () -> "Failed to set field "+fieldMeta.getName()+" of type "+fieldMeta.getType()+" to value "+ value);
}
}
protected void set(Object object, @Nonnull FieldMeta fieldMeta, @Nullable MethodAccess methodAccess, long value){
try {
if (methodAccess != null && fieldMeta.getSetter() != null) {
methodAccess.invoke(object, fieldMeta.getIndex(), value);
} else if(fieldMeta.getField() != null) {
fieldMeta.getField().setLong(object, value);
}
} catch (Exception ex){
logger.warn(ex, () -> "Failed to set field "+fieldMeta.getName()+" of type "+fieldMeta.getType()+" to value "+ value);
}
}
protected void set(Object object, @Nonnull FieldMeta fieldMeta, @Nullable MethodAccess methodAccess, boolean value){
try {
if (methodAccess != null && fieldMeta.getSetter() != null) {
methodAccess.invoke(object, fieldMeta.getIndex(), value);
} else if(fieldMeta.getField() != null) {
fieldMeta.getField().setBoolean(object, value);
}
} catch (Exception ex){
logger.warn(ex, () -> "Failed to set field "+fieldMeta.getName()+" of type "+fieldMeta.getType()+" to value "+ value);
}
}
protected void set(Object object, @Nonnull FieldMeta fieldMeta, @Nullable MethodAccess methodAccess, Object value){
try {
if (methodAccess != null && fieldMeta.getSetter() != null) {
methodAccess.invoke(object, fieldMeta.getIndex(), value);
} else if(fieldMeta.getField() != null) {
fieldMeta.getField().set(object, value);
}
} catch (Exception ex){
logger.warn(ex, () -> "Failed to set field "+fieldMeta.getName()+" of type "+fieldMeta.getType()+" to value "+ value);
}
}
}

View File

@ -1,4 +0,0 @@
package org.anime_game_servers.lua.serialize;
public interface Serializer {
}

View File

@ -2,20 +2,22 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("java-library")
kotlin("jvm") version "1.9.10"
kotlin("jvm")
}
group = "org.anime_game_servers"
group = "org.anime_game_servers.lua"
version = "0.1"
repositories {
mavenCentral()
mavenLocal()
}
dependencies {
testImplementation(platform("org.junit:junit-bom:5.9.1"))
testImplementation("org.junit.jupiter:junit-jupiter")
api(project(":BaseLua"))
implementation("org.anime_game_servers.lua:base-jvm:0.1")
api("org.anime_game_servers.core:gi:0.1")
implementation("com.google.code.findbugs:jsr305:3.0.2")
implementation(kotlin("stdlib-jdk8"))
compileOnly("org.projectlombok:lombok:1.18.30")

View File

@ -1,8 +1,9 @@
package org.anime_game_servers.gi_lua.models;
import com.github.davidmoten.rtreemulti.geometry.Point;
import org.anime_game_servers.core.gi.models.Vector;
public interface Position {
public interface Position extends Vector {
float getX();
float getY();
float getZ();

View File

@ -1,5 +1,9 @@
package org.anime_game_servers.gi_lua.models.constants;
import org.anime_game_servers.core.base.annotations.lua.LuaStatic;
@LuaStatic
public enum ChallengeEventMarkType {
CHALLENGE_EVENT_NONE,
FLIGHT_TIME,

View File

@ -1,12 +1,16 @@
package org.anime_game_servers.gi_lua.models.constants;
import org.anime_game_servers.core.base.annotations.lua.LuaStatic;
@LuaStatic
public enum EntityType {
NONE,
AVATAR,
MONSTER,
NONE, //
AVATAR, //
MONSTER, //
NPC,
GADGET,
REGION,
GADGET, //
REGION, //
WEAPON,
WEATHER,
SCENE,

View File

@ -1,5 +1,9 @@
package org.anime_game_servers.gi_lua.models.constants;
import org.anime_game_servers.core.base.annotations.lua.LuaStatic;
@LuaStatic
public class EventType {
public static final int EVENT_NONE = 0;
/**

View File

@ -1,5 +1,9 @@
package org.anime_game_servers.gi_lua.models.constants;
import org.anime_game_servers.core.base.annotations.lua.LuaStatic;
@LuaStatic
public enum ExhibitionPlayType {
Challenge,
Gallery,

View File

@ -1,5 +1,8 @@
package org.anime_game_servers.gi_lua.models.constants;
import org.anime_game_servers.core.base.annotations.lua.LuaStatic;
@LuaStatic
public enum FatherChallengeProperty {
DURATION,
CUR_SUCC,

View File

@ -1,5 +1,8 @@
package org.anime_game_servers.gi_lua.models.constants;
import org.anime_game_servers.core.base.annotations.lua.LuaStatic;
@LuaStatic
public enum FlowSuiteOperatePolicy {
DEFAULT,
COMPLETE

View File

@ -1,5 +1,8 @@
package org.anime_game_servers.gi_lua.models.constants;
import org.anime_game_servers.core.base.annotations.lua.LuaStatic;
@LuaStatic
public enum GroupKillPolicy {
GROUP_KILL_NONE,
GROUP_KILL_ALL,

View File

@ -1,6 +1,6 @@
package org.anime_game_servers.gi_lua.models.constants;
import org.anime_game_servers.lua.models.IntValueEnum;
import org.anime_game_servers.core.base.interfaces.IntValueEnum;
public enum GroupLoadStrategy implements IntValueEnum {
GROUP_LOAD_NONE(0),

View File

@ -1,24 +0,0 @@
package org.anime_game_servers.gi_lua.models.constants;
import kotlin.collections.MapsKt;
import org.anime_game_servers.lua.models.IntValueEnum;
import java.util.Arrays;
public enum QuestState implements IntValueEnum {
NONE(0),
UNSTARTED(1),
UNFINISHED(2),
FINISHED(3),
FAILED(4);
private final int value;
private QuestState(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
}

View File

@ -1,5 +1,10 @@
package org.anime_game_servers.gi_lua.models.constants;
import org.anime_game_servers.core.base.annotations.lua.LuaNames;
import org.anime_game_servers.core.base.annotations.lua.LuaStatic;
@LuaStatic
@LuaNames(names = {"GadgetState"})
public class ScriptGadgetState {
public static final int Default = 0;
public static final int GatherDrop = 1;

View File

@ -1,5 +1,10 @@
package org.anime_game_servers.gi_lua.models.constants;
import org.anime_game_servers.core.base.annotations.lua.LuaNames;
import org.anime_game_servers.core.base.annotations.lua.LuaStatic;
@LuaStatic
@LuaNames(names = {"RegionShape"})
public class ScriptRegionShape {
public static final int NONE = 0;
public static final int SPHERE = 1;

View File

@ -1,5 +1,8 @@
package org.anime_game_servers.gi_lua.models.constants;
import org.anime_game_servers.core.base.annotations.lua.LuaStatic;
@LuaStatic
public enum SealBattleType {
NONE,
ENERGY_CHARGE,

View File

@ -1,5 +1,8 @@
package org.anime_game_servers.gi_lua.models.constants;
import org.anime_game_servers.core.base.annotations.lua.LuaStatic;
@LuaStatic
public enum VisionLevelType {
VISION_LEVEL_NORMAL,
VISION_LEVEL_LITTLE_REMOTE,

View File

@ -1,5 +1,8 @@
package org.anime_game_servers.gi_lua.models.constants.temporary;
import org.anime_game_servers.core.base.annotations.lua.LuaStatic;
@LuaStatic
public enum GalleryProgressScoreType {
GALLERY_PROGRESS_SCORE_NONE,
GALLERY_PROGRESS_SCORE_NO_DEGRADE

View File

@ -1,5 +1,8 @@
package org.anime_game_servers.gi_lua.models.constants.temporary;
import org.anime_game_servers.core.base.annotations.lua.LuaStatic;
@LuaStatic
public enum GalleryProgressScoreUIType {
GALLERY_PROGRESS_SCORE_UI_TYPE_NONE,
GALLERY_PROGRESS_SCORE_UI_TYPE_BUOYANT_COMBAT,

View File

@ -3,12 +3,6 @@ package org.anime_game_servers.gi_lua.models.loader;
import io.github.oshai.kotlinlogging.KLogger;
import io.github.oshai.kotlinlogging.KotlinLogging;
import lombok.val;
import org.anime_game_servers.gi_lua.models.constants.*;
import org.anime_game_servers.gi_lua.models.constants.ExhibitionPlayType;
import org.anime_game_servers.gi_lua.models.constants.FlowSuiteOperatePolicy;
import org.anime_game_servers.gi_lua.models.constants.temporary.GalleryProgressScoreType;
import org.anime_game_servers.gi_lua.models.constants.temporary.GalleryProgressScoreUIType;
import org.anime_game_servers.gi_lua.script_lib.ScriptLib;
import org.anime_game_servers.lua.engine.BaseScriptLoader;
import org.anime_game_servers.lua.engine.LuaEngine;
import org.anime_game_servers.lua.engine.LuaScript;
@ -16,7 +10,6 @@ import org.anime_game_servers.lua.engine.ScriptConfig;
import org.anime_game_servers.lua.models.ScriptType;
import javax.script.ScriptException;
import java.lang.annotation.ElementType;
import java.nio.file.Path;
public interface GIScriptLoader extends BaseScriptLoader {
@ -71,24 +64,9 @@ public interface GIScriptLoader extends BaseScriptLoader {
}
default void addDefaultsForEngine(LuaEngine luaEngine){
luaEngine.addGlobalEnumByIntValue("QuestState", QuestState.values());
luaEngine.addGlobalEnumByOrdinal("EntityType", EntityType.values());
luaEngine.addGlobalEnumByOrdinal("ElementType", ElementType.values());
luaEngine.addGlobalEnumByOrdinal("GroupKillPolicy", GroupKillPolicy.values());
luaEngine.addGlobalEnumByOrdinal("SealBattleType", SealBattleType.values());
luaEngine.addGlobalEnumByOrdinal("FatherChallengeProperty", FatherChallengeProperty.values());
luaEngine.addGlobalEnumByOrdinal("ChallengeEventMarkType", ChallengeEventMarkType.values());
luaEngine.addGlobalEnumByOrdinal("VisionLevelType", VisionLevelType.values());
luaEngine.addGlobalEnumByOrdinal("ExhibitionPlayType", ExhibitionPlayType.values());
luaEngine.addGlobalEnumByOrdinal("FlowSuiteOperatePolicy", FlowSuiteOperatePolicy.values());
luaEngine.addGlobalEnumByOrdinal("GalleryProgressScoreUIType", GalleryProgressScoreUIType.values());
luaEngine.addGlobalEnumByOrdinal("GalleryProgressScoreType", GalleryProgressScoreType.values());
luaEngine.addGlobalStaticClass("EventType", EventType.class);
luaEngine.addGlobalStaticClass("GadgetState", ScriptGadgetState.class);
luaEngine.addGlobalStaticClass("RegionShape", ScriptRegionShape.class);
luaEngine.addGlobalStaticClass("ScriptLib", ScriptLib.class);
LuaEngine.registerNamespace("org.anime_game_servers.core.gi");
LuaEngine.registerNamespace("org.anime_game_servers.gi_lua");
luaEngine.addGlobals();
}
default boolean loadSceneReplacementScript(ScriptParser parser){
@ -102,8 +80,11 @@ public interface GIScriptLoader extends BaseScriptLoader {
default boolean loadSceneMetaScript(int sceneId, ScriptParser parser){
return loadData(ScriptSource.SCENE, sceneId, "scene"+sceneId+".lua", ScriptType.DATA_STORAGE, parser);
}
default boolean loadSceneBlockScript(int sceneId, int blockId, ScriptParser parser){
return loadData(ScriptSource.SCENE, sceneId, "scene"+sceneId+"_block"+blockId+".lua", ScriptType.DATA_STORAGE, parser);
default boolean loadSceneBlockScript(ScriptSource scriptSource, int targetId, int blockId, ScriptParser parser){
if(scriptSource == ScriptSource.SCENE)
return loadData(scriptSource, targetId, "scene"+targetId+"_block"+blockId+".lua", ScriptType.DATA_STORAGE, parser);
else
return loadData(scriptSource, targetId, "activity"+targetId+"_block"+blockId+".lua", ScriptType.DATA_STORAGE, parser);
}
default boolean loadSceneGroupScript(ScriptSource scriptSource, int targetId, int groupId, ScriptParser parser){
if(scriptSource == ScriptSource.SCENE)

View File

@ -0,0 +1,89 @@
package org.anime_game_servers.gi_lua.models.scene;
import io.github.oshai.kotlinlogging.KLogger;
import io.github.oshai.kotlinlogging.KotlinLogging;
import lombok.Getter;
import lombok.ToString;
import lombok.val;
import org.anime_game_servers.gi_lua.models.loader.GIScriptLoader;
import org.anime_game_servers.gi_lua.models.scene.block.SceneBlock;
import org.anime_game_servers.gi_lua.models.scene.block.SceneGroupInfo;
import org.anime_game_servers.gi_lua.models.scene.group.SceneGroup;
import org.anime_game_servers.lua.engine.LuaScript;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ToString
@Getter
public class ActivityMeta {
private static KLogger logger = KotlinLogging.INSTANCE.logger(ActivityMeta.class.getName());
private int activityId;
private SceneMeta parent;
private List<Integer> blockIds;
private Map<Integer, SceneBlock> blocks;
private Map<Integer, SceneGroupInfo> groupsInfos;
private Map<Integer, SceneGroup> groups;
public static ActivityMeta of(SceneMeta parent, int sceneId, GIScriptLoader scriptLoader) {
return new ActivityMeta(parent,sceneId)
.load(scriptLoader);
}
private ActivityMeta(SceneMeta parent, int activityId) {
this.activityId = activityId;
this.parent = parent;
}
public ActivityMeta load(GIScriptLoader scriptLoader) {
if(!loadActivityMeta(scriptLoader)){
return null;
}
loadBlocks(scriptLoader);
prepareGroups();
return this;
}
private boolean loadActivityMeta(GIScriptLoader scriptLoader){
return scriptLoader.loadActivityMetaScript(activityId, (cs -> {
// Create blocks
this.blockIds = cs.getGlobalVariableList("blocks", Integer.class);
}));
}
private void loadBlocks(GIScriptLoader scriptLoader){
this.blocks = new HashMap<>(blockIds.size());
this.groupsInfos = new HashMap<>();
for(val blockId : blockIds){
val block = SceneBlock.of(parent, this, blockId, scriptLoader);
this.blocks.put(blockId, block);
}
}
private void prepareGroups(){
this.groups = new HashMap<>(groupsInfos.size());
for(val groupInfo : groupsInfos.values()){
val group = SceneGroup.of(groupInfo);
this.groups.put(groupInfo.getId(), group);
}
}
@Nullable
public SceneGroup getGroup(int id) {
return groups.get(id);
}
@Nullable
public SceneGroupInfo getGroupInfo(int id) {
return groupsInfos.get(id);
}
@Nullable
public SceneBlock getBlock(int id) {
return blocks.get(id);
}
}

View File

@ -24,6 +24,7 @@ public class SceneMeta {
private int sceneId;
private SceneConfig config;
private List<Integer> blockIds;
private Map<Integer, ActivityMeta> activities = new HashMap<>();
private Map<Integer, SceneBlock> blocks;
private Map<Integer, SceneGroupInfo> groupsInfos;
private Map<Integer, SceneGroup> groups;
@ -76,11 +77,38 @@ public class SceneMeta {
this.blocks = new HashMap<>(blockIds.size());
this.groupsInfos = new HashMap<>();
for(val blockId : blockIds){
val block = SceneBlock.of(this, 0, blockId, scriptLoader);
val block = SceneBlock.of(this, null, blockId, scriptLoader);
this.blocks.put(blockId, block);
}
}
public void loadActivity(GIScriptLoader scriptLoader, int activityId){
if(sceneId != 3){
return;
}
var meta = activities.get(activityId);
if(meta == null){
meta = ActivityMeta.of(this, activityId, scriptLoader);
activities.put(activityId, meta);
}
this.blocks.putAll(meta.getBlocks());
this.groupsInfos.putAll(meta.getGroupsInfos());
this.groups.putAll(meta.getGroups());
}
private void unloadActivity(int activityId){
if(sceneId != 3){
return;
}
var meta = activities.get(activityId);
if(meta == null){
return;
}
this.blocks.keySet().removeAll(meta.getBlocks().keySet());
this.groupsInfos.keySet().removeAll(meta.getGroupsInfos().keySet());
this.groups.keySet().removeAll(meta.getGroups().keySet());
}
private void prepareGroups(){
this.groups = new HashMap<>(groupsInfos.size());
for(val groupInfo : groupsInfos.values()){

View File

@ -5,9 +5,12 @@ import io.github.oshai.kotlinlogging.KLogger;
import io.github.oshai.kotlinlogging.KotlinLogging;
import lombok.*;
import org.anime_game_servers.gi_lua.models.PositionImpl;
import org.anime_game_servers.gi_lua.models.loader.ScriptSource;
import org.anime_game_servers.gi_lua.models.scene.ActivityMeta;
import org.anime_game_servers.gi_lua.models.scene.SceneMeta;
import org.anime_game_servers.gi_lua.models.loader.GIScriptLoader;
import javax.annotation.Nullable;
import java.util.Map;
import java.util.stream.Collectors;
@ -25,20 +28,22 @@ public class SceneBlock {
// internal only
private transient boolean loaded; // Not an actual variable in the scripts either
private transient SceneMeta meta; // Not an actual variable in the scripts either
private transient ActivityMeta activityMeta; // Not an actual variable in the scripts either
private int sceneId;
private int activityId;
private int id;
public static SceneBlock of(SceneMeta sceneMeta, int activityId, int blockId, GIScriptLoader scriptLoader) {
val block = new SceneBlock(sceneMeta.getSceneId(), activityId, blockId, sceneMeta);
public static SceneBlock of(SceneMeta sceneMeta, @Nullable ActivityMeta activityMeta, int blockId, GIScriptLoader scriptLoader) {
val block = new SceneBlock(sceneMeta.getSceneId(), activityMeta != null ? activityMeta.getActivityId() : 0, blockId, sceneMeta, activityMeta);
block.load(scriptLoader);
return block;
}
private SceneBlock(int sceneId, int activityId, int blockId, SceneMeta meta) {
private SceneBlock(int sceneId, int activityId, int blockId, SceneMeta meta, ActivityMeta activityMeta) {
this.id = blockId;
this.sceneId = sceneId;
this.activityId = activityId;
this.meta = meta;
this.activityMeta = activityMeta;
}
public void setLoaded(boolean loaded) {
@ -50,7 +55,10 @@ public class SceneBlock {
return this;
}
this.setLoaded(true);
if( !scriptLoader.loadSceneBlockScript(sceneId, id, (cs -> {
val scriptType = activityId == 0 ? ScriptSource.SCENE : ScriptSource.ACTIVITY;
val typeId = activityId == 0 ? sceneId : activityId;
if( !scriptLoader.loadSceneBlockScript(scriptType, typeId, id, (cs -> {
// Set groups
this.groupInfo = cs.getGlobalVariableList("groups", SceneGroupInfo.class).stream()
.collect(Collectors.toMap(x -> x.getId(), y -> y, (a, b) -> a));
@ -64,7 +72,12 @@ public class SceneBlock {
}))){
return null;
}
if(activityMeta!=null) {
activityMeta.getGroupsInfos().putAll(this.groupInfo);
} else {
meta.getGroupsInfos().putAll(this.groupInfo);
}
logger.debug(() -> "Successfully loaded block " + this.id + " in scene "+sceneId+".");
return this;
}

View File

@ -1,7 +1,7 @@
package org.anime_game_servers.gi_lua.models.scene.group;
import lombok.Getter;
import org.anime_game_servers.gi_lua.models.Position;
import org.anime_game_servers.core.gi.models.Vector;
import org.anime_game_servers.gi_lua.models.PositionImpl;
import org.anime_game_servers.gi_lua.models.constants.EntityType;
import org.anime_game_servers.gi_lua.models.constants.ScriptRegionShape;
@ -24,7 +24,7 @@ public class SceneRegion extends SceneObject{
private List<String> team_ability_group;
private boolean is_trigger_reload_group = false;
public boolean contains(Position position) {
public boolean contains(Vector position) {
switch (shape) {
case ScriptRegionShape.CUBIC:
return (Math.abs(pos.getX() - position.getX()) <= size.getX()/2f) &&

View File

@ -1,5 +1,11 @@
package org.anime_game_servers.gi_lua.script_lib
/**
* Lua context for calls to a Gadget Controller scripts functions.
* This context always contains a reference to the entity that calls the controller function
*/
interface ControllerLuaContext<GadgetEntity> : LuaContext {
val gadget:GadgetEntity
fun <T: ControllerLuaContext<GadgetEntity>> getScriptLibHandlerProvider(): ScriptLibControllerHandlerProvider<GadgetEntity, T>
}

View File

@ -8,4 +8,8 @@ interface GroupEventLuaContext: LuaContext {
fun getArgs(): ScriptArgs
fun <T: GroupEventLuaContext> getScriptLibHandlerProvider(): ScriptLibGroupHandlerProvider<T>
/*override fun uid() = getArgs().uid
override fun source_entity_id() = getArgs().source_eid
override fun target_entity_id() = getArgs().target_eid*/
}

View File

@ -5,4 +5,9 @@ import org.anime_game_servers.lua.engine.LuaEngine;
public interface LuaContext {
LuaEngine getEngine();
ScriptLibHandler getScriptLibHandler();
// fields used by some scripts
/*int uid();
int source_entity_id();
int target_entity_id();*/
}

View File

@ -3,6 +3,7 @@ package org.anime_game_servers.gi_lua.script_lib;
import io.github.oshai.kotlinlogging.KLogger;
import io.github.oshai.kotlinlogging.KotlinLogging;
import lombok.val;
import org.anime_game_servers.core.base.annotations.lua.LuaStatic;
import org.anime_game_servers.gi_lua.models.constants.*;
import org.anime_game_servers.gi_lua.models.constants.ExhibitionPlayType;
import org.anime_game_servers.gi_lua.models.constants.FlowSuiteOperatePolicy;
@ -10,12 +11,11 @@ import org.anime_game_servers.gi_lua.models.constants.temporary.GalleryProgressS
import org.anime_game_servers.gi_lua.models.constants.temporary.GalleryProgressScoreUIType;
import org.anime_game_servers.lua.engine.LuaTable;
import java.util.ArrayList;
import static org.anime_game_servers.gi_lua.utils.ScriptUtils.luaToPos;
import static org.anime_game_servers.gi_lua.utils.ScriptUtils.posToLua;
import static org.anime_game_servers.gi_lua.script_lib.ScriptLibErrors.*;
@LuaStatic
public class ScriptLib {
/**
@ -841,25 +841,26 @@ public class ScriptLib {
val killPolicyId = table.optInt("kill_policy", -1);
if(groupId == -1){
scriptLogger.error(() -> "KillGroupEntity: groupId not set");
return 1;
return INVALID_PARAMETER_TABLE_CONTENT.getValue();
}
if(killPolicyId == -1){
scriptLogger.error(() -> "KillGroupEntity: kill_policy not set");
return killByCfgIds(context, groupId, table);
}
return killByGroupPolicy(context, groupId, killPolicyId);
}
private static int killByGroupPolicy(GroupEventLuaContext context, int groupId, int killPolicyId){
scriptLogger.debug(() -> "KillGroupEntity: kill by group policy");
if(killPolicyId >= GroupKillPolicy.values().length){
scriptLogger.error(() -> "KillGroupEntity: kill_policy out of bounds");
return 2;
return INVALID_PARAMETER.getValue();
}
val policy = GroupKillPolicy.values()[killPolicyId];
return context.getScriptLibHandler().KillGroupEntityByPolicy(context, groupId, policy);
}
private static int killByCfgIds(GroupEventLuaContext context, int groupId, LuaTable luaTable){
scriptLogger.debug(() -> "KillGroupEntity: kill by cfg ids");
val monsterList = luaTable.getTable("monsters");
val gadgetList = luaTable.getTable("gadgets");
val monsters = monsterList != null ? monsterList.getAsIntArray() : new int[0];
@ -970,11 +971,11 @@ public class ScriptLib {
val galleryId = param4.optInt("gallery_id", -1);
if(exhibitionTypeIndex < 0 || exhibitionTypeIndex >= ExhibitionPlayType.values().length){
scriptLogger.error(() -> "Invalid exhibition type " + exhibitionTypeIndex);
return 1;
return INVALID_PARAMETER_TABLE_CONTENT.getValue();
}
if (galleryId == -1){
scriptLogger.error(() -> "Invalid gallery id " + galleryId);
return 2;
return INVALID_PARAMETER_TABLE_CONTENT.getValue();
}
val exhibitionTypeEnum = ExhibitionPlayType.values()[exhibitionTypeIndex];
return context.getScriptLibHandler().AddExhibitionAccumulableDataAfterSuccess(context, uid, param2, param3, exhibitionTypeEnum, galleryId);
@ -1098,56 +1099,109 @@ public class ScriptLib {
* Methods used in EntityControllers/using ControllerLuaContext
*/
public static int SetGadgetState(ControllerLuaContext<?> context, int gadgetState) {
return context.getScriptLibHandler().SetGadgetState(context, gadgetState);
public static int SetGadgetState(ControllerLuaContext<Object> context, int gadgetState) {
val handler = context.getScriptLibHandlerProvider().getGadgetControllerHandler();
if(handler == null){
return NOT_IMPLEMENTED.getValue();
}
return handler.SetGadgetState(context, gadgetState);
}
public static int GetGadgetState(ControllerLuaContext<?> context) {
return context.getScriptLibHandler().GetGadgetState(context);
public static int GetGadgetState(ControllerLuaContext<Object> context) {
val handler = context.getScriptLibHandlerProvider().getGadgetControllerHandler();
if(handler == null){
return NOT_IMPLEMENTED.getValue();
}
return handler.GetGadgetState(context);
}
public static int ResetGadgetState(ControllerLuaContext<?> context, int gadgetState) {
return context.getScriptLibHandler().ResetGadgetState(context, gadgetState);
public static int ResetGadgetState(ControllerLuaContext<Object> context, int gadgetState) {
val handler = context.getScriptLibHandlerProvider().getGadgetControllerHandler();
if(handler == null){
return NOT_IMPLEMENTED.getValue();
}
return handler.ResetGadgetState(context, gadgetState);
}
public static int SetGearStartValue(ControllerLuaContext<?> context, int startValue) {
return context.getScriptLibHandler().SetGearStartValue(context, startValue);
public static int SetGearStartValue(ControllerLuaContext<Object> context, int startValue) {
val handler = context.getScriptLibHandlerProvider().getGadgetControllerHandler();
if(handler == null){
return NOT_IMPLEMENTED.getValue();
}
return handler.SetGearStartValue(context, startValue);
}
public static int GetGearStartValue(ControllerLuaContext<?> context) {
return context.getScriptLibHandler().GetGearStartValue(context);
public static int GetGearStartValue(ControllerLuaContext<Object> context) {
val handler = context.getScriptLibHandlerProvider().getGadgetControllerHandler();
if(handler == null){
return NOT_IMPLEMENTED.getValue();
}
return handler.GetGearStartValue(context);
}
public static int SetGearStopValue(ControllerLuaContext<?> context, int startValue) {
return context.getScriptLibHandler().SetGearStopValue(context, startValue);
public static int SetGearStopValue(ControllerLuaContext<Object> context, int startValue) {
val handler = context.getScriptLibHandlerProvider().getGadgetControllerHandler();
if(handler == null){
return NOT_IMPLEMENTED.getValue();
}
return handler.SetGearStopValue(context, startValue);
}
public static int GetGearStopValue(ControllerLuaContext<?> context) {
return context.getScriptLibHandler().GetGearStopValue(context);
public static int GetGearStopValue(ControllerLuaContext<Object> context) {
val handler = context.getScriptLibHandlerProvider().getGadgetControllerHandler();
if(handler == null){
return NOT_IMPLEMENTED.getValue();
}
return handler.GetGearStopValue(context);
}
public static int GetGadgetStateBeginTime(ControllerLuaContext<?> context) {
return context.getScriptLibHandler().GetGadgetStateBeginTime(context);
public static int GetGadgetStateBeginTime(ControllerLuaContext<Object> context) {
val handler = context.getScriptLibHandlerProvider().getGadgetControllerHandler();
if(handler == null){
return NOT_IMPLEMENTED.getValue();
}
return handler.GetGadgetStateBeginTime(context);
}
public static int GetContextGadgetConfigId(ControllerLuaContext<?> context) {
return context.getScriptLibHandler().GetContextGadgetConfigId(context);
public static int GetContextGadgetConfigId(ControllerLuaContext<Object> context) {
val handler = context.getScriptLibHandlerProvider().getGadgetControllerHandler();
if(handler == null){
return NOT_IMPLEMENTED.getValue();
}
return handler.GetContextGadgetConfigId(context);
}
public static int GetContextGroupId(ControllerLuaContext<?> context) {
return context.getScriptLibHandler().GetContextGroupId(context);
public static int GetContextGroupId(ControllerLuaContext<Object> context) {
val handler = context.getScriptLibHandlerProvider().getGadgetControllerHandler();
if(handler == null){
return NOT_IMPLEMENTED.getValue();
}
return handler.GetContextGroupId(context);
}
public static int SetGadgetEnableInteract(ControllerLuaContext<?> context, int groupId, int configId, boolean enable) {
return context.getScriptLibHandler().SetGadgetEnableInteract(context, groupId, configId, enable);
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();
}
return handler.SetGadgetEnableInteract(context, groupId, configId, enable);
}
public static int DropSubfield(ControllerLuaContext<?> context, Object paramsTable) {
public static int DropSubfield(ControllerLuaContext<Object> context, Object paramsTable) {
val params = context.getEngine().getTable(paramsTable);
return context.getScriptLibHandler().DropSubfield(context, params);
val handler = context.getScriptLibHandlerProvider().getGadgetControllerHandler();
if(handler == null){
return NOT_IMPLEMENTED.getValue();
}
return context.getScriptLibHandlerProvider().getGadgetControllerHandler().DropSubfield(context, params);
}
public static int[] GetGatherConfigIdList(ControllerLuaContext<?> context) {
return context.getScriptLibHandler().GetGatherConfigIdList(context);
public static int[] GetGatherConfigIdList(ControllerLuaContext<Object> context) {
val handler = context.getScriptLibHandlerProvider().getGadgetControllerHandler();
if(handler == null){
return null;
}
return handler.GetGatherConfigIdList(context);
}
}

View File

@ -0,0 +1,8 @@
package org.anime_game_servers.gi_lua.script_lib
import org.anime_game_servers.gi_lua.script_lib.handler.GadgetControllerHandler
interface ScriptLibControllerHandlerProvider<GadgetEntity, ControllerEventContext : ControllerLuaContext<GadgetEntity>> {
fun getScriptLibHandler(): ScriptLibHandler<*, *>
fun getGadgetControllerHandler(): GadgetControllerHandler<GadgetEntity, ControllerEventContext>?
}

View File

@ -1,6 +1,6 @@
package org.anime_game_servers.gi_lua.script_lib
import org.anime_game_servers.lua.models.IntValueEnum
import org.anime_game_servers.core.base.interfaces.IntValueEnum
enum class ScriptLibErrors(private val _value:Int): IntValueEnum {
NOT_IMPLEMENTED(-100),

View File

@ -1,7 +1,8 @@
package org.anime_game_servers.gi_lua.script_lib;
import lombok.val;
import org.anime_game_servers.gi_lua.models.Position;
import org.anime_game_servers.core.gi.enums.QuestState;
import org.anime_game_servers.core.gi.models.Vector;
import org.anime_game_servers.gi_lua.models.constants.*;
import org.anime_game_servers.gi_lua.models.constants.ExhibitionPlayType;
import org.anime_game_servers.gi_lua.models.constants.FlowSuiteOperatePolicy;
@ -118,7 +119,7 @@ public interface ScriptLibHandler<GroupEventContext extends GroupEventLuaContext
* @param pos The position to spawn the gadget at
* @param rot The rotation of the gadget when spawned
*/
int CreateGadgetByConfigIdByPos(GroupEventContext context, int configId, Position pos, Position rot);
int CreateGadgetByConfigIdByPos(GroupEventContext context, int configId, Vector pos, Vector rot);
/**
@ -135,7 +136,7 @@ public interface ScriptLibHandler<GroupEventContext extends GroupEventLuaContext
* @param position The position to spawn the vehicle at
* @param rot The rotation to spawn the vehicle with
*/
int CreateVehicle(GroupEventContext context, int uid, int gadgetId, Position position, Position rot);
int CreateVehicle(GroupEventContext context, int uid, int gadgetId, Vector position, Vector rot);
int CheckRemainGadgetCountByGroupId(GroupEventContext context, LuaTable table);
@ -150,8 +151,10 @@ public interface ScriptLibHandler<GroupEventContext extends GroupEventLuaContext
int ChangeGroupGadget(GroupEventContext context, LuaTable table) ;
int GetSceneOwnerUid(GroupEventContext context);
@Nonnull QuestState GetHostQuestState(GroupEventContext context, int questId);
@Nonnull QuestState GetQuestState(GroupEventContext context, int entityId, int questId);
@Nonnull
QuestState GetHostQuestState(GroupEventContext context, int questId);
@Nonnull
QuestState GetQuestState(GroupEventContext context, int entityId, int questId);
int ShowReminder(GroupEventContext context, int reminderId);
int RemoveEntityByConfigId(GroupEventContext context, int groupId, EntityType entityType, int configId);
int CreateGroupTimerEvent(GroupEventContext context, int groupID, String source, double time);
@ -248,9 +251,9 @@ public interface ScriptLibHandler<GroupEventContext extends GroupEventLuaContext
int MoveAvatarByPointArray(GroupEventContext context, int uid, int targetId, LuaTable var3, String var4);
int MovePlayerToPos(GroupEventContext context, int[] targetUIds, Position pos, Position rot, int radius, boolean isSkipUi);
int MovePlayerToPos(GroupEventContext context, int[] targetUIds, Vector pos, Vector rot, int radius, boolean isSkipUi);
int TransPlayerToPos(GroupEventContext context, int[] targetUIds, Position pos, Position rot, int radius, boolean isSkipUi);
int TransPlayerToPos(GroupEventContext context, int[] targetUIds, Vector pos, Vector rot, int radius, boolean isSkipUi);
int PlayCutScene(GroupEventContext context, int cutsceneId, int var2);
@ -317,9 +320,9 @@ public interface ScriptLibHandler<GroupEventContext extends GroupEventLuaContext
int GetAvatarEntityIdByUid(GroupEventContext context, int uid);
Position GetPosByEntityId(GroupEventContext context, int entityId);
Vector GetPosByEntityId(GroupEventContext context, int entityId);
Position GetRotationByEntityId(GroupEventContext context, int entityId);
Vector GetRotationByEntityId(GroupEventContext context, int entityId);
ActivityOpenAndCloseTime GetActivityOpenAndCloseTimeByScheduleId(GroupEventContext context, int scheduleId);
@ -440,34 +443,4 @@ public interface ScriptLibHandler<GroupEventContext extends GroupEventLuaContext
* @param val5 TODO
*/
int ExecuteGadgetLua(GroupEventLuaContext context, int groupId, int gadgetCfgId, int activityType, int var4, int val5);
/**
* Methods used in EntityControllers/using ControllerEventContext
*/
int SetGadgetState(ControllerEventContext context, int gadgetState);
int GetGadgetState(ControllerEventContext context);
int ResetGadgetState(ControllerEventContext context, int gadgetState) ;
int SetGearStartValue(ControllerEventContext context, int startValue);
int GetGearStartValue(ControllerEventContext context);
int SetGearStopValue(ControllerEventContext context, int startValue);
int GetGearStopValue(ControllerEventContext context) ;
int GetGadgetStateBeginTime(ControllerEventContext context);
int GetContextGadgetConfigId(ControllerEventContext context) ;
int GetContextGroupId(ControllerEventContext context);
int SetGadgetEnableInteract(ControllerEventContext context, int groupId, int configId, boolean enable);
int DropSubfield(ControllerEventContext context, LuaTable paramsTable);
int[] GetGatherConfigIdList(ControllerEventContext context);
}

View File

@ -0,0 +1,54 @@
package org.anime_game_servers.gi_lua.script_lib.handler
import org.anime_game_servers.gi_lua.script_lib.ControllerLuaContext
import org.anime_game_servers.lua.engine.LuaTable
/**
* Handler for scriptlib functions used in EntityControllers, which use the ControllerEventContext.
* These are only callable from a gadget controller context.
*/
interface GadgetControllerHandler<GadgetEntity, ControllerEventContext : ControllerLuaContext<GadgetEntity>> {
/**
* Changes the state of the gadget that called the controller.
*/
fun SetGadgetState(context: ControllerEventContext, gadgetState: Int): Int
/**
* Returns the current state of the gadget that called the controller.
*/
fun GetGadgetState(context: ControllerEventContext): Int
/**
* Resets the gadgets state to its state when born state.
*/
fun ResetGadgetState(context: ControllerEventContext, gadgetState: Int): Int
fun SetGearStartValue(context: ControllerEventContext, startValue: Int): Int
fun GetGearStartValue(context: ControllerEventContext): Int
fun SetGearStopValue(context: ControllerEventContext, startValue: Int): Int
fun GetGearStopValue(context: ControllerEventContext): Int
fun GetGadgetStateBeginTime(context: ControllerEventContext): Int
/**
* Returns the config id of the gadget that called the controller.
*/
fun GetContextGadgetConfigId(context: ControllerEventContext): Int
/**
* Returns the group id of the gadget that called the controller.
*/
fun GetContextGroupId(context: ControllerEventContext): Int
/**
* Sets a gadget interacteable based on the config id and group id.
*/
fun SetGadgetEnableInteract(context: ControllerEventContext, groupId: Int, configId: Int, enable: Boolean): Int
fun DropSubfield(context: ControllerEventContext, paramsTable: LuaTable?): Int
fun GetGatherConfigIdList(context: ControllerEventContext): IntArray?
}

View File

@ -1,14 +1,14 @@
package org.anime_game_servers.gi_lua.utils;
import lombok.val;
import org.anime_game_servers.gi_lua.models.Position;
import org.anime_game_servers.core.gi.models.Vector;
import org.anime_game_servers.gi_lua.models.PositionImpl;
import org.anime_game_servers.lua.engine.LuaEngine;
import org.anime_game_servers.lua.engine.LuaTable;
public class ScriptUtils {
public static LuaTable posToLua(Position position, LuaEngine engine){
public static LuaTable posToLua(Vector position, LuaEngine engine){
var result = engine.createTable();
if(position != null){
result.set("x", position.getX());
@ -23,7 +23,7 @@ public class ScriptUtils {
return result;
}
public static Position luaToPos(LuaTable position){
public static Vector luaToPos(LuaTable position){
val result = new PositionImpl();
if(position != null){
result.setX(position.optInt("x", 0));

View File

@ -2,10 +2,10 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("java-library")
kotlin("jvm") version "1.9.10"
kotlin("jvm")
}
group = "org.anime_game_servers"
group = "org.anime_game_servers.lua"
version = "0.1"
repositories {
@ -15,7 +15,7 @@ repositories {
dependencies {
testImplementation(platform("org.junit:junit-bom:5.9.1"))
testImplementation("org.junit.jupiter:junit-jupiter")
implementation(project(":BaseLua"))
implementation("org.anime_game_servers.lua:base-jvm:0.1")
implementation("com.google.code.findbugs:jsr305:3.0.2")
implementation("org.anime_game_servers:JNLua_GC:0.1.0")
implementation(kotlin("stdlib-jdk8"))
@ -30,11 +30,11 @@ tasks.test {
}
val compileKotlin: KotlinCompile by tasks
compileKotlin.kotlinOptions {
jvmTarget = "1.8"
jvmTarget = "17"
}
val compileTestKotlin: KotlinCompile by tasks
compileTestKotlin.kotlinOptions {
jvmTarget = "1.8"
jvmTarget = "17"
}
publishing {

View File

@ -1,111 +0,0 @@
package org.anime_game_servers.jnlua_engine;
import io.github.oshai.kotlinlogging.KLogger;
import io.github.oshai.kotlinlogging.KotlinLogging;
import lombok.Getter;
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 org.anime_game_servers.lua.models.IntValueEnum;
import org.anime_game_servers.lua.models.ScriptType;
import org.jetbrains.annotations.Nullable;
import org.terasology.jnlua.JavaFunction;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleBindings;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.AbstractMap;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class JNLuaEngine implements LuaEngine {
private static KLogger logger = KotlinLogging.INSTANCE.logger(JNLuaEngine.class.getName());
@Getter
private final ScriptEngineManager manager;
@Getter(onMethod = @__(@Override))
private final JNLuaSerializer serializer;
@Getter
private final SimpleBindings bindings;
@Getter(onMethod = @__(@Override))
private final ScriptConfig scriptConfig;
public JNLuaEngine(ScriptConfig scriptConfig) {
this.scriptConfig = scriptConfig;
this.manager = new ScriptEngineManager();
this.bindings = new SimpleBindings();
this.serializer = new JNLuaSerializer();
this.bindings.put("print", (JavaFunction) luaState -> {
logger.debug(() -> "[LUA] print " + luaState.checkString(1));
return 1;
});
}
@Override
public <T extends Enum<T>> boolean addGlobalEnumByOrdinal(String name, T[] enumArray) {
Map<String, Integer> table = new HashMap<>();
Arrays.stream(enumArray).forEach(e -> {
table.put(e.name(), e.ordinal());
table.put(e.name().toUpperCase(), e.ordinal());
});
bindings.put(name, table);
return true;
}
@Override
public <T extends Enum<T> & IntValueEnum> boolean addGlobalEnumByIntValue(String name, T[] enumArray) {
Map<String, Integer> table = new HashMap<>();
Arrays.stream(enumArray).forEach(e -> {
table.put(e.name(), e.getValue());
table.put(e.name().toUpperCase(), e.getValue());
});
bindings.put(name, table);
return true;
}
@Override
public boolean addGlobalStaticClass(String name, Class<?> staticClass) {
try {
bindings.put(name, new StaticClassWrapper(staticClass));
return true;
} catch (Exception e) {
logger.error("Failed to add static class to lua engine: " + name, e);
}
return false;
}
@Override
public boolean addObject(String name, Object object) {
bindings.put(name, object);
return false;
}
@Nullable
@Override
public LuaScript getScript(Path scriptPath, ScriptType scriptType) {
if (!Files.exists(scriptPath)) return null;
try {
return new JNLuaScript(this, scriptPath, scriptType);
} catch (IOException | ScriptException e) {
throw new RuntimeException(e);
}
}
@Override
public LuaTable getTable(Object table) {
return new JNLuaTable((AbstractMap<?, ?>) table);
}
@Override
public LuaTable createTable() {
return new JNLuaTable(new HashMap<>());
}
}

View File

@ -0,0 +1,83 @@
package org.anime_game_servers.jnlua_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 org.anime_game_servers.lua.models.ScriptType
import org.terasology.jnlua.JavaFunction
import org.terasology.jnlua.LuaState
import java.io.IOException
import java.nio.file.Files
import java.nio.file.Path
import java.util.*
import javax.script.ScriptEngineManager
import javax.script.ScriptException
import javax.script.SimpleBindings
class JNLuaEngine(override val scriptConfig: ScriptConfig) : LuaEngine {
val manager = ScriptEngineManager()
override val serializer: JNLuaSerializer = JNLuaSerializer()
val bindings = SimpleBindings()
init {
bindings["print"] = JavaFunction { luaState: LuaState ->
logger.debug { "[LUA] print " + luaState.checkString(1) }
1
}
}
override fun <T : Enum<*>> addGlobalEnum(name: String, enumArray: Array<T>): Boolean {
val table: MutableMap<String, Int> = HashMap()
Arrays.stream(enumArray).forEach { e: T ->
val fieldName = getEnumFieldName(e)
val value = getEnumFieldValue(e)
table[fieldName] = value
table[fieldName.uppercase(Locale.getDefault())] = value
}
bindings[name] = table
return true
}
override fun addGlobalStaticClass(name: String, staticClass: Class<*>): Boolean {
try {
bindings[name] = StaticClassWrapper(staticClass)
return true
} catch (e: Exception) {
logger.error(e) { "Failed to add static class to lua engine: $name" }
}
return false
}
override fun addObject(name: String, `object`: Any): Boolean {
bindings[name] = `object`
return false
}
override fun getScript(scriptPath: Path, scriptType: ScriptType): LuaScript? {
if (!Files.exists(scriptPath)) return null
try {
return JNLuaScript(this, scriptPath, scriptType)
} catch (e: IOException) {
throw RuntimeException(e)
} catch (e: ScriptException) {
throw RuntimeException(e)
}
}
override fun getTable(table: Any): LuaTable? {
return JNLuaTable((table as AbstractMap<*, *>))
}
override fun createTable(): LuaTable {
return JNLuaTable(HashMap<Any, Any>())
}
companion object {
private val logger = logger(JNLuaEngine::class.java.name)
}
}

View File

@ -166,7 +166,6 @@ public class JNLuaSerializer extends BaseSerializer {
set(object, fieldMeta, methodAccess, listObj);
} else {
set(object, fieldMeta, methodAccess, serialize(fieldMeta.getType(), fieldMeta.getField(), (LuaValueProxy) keyValue));
//methodAccess.invoke(object, fieldMeta.index, keyValue);
}
} catch (Exception ex) {
logger.error(ex, () -> "Exception serializing");

View File

@ -1,255 +0,0 @@
package org.anime_game_servers.jnlua_engine;
import lombok.val;
import org.anime_game_servers.lua.engine.LuaTable;
import org.jetbrains.annotations.Nullable;
import java.util.AbstractMap;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class JNLuaTable implements LuaTable {
AbstractMap<Object, Object> table;
JNLuaTable(AbstractMap<?, ?> table) {
this.table = (AbstractMap<Object, Object>) table;
}
@Override
public boolean has(String key) {
return table.containsKey(key);
}
@Override
public Object get(String key) {
return table.get(key);
}
@Override
public Object get(int key) {
return table.get(key);
}
@Override
public int getInt(String key) {
return ((Number) table.get(key)).intValue();
}
@Override
public int optInt(String key, int defaultValue) {
val value = table.get(key);
if (value instanceof Number)
return ((Number) value).intValue();
return defaultValue;
}
@Override
public int getInt(int key) {
return ((Number) table.get(key)).intValue();
}
@Override
public int optInt(int key, int defaultValue) {
val value = table.get(key);
if (value instanceof Number)
return ((Number) value).intValue();
return defaultValue;
}
@Override
public void set(int key, int value) {
table.put(key, value);
}
@Override
public void set(String key, int value) {
table.put(key, value);
}
@Override
public double getDouble(String key) {
return ((Number) table.get(key)).doubleValue();
}
@Override
public double optDouble(String key, double defaultValue) {
val value = table.get(key);
if (value instanceof Number)
return ((Number) value).doubleValue();
return defaultValue;
}
@Override
public double getDouble(int key) {
return ((Number) table.get(key)).doubleValue();
}
@Override
public double optDouble(int key, double defaultValue) {
val value = table.get(key);
if (value instanceof Number)
return ((Number) value).doubleValue();
return defaultValue;
}
@Override
public float getFloat(String key) {
return ((Number) table.get(key)).floatValue();
}
@Override
public float optFloat(String key, float defaultValue) {
val value = table.get(key);
if (value instanceof Number)
return ((Number) value).floatValue();
return defaultValue;
}
@Override
public float getFloat(int key) {
return ((Number) table.get(key)).floatValue();
}
@Override
public float optFloat(int key, float defaultValue) {
val value = table.get(key);
if (value instanceof Number)
return ((Number) value).floatValue();
return defaultValue;
}
@Override
public void set(int key, double value) {
table.put(key, value);
}
@Override
public void set(String key, double value) {
table.put(key, value);
}
@Override
public boolean getBoolean(String key) {
return (Boolean) table.get(key);
}
@Override
public boolean optBoolean(String key, boolean defaultValue) {
val value = table.get(key);
if (value instanceof Boolean)
return (boolean) value;
return defaultValue;
}
@Override
public boolean getBoolean(int key) {
return (Boolean) table.get(key);
}
@Override
public boolean optBoolean(int key, boolean defaultValue) {
val value = table.get(key);
if (value instanceof Boolean)
return (boolean) value;
return defaultValue;
}
@Override
public void set(int key, boolean value) {
table.put(key, value);
}
@Override
public void set(String key, boolean value) {
table.put(key, value);
}
@Override
public String getString(String key) {
return (String) table.get(key);
}
@Override
public String optString(String key, String defaultValue) {
val value = table.get(key);
if (value instanceof String)
return (String) value;
return defaultValue;
}
@Override
public String getString(int key) {
return (String) table.get(key);
}
@Override
public String optString(int key, String defaultValue) {
val value = table.get(key);
if (value instanceof String)
return (String) value;
return defaultValue;
}
@Override
public void set(int key, String value) {
table.put(key, value);
}
@Override
public void set(String key, String value) {
table.put(key, value);
}
@Nullable
@Override
public LuaTable getTable(String key) {
return new JNLuaTable((AbstractMap<?, ?>) table.get(key));
}
@Nullable
@Override
public LuaTable getTable(int key) {
return new JNLuaTable((AbstractMap<?, ?>) table.get(key));
}
@Override
public void set(int key, LuaTable value) {
table.put(key, value.getRawTable());
}
@Override
public void set(String key, LuaTable value) {
table.put(key, value.getRawTable());
}
@Override
public Set<String> getKeys() {
//TODO check if this is correct
val keys = table.keySet();
if (keys != null) {
return (Set<String>) (Set<?>) keys;
}
return IntStream.rangeClosed(1, table.size()).mapToObj(String::valueOf).collect(Collectors.toSet());
}
@Override
public int[] getAsIntArray() {
int[] result = new int[table.size()];
for (int i = 0; i < table.size(); i++) {
result[i] = (Integer) table.get(i + 1);
}
return result;
}
@Override
public int getSize() {
return table.size();
}
@Override
public Object getRawTable() {
return table;
}
}

View File

@ -0,0 +1,188 @@
package org.anime_game_servers.jnlua_engine
import org.anime_game_servers.lua.engine.LuaTable
import java.util.*
import java.util.stream.Collectors
import java.util.stream.IntStream
class JNLuaTable internal constructor(table: AbstractMap<*, *>) : LuaTable {
var table: AbstractMap<Any, Any>
init {
this.table = table as AbstractMap<Any, Any>
}
override fun has(key: String): Boolean {
return table.containsKey(key)
}
override fun get(key: String): Any? {
return table[key]
}
override fun get(key: Int): Any? {
return table[key]
}
override fun getInt(key: String): Int {
return (table[key] as Number).toInt()
}
override fun optInt(key: String, defaultValue: Int): Int {
val value = table[key]
return (value as? Number)?.toInt() ?: defaultValue
}
override fun getInt(key: Int): Int {
return (table[key] as Number).toInt()
}
override fun optInt(key: Int, defaultValue: Int): Int {
val value = table[key]
return (value as? Number)?.toInt() ?: defaultValue
}
override fun set(key: Int, value: Int) {
table[key] = value
}
override fun set(key: String, value: Int) {
table[key] = value
}
override fun getDouble(key: String): Double {
return (table[key] as Number).toDouble()
}
override fun optDouble(key: String, defaultValue: Double): Double {
val value = table[key]
return (value as? Number)?.toDouble() ?: defaultValue
}
override fun getDouble(key: Int): Double {
return (table[key] as Number).toDouble()
}
override fun optDouble(key: Int, defaultValue: Double): Double {
val value = table[key]
return (value as? Number)?.toDouble() ?: defaultValue
}
override fun getFloat(key: String): Float {
return (table[key] as Number).toFloat()
}
override fun optFloat(key: String, defaultValue: Float): Float {
val value = table[key]
return (value as? Number)?.toFloat() ?: defaultValue
}
override fun getFloat(key: Int): Float {
return (table[key] as Number).toFloat()
}
override fun optFloat(key: Int, defaultValue: Float): Float {
val value = table[key]
return (value as? Number)?.toFloat() ?: defaultValue
}
override fun set(key: Int, value: Double) {
table[key] = value
}
override fun set(key: String, value: Double) {
table[key] = value
}
override fun getBoolean(key: String): Boolean {
return table[key] as Boolean
}
override fun optBoolean(key: String, defaultValue: Boolean): Boolean {
val value = table[key]
return value as? Boolean ?: defaultValue
}
override fun getBoolean(key: Int): Boolean {
return table[key] as Boolean
}
override fun optBoolean(key: Int, defaultValue: Boolean): Boolean {
val value = table[key]
return value as? Boolean ?: defaultValue
}
override fun set(key: Int, value: Boolean) {
table[key] = value
}
override fun set(key: String, value: Boolean) {
table[key] = value
}
override fun getString(key: String): String {
return table[key] as String
}
override fun optString(key: String, defaultValue: String?): String? {
val value = table[key]
return value as? String ?: defaultValue
}
override fun getString(key: Int): String {
return table[key] as String
}
override fun optString(key: Int, defaultValue: String?): String? {
val value = table[key]
return value as? String ?: defaultValue
}
override fun set(key: Int, value: String) {
table[key] = value
}
override fun set(key: String, value: String) {
table[key] = value
}
override fun getTable(key: String): LuaTable? {
return (table[key] as? AbstractMap<*, *>?)?.let { JNLuaTable(it) }
}
override fun getTable(key: Int): LuaTable? {
return (table[key] as? AbstractMap<*, *>?)?.let { JNLuaTable(it) }
}
override fun set(key: Int, value: LuaTable) {
table[key] = value.getRawTable()
}
override fun set(key: String, value: LuaTable) {
table[key] = value.getRawTable()
}
override fun getKeys(): Set<String> {
//TODO check if this is correct
return table.keys.let {
it as? Set<String>
} ?: IntStream.rangeClosed(1, table.size).mapToObj { i: Int -> java.lang.String.valueOf(i) }
.collect(Collectors.toSet())
}
override fun getAsIntArray(): IntArray {
val result = IntArray(table.size)
for (i in 0 until table.size) {
result[i] = (table[i + 1] as Int)
}
return result
}
override fun getSize(): Int {
return table.size
}
override fun getRawTable(): Any {
return table
}
}

View File

@ -5,7 +5,11 @@ import lombok.Data;
import javax.annotation.Nonnull;
@Data @AllArgsConstructor
@Data
public class StaticClassWrapper {
@Nonnull private Class<?> staticClass;
public StaticClassWrapper(@Nonnull Class<?> staticClass){
this.staticClass = staticClass;
}
}

View File

@ -2,9 +2,10 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("java-library")
kotlin("jvm")
}
group = "org.anime_game_servers"
group = "org.anime_game_servers.lua"
version = "0.1"
repositories {
@ -14,13 +15,14 @@ repositories {
dependencies {
testImplementation(platform("org.junit:junit-bom:5.9.1"))
testImplementation("org.junit.jupiter:junit-jupiter")
implementation(project(":BaseLua"))
implementation("org.anime_game_servers.lua:base-jvm:0.1")
implementation("com.google.code.findbugs:jsr305:3.0.2")
implementation("org.anime_game_servers:luaj:3.0.3")
compileOnly("org.projectlombok:lombok:1.18.30")
annotationProcessor("org.projectlombok:lombok:1.18.30")
implementation("com.esotericsoftware:reflectasm:1.11.9")
implementation("io.github.oshai:kotlin-logging-jvm:5.1.0")
implementation(kotlin("stdlib-jdk8"))
}
tasks.test {
@ -35,3 +37,6 @@ publishing {
}
}
}
kotlin {
jvmToolchain(17)
}

View File

@ -1,129 +0,0 @@
package org.anime_game_servers.luaj_engine;
import io.github.oshai.kotlinlogging.KLogger;
import io.github.oshai.kotlinlogging.KotlinLogging;
import lombok.Getter;
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.ScriptConfig;
import org.anime_game_servers.lua.models.IntValueEnum;
import org.anime_game_servers.lua.models.ScriptType;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.lib.ResourceFinder;
import org.luaj.vm2.lib.jse.CoerceJavaToLua;
import org.luaj.vm2.script.LuajContext;
import javax.annotation.Nullable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
public class LuaJEngine implements LuaEngine {
private static KLogger logger = KotlinLogging.INSTANCE.logger(LuaJEngine.class.getName());
private final ScriptEngineManager manager;
private final LuajContext context;
@Getter(onMethod = @__(@Override))
private final ScriptConfig scriptConfig;
@Getter
private final ScriptEngine engine;
@Getter(onMethod = @__(@Override))
private final LuaJSerializer serializer;
public LuaJEngine(ScriptConfig scriptConfig) {
this.scriptConfig = scriptConfig;
manager = new ScriptEngineManager();
engine = manager.getEngineByName("luaj");
serializer = new LuaJSerializer();
context = (LuajContext) engine.getContext();
// Set engine to replace require as a temporary fix to missing scripts
context.globals.finder = new ResourceFinder() {
@Override
public InputStream findResource(String filename) {
val scriptPath = scriptConfig.getScriptLoader().getScriptPath(filename);
val stream = scriptConfig.getScriptLoader().openScript(scriptPath);
return stream!=null ? stream : new ByteArrayInputStream(new byte[0]);
}
@Override
public boolean useRawParamString() {
return true;
}
};
}
@Override
public <T extends Enum<T>> boolean addGlobalEnumByOrdinal(String name, T[] enumArray) {
LuaTable table = new LuaTable();
Arrays.stream(enumArray).forEach(e -> {
table.set(e.name(), e.ordinal());
table.set(e.name().toUpperCase(), e.ordinal());
});
context.globals.set(name, table);
return true;
}
@Override
public <T extends Enum<T> & IntValueEnum> boolean addGlobalEnumByIntValue(String name, T[] enumArray) {
LuaTable table = new LuaTable();
Arrays.stream(enumArray).forEach(e -> {
table.set(e.name(), e.getValue());
table.set(e.name().toUpperCase(), e.getValue());
});
context.globals.set(name, table);
return true;
}
@Override
public boolean addGlobalStaticClass(String name, Class<?> staticClass) {
try {
context.globals.set(name, CoerceJavaToLua.coerce(staticClass.getConstructor().newInstance()));
return true;
} catch (InstantiationException | IllegalAccessException | InvocationTargetException |
NoSuchMethodException e) {
logger.error(e, () -> "Failed to add static class to lua engine: " + name);
}
return false;
}
@Override
public boolean addObject(String name, Object object) {
context.globals.set(name, CoerceJavaToLua.coerce(object));
return true;
}
@Nullable
@Override
public LuaScript getScript(Path scriptPath, ScriptType scriptType) {
if (!Files.exists(scriptPath)) return null;
try {
return new LuaJScript(this, scriptPath);
} catch (IOException | ScriptException e) {
throw new RuntimeException(e);
}
}
@Override
public org.anime_game_servers.lua.engine.LuaTable getTable(Object table) {
if (table instanceof LuaTable)
return new LuaJTable(this, (LuaTable) table);
throw new IllegalArgumentException("Table must be a LuaTable");
}
@Override
public org.anime_game_servers.lua.engine.LuaTable createTable() {
return new LuaJTable(this, new LuaTable());
}
}

View File

@ -0,0 +1,105 @@
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 org.anime_game_servers.lua.models.ScriptType
import org.luaj.vm2.lib.ResourceFinder
import org.luaj.vm2.lib.jse.CoerceJavaToLua
import org.luaj.vm2.script.LuajContext
import java.io.ByteArrayInputStream
import java.io.IOException
import java.io.InputStream
import java.lang.reflect.InvocationTargetException
import java.nio.file.Files
import java.nio.file.Path
import java.util.*
import javax.script.ScriptEngine
import javax.script.ScriptEngineManager
import javax.script.ScriptException
class LuaJEngine(override val scriptConfig: ScriptConfig) : LuaEngine {
private val manager = ScriptEngineManager()
private val context: LuajContext
val engine: ScriptEngine = manager.getEngineByName("luaj")
override val serializer: LuaJSerializer = LuaJSerializer()
init {
context = engine.context as LuajContext
// Set engine to replace require as a temporary fix to missing scripts
context.globals.finder = object : ResourceFinder {
override fun findResource(filename: String): InputStream {
val scriptPath = scriptConfig.scriptLoader.getScriptPath(filename)
val stream = scriptConfig.scriptLoader.openScript(scriptPath!!)
return stream ?: ByteArrayInputStream(ByteArray(0))
}
override fun useRawParamString(): Boolean {
return true
}
}
}
override fun <T : Enum<*>> addGlobalEnum(name: String, enumArray: Array<T>): Boolean {
val table = org.luaj.vm2.LuaTable()
Arrays.stream(enumArray).forEach { e: T ->
val fieldName = getEnumFieldName(e)
val value = getEnumFieldValue(e)
table[fieldName] = value
table[fieldName.uppercase(Locale.getDefault())] = value
}
context.globals[name] = table
return true
}
override fun addGlobalStaticClass(name: String, staticClass: Class<*>): Boolean {
try {
context.globals[name] = CoerceJavaToLua.coerce(staticClass.getConstructor().newInstance())
return true
} catch (e: InstantiationException) {
logger.error(e) { "Failed to add static class to lua engine: $name" }
} catch (e: IllegalAccessException) {
logger.error(e) { "Failed to add static class to lua engine: $name" }
} catch (e: InvocationTargetException) {
logger.error(e) { "Failed to add static class to lua engine: $name" }
} catch (e: NoSuchMethodException) {
logger.error(e) { "Failed to add static class to lua engine: $name" }
}
return false
}
override fun addObject(name: String, `object`: Any): Boolean {
context.globals[name] = CoerceJavaToLua.coerce(`object`)
return true
}
override fun getScript(scriptPath: Path, scriptType: ScriptType): LuaScript? {
if (!Files.exists(scriptPath)) return null
try {
return LuaJScript(this, scriptPath)
} catch (e: IOException) {
throw RuntimeException(e)
} catch (e: ScriptException) {
throw RuntimeException(e)
}
}
override fun getTable(table: Any): LuaTable? {
if (table is org.luaj.vm2.LuaTable) return LuaJTable(this, table)
throw IllegalArgumentException("Table must be a LuaTable")
}
override fun createTable(): LuaTable {
return LuaJTable(this, org.luaj.vm2.LuaTable())
}
companion object {
private val logger = logger(LuaJEngine::class.java.name)
}
}

View File

@ -2,9 +2,12 @@ package org.anime_game_servers.luaj_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.serialize.BaseSerializer;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.ast.Str;
import javax.annotation.Nullable;
import java.lang.reflect.Field;
@ -31,34 +34,39 @@ public class LuaJSerializer extends BaseSerializer {
@Override
public <T> Map<String, T> toMap(Class<T> type, Object obj) {
return serializeMap(type, (LuaTable) obj);
return serializeMap(String.class, type, (LuaTable) obj);
}
@Nullable
private <T> T valueToType(Class<T> type, LuaValue keyValue) {
T object = null;
Object object = null;
if (keyValue.istable()) {
object = serialize(type, null, keyValue.checktable());
} else if (keyValue.isint()) {
object = (T) (Integer) keyValue.toint();
object = (Integer) keyValue.toint();
} else if (keyValue.isnumber()) {
object = (T) (Float) keyValue.tofloat(); // terrible...
object = (Float) keyValue.tofloat(); // terrible...
} else if (keyValue.isstring()) {
object = (T) keyValue.tojstring();
object = keyValue.tojstring();
} else if (keyValue.isboolean()) {
object = (T) (Boolean) keyValue.toboolean();
object = (Boolean) keyValue.toboolean();
} else {
object = (T) keyValue;
object = keyValue;
}
if (object == null) {
logger.warn(() -> "Can't serialize value: "+keyValue+" to type: "+type);
}
return object;
if(String.class.isAssignableFrom(type)){
return (T) String.valueOf(object);
} else {
return (T) object;
}
}
private <T> Map<String, T> serializeMap(Class<T> type, LuaTable table) {
Map<String, T> map = new HashMap<>();
private <K,V> Map<K, V> serializeMap(Class<K> typeKey, Class<V> typeValue, LuaTable table) {
Map<K, V> map = new HashMap<>();
if (table == null) {
return map;
@ -66,16 +74,24 @@ public class LuaJSerializer extends BaseSerializer {
try {
LuaValue[] keys = table.keys();
for (LuaValue k : keys) {
for (LuaValue luaKey : keys) {
try {
LuaValue keyValue = table.get(k);
T object = valueToType(type, keyValue);
if (object != null) {
map.put(String.valueOf(k), object);
K key = valueToType(typeKey, luaKey);
if(key == null){
logger.warn(() -> "Can't serialize key: "+luaKey+" to type: "+typeKey);
continue;
}
} catch (Exception ex) {
LuaValue luaValue = table.get(luaKey);
V value = valueToType(typeValue, luaValue);
if(value == null){
logger.warn(() -> "Can't serialize value: "+value+" to type: "+typeValue);
continue;
}
map.put(key, value);
} catch (Exception ex) {
logger.debug(ex, () -> "Exception serializing map");
}
}
} catch (Exception e) {
@ -125,6 +141,34 @@ public class LuaJSerializer extends BaseSerializer {
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;
}
public <T> T serialize(Class<T> type, @Nullable Field field, LuaTable table) {
T object = null;
@ -138,6 +182,18 @@ public class LuaJSerializer extends BaseSerializer {
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)) {

View File

@ -1,8 +1,8 @@
package org.anime_game_servers.luaj_engine;
import org.anime_game_servers.lua.engine.LuaValue;
import org.anime_game_servers.lua.engine.LuaValueJvm;
public class LuaJValue implements LuaValue {
public class LuaJValue implements LuaValueJvm {
private final org.luaj.vm2.LuaValue value;
private final LuaJEngine engine;

60
base/build.gradle.kts Normal file
View File

@ -0,0 +1,60 @@
plugins {
kotlin("multiplatform")
}
group = "org.anime_game_servers.lua"
version = "0.1"
kotlin {
jvm {
jvmToolchain(17)
withJava()
testRuns["test"].executionTask.configure {
useJUnitPlatform()
}
}
js(IR) {
browser {
commonWebpackConfig {
cssSupport {
enabled.set(true)
}
}
}
}
val hostOs = System.getProperty("os.name")
val isMingwX64 = hostOs.startsWith("Windows")
val nativeTarget = when {
hostOs == "Mac OS X" -> macosX64("native")
hostOs == "Linux" -> linuxX64("native")
isMingwX64 -> mingwX64("native")
else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
}
sourceSets {
val commonMain by getting {
dependencies {
api("org.anime_game_servers.core:base:0.1")
implementation(kotlin("reflect"))
}
}
val commonTest by getting {
}
val jvmMain by getting {
dependencies {
api("org.anime_game_servers.core:base-jvm:0.1")
implementation("io.github.oshai:kotlin-logging-jvm:5.1.0")
implementation("com.esotericsoftware:reflectasm:1.11.9")
implementation("com.google.code.findbugs:jsr305:3.0.2")
implementation(kotlin("reflect"))
implementation ("org.reflections:reflections:0.10.2")
}
}
val jvmTest by getting
val jsMain by getting
val jsTest by getting
val nativeMain by getting
val nativeTest by getting
}
}

View File

@ -15,5 +15,4 @@ interface LuaValue {
fun asDouble(): Double
fun asFloat(): Float
fun asString(): String?
fun <T> asObject(type: Class<T>): T?
}

View File

@ -1,6 +1,8 @@
package org.anime_game_servers.lua.models
class BooleanLuaValue(private val value: Boolean) : MockLuaValue() {
import kotlin.jvm.JvmField
open class BooleanLuaValue(private val value: Boolean) : MockLuaValue {
override fun isBoolean() = true
override fun asBoolean() = value
@ -10,10 +12,6 @@ class BooleanLuaValue(private val value: Boolean) : MockLuaValue() {
override fun asFloat() = if (value) 1f else 0f
override fun asString() = value.toString()
override fun <T> asObject(type: Class<T>): T? {
return if (type == Boolean::class.java) type.cast(value) as T else null
}
companion object {
@JvmField
val TRUE = BooleanLuaValue(true)

View File

@ -1,6 +1,8 @@
package org.anime_game_servers.lua.models
class IntLuaValue(private val value: Int) : MockLuaValue() {
import kotlin.jvm.JvmField
open class IntLuaValue(protected val value: Int) : MockLuaValue {
override fun isInteger() = true
override fun asBoolean() = value != 0
@ -10,10 +12,6 @@ class IntLuaValue(private val value: Int) : MockLuaValue() {
override fun asFloat() = value.toFloat()
override fun asString() = value.toString()
override fun <T> asObject(type: Class<T>): T? {
return if (type == Number::class.java) type.cast(value) as T? else null
}
companion object {
@JvmField
val ZERO = IntLuaValue(0)

View File

@ -2,7 +2,7 @@ package org.anime_game_servers.lua.models
import org.anime_game_servers.lua.engine.LuaValue
abstract class MockLuaValue : LuaValue {
interface MockLuaValue : LuaValue {
override fun isNull() = false
override fun isBoolean() = false
override fun isInteger() = false

View File

@ -0,0 +1,3 @@
package org.anime_game_servers.lua.serialize
interface Serializer

View File

@ -0,0 +1,85 @@
package org.anime_game_servers.lua.engine
import org.anime_game_servers.core.base.annotations.lua.LuaNames
import org.anime_game_servers.core.base.annotations.lua.LuaStatic
import org.anime_game_servers.core.base.interfaces.IntKey
import org.anime_game_servers.core.base.interfaces.IntValueEnum
import org.anime_game_servers.lua.models.ScriptType
import org.anime_game_servers.lua.serialize.Serializer
import org.reflections.Reflections
import java.nio.file.Path
interface LuaEngine {
val scriptConfig: ScriptConfig
fun <T : Enum<*>> addGlobalEnum(name: String, enumArray: Array<T>): Boolean
fun addGlobalStaticClass(name: String, staticClass: Class<*>): Boolean
fun addObject(name: String, `object`: Any): Boolean
val serializer: Serializer?
fun getScript(scriptName: String, scriptType: ScriptType): LuaScript? {
val scriptPath = scriptConfig.scriptLoader.getScriptPath(scriptName) ?: return null
return getScript(scriptPath, scriptType)
}
fun getScript(scriptPath: Path, scriptType: ScriptType): LuaScript?
fun getTable(table: Any): LuaTable?
fun createTable(): LuaTable
fun getEnumFieldName(enumValue: Enum<*>): String {
return enumValue::class.java.getField(enumValue.name).getAnnotation(LuaNames::class.java)?.let {
it.names.firstOrNull() ?: enumValue.name
} ?: enumValue.name
}
fun getEnumFieldValue(enumValue: Enum<*>): Int {
return when (enumValue){
is IntValueEnum -> enumValue.getValue()
is IntKey -> enumValue.getIntKey()
else -> enumValue.ordinal
}
}
private fun getLuaName(classObject: Class<*>): String {
return classObject.getAnnotation(LuaNames::class.java)?.let {
it.names.firstOrNull() ?: classObject.simpleName
} ?: classObject.simpleName
}
fun addGlobals() {
registeredEnums.forEach { enumClass ->
val enumArray = enumClass.enumConstants
addGlobalEnum(getLuaName(enumClass), enumArray)
}
registeredStaticClasses.forEach { staticClass ->
addGlobalStaticClass(getLuaName(staticClass), staticClass)
}
}
companion object {
var registeredEnums : MutableList<Class<Enum<*>>> = mutableListOf()
var registeredStaticClasses : MutableList<Class<*>> = mutableListOf()
@JvmStatic
fun registerNamespace(namespace: String){
val reflector = Reflections(namespace)
registerDefaults(reflector)
}
@JvmStatic
fun registerDefaults(reflector: Reflections){
reflector.getTypesAnnotatedWith(LuaStatic::class.java).forEach { annotatedClass ->
when{
annotatedClass.isAnnotation -> return@forEach
annotatedClass.isEnum -> {
registeredEnums.add(annotatedClass as Class<Enum<*>>)
}
else -> registeredStaticClasses.add(annotatedClass)
}
}
}
}
}

View File

@ -1,13 +1,12 @@
package org.anime_game_servers.lua.engine
import javax.annotation.Nonnull
import javax.script.ScriptException
interface LuaScript {
fun hasMethod(@Nonnull methodName: String): Boolean
fun hasMethod(methodName: String): Boolean
@Throws(ScriptException::class, NoSuchMethodException::class)
fun callMethod(@Nonnull methodName: String, vararg args: Any?): LuaValue?
fun callMethod(methodName: String, vararg args: Any?): LuaValue?
@Throws(ScriptException::class)
fun evaluate()

View File

@ -0,0 +1,81 @@
package org.anime_game_servers.lua.engine
interface LuaTable {
fun has(key: String): Boolean
fun get(key: String): Any?
fun get(key: Int): Any?
fun getInt(key: String): Int
fun optInt(key: String, defaultValue: Int): Int
fun getInt(key: Int): Int
fun optInt(key: Int, defaultValue: Int): Int
fun set(key: Int, value: Int)
fun set(key: String, value: Int)
fun getDouble(key: String): Double
fun optDouble(key: String, defaultValue: Double): Double
fun getDouble(key: Int): Double
fun optDouble(key: Int, defaultValue: Double): Double
fun getFloat(key: String): Float
fun optFloat(key: String, defaultValue: Float): Float
fun getFloat(key: Int): Float
fun optFloat(key: Int, defaultValue: Float): Float
fun set(key: Int, value: Double)
fun set(key: String, value: Double)
fun getBoolean(key: String): Boolean
fun optBoolean(key: String, defaultValue: Boolean): Boolean
fun getBoolean(key: Int): Boolean
fun optBoolean(key: Int, defaultValue: Boolean): Boolean
fun set(key: Int, value: Boolean)
fun set(key: String, value: Boolean)
fun getString(key: String): String?
fun optString(key: String, defaultValue: String?): String?
fun getString(key: Int): String?
fun optString(key: Int, defaultValue: String?): String?
fun set(key: Int, value: String)
fun set(key: String, value: String)
fun getTable(key: String): LuaTable?
fun getTable(key: Int): LuaTable?
fun set(key: Int, value: LuaTable)
fun set(key: String, value: LuaTable)
fun getKeys(): Set<String?>
fun getAsIntArray(): IntArray
fun getSize(): Int
fun getRawTable(): Any?
}

View File

@ -0,0 +1,5 @@
package org.anime_game_servers.lua.engine
interface LuaValueJvm : LuaValue {
fun <T> asObject(type: Class<T>): T?
}

View File

@ -0,0 +1,17 @@
package org.anime_game_servers.lua.models
import org.anime_game_servers.lua.engine.LuaValueJvm
class BooleanLuaValueJvm(private val value: Boolean) : BooleanLuaValue(value), LuaValueJvm {
override fun <T> asObject(type: Class<T>): T? {
return if (type == Boolean::class.java) type.cast(value) as T else null
}
companion object {
@JvmField
val TRUE = BooleanLuaValueJvm(true)
@JvmField
val FALSE = BooleanLuaValueJvm(false)
}
}

View File

@ -0,0 +1,18 @@
package org.anime_game_servers.lua.models
import org.anime_game_servers.lua.engine.LuaValueJvm
class IntLuaValueJvm(value: Int) : IntLuaValue(value), LuaValueJvm {
override fun <T> asObject(type: Class<T>): T? {
return if (type == Number::class.java) type.cast(value) as T? else null
}
companion object {
@JvmField
val ZERO = IntLuaValueJvm(0)
@JvmField
val ONE = IntLuaValueJvm(1)
@JvmField
val N_ONE = IntLuaValueJvm(-1)
}
}

View File

@ -0,0 +1,153 @@
package org.anime_game_servers.lua.serialize
import com.esotericsoftware.reflectasm.ConstructorAccess
import com.esotericsoftware.reflectasm.MethodAccess
import io.github.oshai.kotlinlogging.KotlinLogging.logger
import java.lang.reflect.Field
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import javax.annotation.Nonnull
abstract class BaseSerializer : Serializer {
abstract fun <T> toList(type: Class<T>?, obj: Any?): List<T>?
abstract fun <T> toObject(type: Class<T>?, obj: Any?): T
abstract fun <T> toMap(type: Class<T>?, obj: Any?): Map<String?, T>?
protected fun getSetterName(fieldName: String?): String? {
if (fieldName == null || fieldName.length == 0) {
return null
}
if (fieldName.length == 1) {
return "set" + fieldName.uppercase(Locale.getDefault())
}
return "set" + fieldName[0].uppercaseChar() + fieldName.substring(1)
}
protected fun <T> cacheType(type: Class<T>): Map<String, FieldMeta> {
if (fieldMetaCache.containsKey(type)) {
return fieldMetaCache[type]!!
}
if (!constructorCache.containsKey(type)) {
constructorCache.putIfAbsent(type, ConstructorAccess.get(type))
}
val methodAccess = Optional.ofNullable(methodAccessCache[type]).orElse(MethodAccess.get(type))
methodAccessCache.putIfAbsent(type, methodAccess)
val fieldMetaMap = HashMap<String, FieldMeta>()
val methodNameSet = HashSet(Arrays.stream(methodAccess.methodNames).toList())
var classtype: Class<*>? = type
while (classtype != null) {
Arrays.stream(classtype.declaredFields)
.forEach { field: Field ->
if (methodNameSet.contains(getSetterName(field.name))) {
val setter = getSetterName(field.name)
val index = methodAccess.getIndex(setter)
fieldMetaMap[field.name] = FieldMeta(field.name, setter, index, field.type, field)
} else {
field.isAccessible = true
fieldMetaMap[field.name] = FieldMeta(field.name, null, -1, field.type, field)
}
}
classtype = classtype.superclass
}
Arrays.stream(type.fields)
.filter { field: Field -> !fieldMetaMap.containsKey(field.name) }
.filter { field: Field -> methodNameSet.contains(getSetterName(field.name)) }
.forEach { field: Field ->
val setter = getSetterName(field.name)
val index = methodAccess.getIndex(setter)
fieldMetaMap[field.name] = FieldMeta(field.name, setter, index, field.type, field)
}
fieldMetaCache[type] = fieldMetaMap
return fieldMetaMap
}
protected fun set(`object`: Any?, @Nonnull fieldMeta: FieldMeta, methodAccess: MethodAccess?, value: Int) {
try {
if (methodAccess != null && fieldMeta.setter != null) {
methodAccess.invoke(`object`, fieldMeta.index, value)
} else if (fieldMeta.field != null) {
fieldMeta.field.setInt(`object`, value)
}
} catch (ex: Exception) {
logger.warn(ex) { "Failed to set field " + fieldMeta.name + " of type " + fieldMeta.type + " to value " + value }
}
}
protected fun set(`object`: Any?, @Nonnull fieldMeta: FieldMeta, methodAccess: MethodAccess?, value: Double) {
try {
if (methodAccess != null && fieldMeta.setter != null) {
methodAccess.invoke(`object`, fieldMeta.index, value)
} else if (fieldMeta.field != null) {
fieldMeta.field.setDouble(`object`, value)
}
} catch (ex: Exception) {
logger.warn(ex) { "Failed to set field " + fieldMeta.name + " of type " + fieldMeta.type + " to value " + value }
}
}
protected fun set(`object`: Any?, @Nonnull fieldMeta: FieldMeta, methodAccess: MethodAccess?, value: Float) {
try {
if (methodAccess != null && fieldMeta.setter != null) {
methodAccess.invoke(`object`, fieldMeta.index, value)
} else if (fieldMeta.field != null) {
fieldMeta.field.setFloat(`object`, value)
}
} catch (ex: Exception) {
logger.warn(ex) { "Failed to set field " + fieldMeta.name + " of type " + fieldMeta.type + " to value " + value }
}
}
protected fun set(`object`: Any?, @Nonnull fieldMeta: FieldMeta, methodAccess: MethodAccess?, value: Long) {
try {
if (methodAccess != null && fieldMeta.setter != null) {
methodAccess.invoke(`object`, fieldMeta.index, value)
} else if (fieldMeta.field != null) {
fieldMeta.field.setLong(`object`, value)
}
} catch (ex: Exception) {
logger.warn(ex) { "Failed to set field " + fieldMeta.name + " of type " + fieldMeta.type + " to value " + value }
}
}
protected fun set(`object`: Any?, @Nonnull fieldMeta: FieldMeta, methodAccess: MethodAccess?, value: Boolean) {
try {
if (methodAccess != null && fieldMeta.setter != null) {
methodAccess.invoke(`object`, fieldMeta.index, value)
} else if (fieldMeta.field != null) {
fieldMeta.field.setBoolean(`object`, value)
}
} catch (ex: Exception) {
logger.warn(ex) { "Failed to set field " + fieldMeta.name + " of type " + fieldMeta.type + " to value " + value }
}
}
protected fun set(`object`: Any?, @Nonnull fieldMeta: FieldMeta, methodAccess: MethodAccess?, value: Any) {
try {
if (methodAccess != null && fieldMeta.setter != null) {
methodAccess.invoke(`object`, fieldMeta.index, value)
} else if (fieldMeta.field != null) {
fieldMeta.field[`object`] = value
}
} catch (ex: Exception) {
logger.warn(ex) { "Failed to set field " + fieldMeta.name + " of type " + fieldMeta.type + " to value " + value }
}
}
companion object {
private val logger = logger(BaseSerializer::class.java.name)
@JvmField
protected val methodAccessCache: MutableMap<Class<*>, MethodAccess> = ConcurrentHashMap()
@JvmField
protected val constructorCache: MutableMap<Class<*>, ConstructorAccess<*>> = ConcurrentHashMap()
@JvmField
protected val fieldMetaCache: MutableMap<Class<*>, Map<String, FieldMeta>> = ConcurrentHashMap()
}
}

View File

@ -3,7 +3,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("java")
id("maven-publish")
kotlin("jvm") version "1.9.10"
kotlin("multiplatform") version "1.9.22" apply false
}
group = "org.anime_game_servers"
@ -13,15 +13,15 @@ repositories {
mavenCentral()
}
dependencies {
/*dependencies {
testImplementation(platform("org.junit:junit-bom:5.9.1"))
testImplementation("org.junit.jupiter:junit-jupiter")
implementation(kotlin("stdlib-jdk8"))
}
}*/
tasks.test {
/*tasks.test {
useJUnitPlatform()
}
}*/
allprojects {
apply(plugin ="maven-publish")
@ -53,11 +53,11 @@ allprojects {
}
}
}
val compileKotlin: KotlinCompile by tasks
/*val compileKotlin: KotlinCompile by tasks
compileKotlin.kotlinOptions {
jvmTarget = "1.8"
}
val compileTestKotlin: KotlinCompile by tasks
compileTestKotlin.kotlinOptions {
jvmTarget = "1.8"
}
}*/

View File

@ -2,4 +2,4 @@ rootProject.name = "AnimeGameLua"
include("LuaJEngine")
include("JNLuaEngine")
include("GILua")
include("BaseLua")
include("base")