Skip to content

Commit

Permalink
testing exp lua support (BAREBONES)
Browse files Browse the repository at this point in the history
  • Loading branch information
charlesisfeline committed Jul 31, 2024
1 parent 1cddfcf commit 019d700
Show file tree
Hide file tree
Showing 4 changed files with 326 additions and 2 deletions.
1 change: 1 addition & 0 deletions libs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<git name="flxanimate" url="https://github.com/FNF-CNE-Devs/flxanimate" />
<git name="hxdiscord_rpc" url="https://github.com/FNF-CNE-Devs/hxdiscord_rpc" />
<lib name="hxvlc" />
<git name="linc_luajit" url="https://github.com/nebulazorua/linc_luajit" />

<!-- Documentation and other features -->
<git name="away3d" url="https://github.com/FNF-CNE-Devs/away3d" />
Expand Down
5 changes: 5 additions & 0 deletions project.xml
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@
<!-- Comment this out to use the default OpenFL assets system for the assets/ folder. !-->
<haxedef name="USE_ADAPTED_ASSETS" unless="web" />

<!-- Comment this out to disable the use of luajit. !-->
<define name="ENABLE_LUA" if="windows || mac || linux"/>

<!-- Comment this out to disable support for custom ndlls. !-->
<haxedef name="NDLLS_SUPPORTED" unless="web || mobile || iphonesim" />

Expand All @@ -150,8 +153,10 @@
<!-- Comment this out to disable dark mode windows -->
<define name="DARK_MODE_WINDOW"/>

<haxedef name="ENABLE_LUA" if="ENABLE_LUA" />
<haxedef name="VIDEO_CUTSCENES" if="VIDEO_CUTSCENES" />
<haxedef name="UPDATE_CHECKING" if="UPDATE_CHECKING" />
<haxedef name="LUAJIT_ENABLE_LUA52COMPAT" if="ENABLE_LUA" />
<haxedef name="DISCORD_RPC" if="DISCORD_RPC" />
<haxedef name="SHOW_BUILD_ON_FPS" if="SHOW_BUILD_ON_FPS" />
<haxedef name="SOFTCODED_CLASSES" if="SOFTCODED_CLASSES" />
Expand Down
309 changes: 309 additions & 0 deletions source/funkin/backend/scripting/LuaScript.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,309 @@
package funkin.backend.scripting;

#if ENABLE_LUA
import llua.Lua;
import llua.LuaL;
import llua.State;
import llua.Convert;
import llua.Macro.*;
import haxe.DynamicAccess;

import openfl.utils.Assets;

using llua.Lua;
using llua.LuaL;
using llua.Convert;

class LuaScript extends Script {
public var state:State = null;

public var lastStackID:Int = 0;
public var stack:Map<Int, Dynamic> = [];

public var luaCallbacks:Map<String, Dynamic> = [];

public override function onCreate(path:String) {
super.onCreate(path);

state = LuaL.newstate();
Lua.set_callbacks_function(cpp.Callable.fromStaticFunction(callback_handler));
LuaL.openlibs(state);
Lua.register_hxtrace_func(cpp.Callable.fromStaticFunction(print_function));
state.register_hxtrace_lib();

luaCallbacks["__onPointerIndex"] = onPointerIndex;
luaCallbacks["__onPointerNewIndex"] = onPointerNewIndex;
luaCallbacks["__onPointerCall"] = onPointerCall;
luaCallbacks["__gc"] = onGarbageCollection;

state.newmetatable("__funkinMetaTable");

state.pushstring('__index');
state.pushcfunction(cpp.Callable.fromStaticFunction(__index));
state.settable(-3);

state.pushstring('__newindex');
state.pushcfunction(cpp.Callable.fromStaticFunction(__newindex));
state.settable(-3);

state.pushstring('__call');
state.pushcfunction(cpp.Callable.fromStaticFunction(__call));
state.settable(-3);

state.setglobal("__funkinMetaTable");
}

public override function onLoad() {
if (state.dostring(Assets.getText(path)) != 0)
this.error('${state.tostring(-1)}');
}

public static var callbackReturnVariables = [];

public override function onCall(funcName:String, args:Array<Dynamic>):Dynamic {
state.settop(0);
state.getglobal(funcName);

if (state.type(-1) != Lua.LUA_TFUNCTION)
return null;

for (k=>val in args)
pushArg(val);

if (state.pcall(args.length, 1, 0) != 0) {
this.error('${state.tostring(-1)}');
return null;
}

var v = fromLua(state.gettop());
state.settop(0);
return v;
}

public override function set(variable:String, value:Dynamic) {
pushArg(value);
state.setglobal(variable);
}

public override function onDestroy() {
super.onDestroy();

if (state != null) {
Lua.close(state);
state = null;
}
}
public override function reload() {
Logs.trace('Hot-reloading is currently not supported on Lua.', WARNING);
}

// UTILS
#if REGION

static inline function print_function(s:String) : Int {
if (Script.curScript != null)
Script.curScript.trace(s);
return 0;
}

public function fromLua(stackPos:Int):Dynamic {
var ret:Any = null;

switch(state.type(stackPos)) {
case Lua.LUA_TNIL:
ret = null;
case Lua.LUA_TBOOLEAN:
ret = state.toboolean(stackPos);
case Lua.LUA_TNUMBER:
ret = state.tonumber(stackPos);
case Lua.LUA_TSTRING:
ret = state.tostring(stackPos);
case Lua.LUA_TTABLE:
ret = toHaxeObj(stackPos);
case Lua.LUA_TFUNCTION:
null; // no support for functions yet
case Lua.LUA_TUSERDATA:
ret = LuaL.ref(l, Lua.LUA_REGISTRYINDEX);
trace("userdata\n");
case Lua.LUA_TLIGHTUSERDATA:
ret = LuaL.ref(l, Lua.LUA_REGISTRYINDEX);
trace("lightuserdata\n");
case Lua.LUA_TTHREAD:
ret = null;
trace("thread\n");
case idk:
ret = null;
trace("return value not supported\n"+Std.string(idk)+" - "+stackPos);
}


if (ret is Dynamic && Reflect.hasField(ret, "__stack_id")) {
// is a "pointer"! convert it back.
var pos:Int = Reflect.field(ret, "__stack_id");
return stack[pos];
}
return ret;
}

public function pushArg(val:Dynamic) {
switch (Type.typeof(val)) {
case Type.ValueType.TNull:
state.pushnil();
case Type.ValueType.TBool:
state.pushboolean(val);
case Type.ValueType.TInt:
state.pushinteger(cast(val, Int));
case Type.ValueType.TFloat:
state.pushnumber(val);
case Type.ValueType.TClass(String):
state.pushstring(cast(val, String));
case Type.ValueType.TClass(Array):
var arr:Array<Any> = cast val;
var size:Int = arr.length;
state.createtable(size, 0);

for (i in 0...size) {
state.pushnumber(i + 1);
pushArg(arr[i]);
state.settable(-3);
}
case Type.ValueType.TObject:
@:privateAccess
state.objectToLua(val); // {}
default:

var p = {
__stack_id: lastStackID++,
};
state.toLua(p);
state.getmetatable("__funkinMetaTable");
state.setmetatable(-2);

state.pushstring('__gc');
state.pushcfunction(cpp.Callable.fromStaticFunction(__gc));
state.settable(-3);

stack[p.__stack_id] = val;
}
}
public static function __index(state:StatePointer):Int {
return callback_handler(cast cpp.Pointer.fromRaw(state).ref, "__onPointerIndex");
}
public static function __newindex(state:StatePointer):Int {
return callback_handler(cast cpp.Pointer.fromRaw(state).ref, "__onPointerNewIndex");
}
public static function __call(state:StatePointer):Int {
return callback_handler(cast cpp.Pointer.fromRaw(state).ref, "__onPointerCall");
}
public static function __gc(state:StatePointer):Int {
// callbackPreventAutoConvert = true;
var v = callback_handler(cast cpp.Pointer.fromRaw(state).ref, "__gc");
// callbackPreventAutoConvert = false;
return v;
}

public function onPointerIndex(obj:Dynamic, key:String) {
if (obj != null)
return Reflect.getProperty(obj, key);
return null;
}

public function onPointerCall(obj:Dynamic, ...args:Any) {
trace(obj);
trace(args);
if (obj != null && Reflect.isFunction(obj))
return Reflect.callMethod(null, obj, args.toArray());
return null;
}

public function onPointerNewIndex(obj:Dynamic, key:String, val:Dynamic) {
if (key == "__gc") return null;

if (obj != null)
Reflect.setProperty(obj, key, val);
return null;
}

public function onGarbageCollection(obj:Dynamic) {
trace(obj);
if (Reflect.hasField(obj, "__stack_id")) {
trace('Clearing item ID: ${obj.__stack_id} from stack due to garbage collection');
stack.remove(obj.__stack_id);
}
}

private static var callbackPreventAutoConvert:Bool = false;
public static function callback_handler(l:State, fname:String):Int {

if (!(Script.curScript is LuaScript))
return 0;
var curLua:LuaScript = cast Script.curScript;

var cbf = curLua.luaCallbacks.get(fname);
callbackReturnVariables = [];

if (cbf == null || !Reflect.isFunction(cbf))
return 0;

var nparams:Int = Lua.gettop(l);
var args:Array<Dynamic> = callbackPreventAutoConvert ? [for(i in 0...nparams) l.fromLua(-nparams + i)] : [for(i in 0...nparams) curLua.fromLua(-nparams + i)];

var ret:Dynamic = null;

try {
ret = (nparams > 0) ? Reflect.callMethod(null, cbf, args) : cbf();
} catch(e) {
curLua.error(e.details()); // for super cool mega logging!!!
throw e;
}
Lua.settop(l, 0);

if (callbackReturnVariables.length <= 0)
callbackReturnVariables.push(ret);
for(e in callbackReturnVariables)
curLua.pushArg(e);

/* return the number of results */
return callbackReturnVariables.length;

}

public function toHaxeObj(i:Int):Any {
var count = 0;
var array = true;

loopTable(state, i, {
if(array) {
if(Lua.type(state, -2) != Lua.LUA_TNUMBER) array = false;
else {
var index = Lua.tonumber(state, -2);
if(index < 0 || Std.int(index) != index) array = false;
}
}
count++;
});

return
if(count == 0) {
{};
} else if(array) {
var v = [];
loopTable(state, i, {
var index = Std.int(Lua.tonumber(state, -2)) - 1;
v[index] = fromLua(-1);
});
cast v;
} else {
var v:DynamicAccess<Any> = {};
loopTable(state, i, {
switch Lua.type(state, -2) {
case t if(t == Lua.LUA_TSTRING): v.set(Lua.tostring(state, -2), fromLua(-1));
case t if(t == Lua.LUA_TNUMBER):v.set(Std.string(Lua.tonumber(state, -2)), fromLua(-1));
}
});
cast v;
}
}
#end
}
#end
13 changes: 11 additions & 2 deletions source/funkin/backend/scripting/Script.hx
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,7 @@ class Script extends FlxBasic implements IFlxDestroyable {
var arr = Assets.getText(path).split("________PACKSEP________");
fromString(arr[1], arr[0]);
case "lua":
Logs.trace("Lua is not supported in this engine. Use HScript instead.", ERROR);
new DummyScript(path);
new LuaScript(path);
default:
new DummyScript(path);
}
Expand Down Expand Up @@ -325,4 +324,14 @@ class Script extends FlxBasic implements IFlxDestroyable {
public function onCreate(path:String) {}

public function onLoad() {}

private static var __scriptType:Class<Script>;
public static function onFinalize(s:Script) {
#if cpp
// destroy everything
Sys.println("GARBAGE COLLECTOR - FINALIZING");
s.destroy();
Sys.println("GARBAGE COLLECTOR - FINALIZING");
#end
}
}

0 comments on commit 019d700

Please sign in to comment.