From fcb9dd4ed4b69225c9a8d1102dc1c6b276162b60 Mon Sep 17 00:00:00 2001 From: "Vinegar (Moxie)" <124418090+moxie-coder@users.noreply.github.com> Date: Sun, 2 Nov 2025 14:00:23 -0500 Subject: [PATCH 1/2] refactor: replace linc_luajit with hxluajit This will need EXTENSIVE testing btw to make sure most mods still work and such, because despite it still being luaJIT, it's an different implementation --- Project.xml | 4 +- hmm.json | 4 +- setup/unix.sh | 2 +- setup/windows.bat | 2 +- source/Achievements.hx | 15 +- source/DiscordClient.hx | 7 +- source/Main.hx | 2 - source/import.hx | 4 +- source/psychlua/CallbackHandler.hx | 54 ----- source/psychlua/Convert.hx | 306 +++++++++++++++++++++++++ source/psychlua/CustomSubstate.hx | 6 +- source/psychlua/FlxAnimateFunctions.hx | 12 +- source/psychlua/FunkinLua.hx | 45 ++-- source/psychlua/HScript.hx | 2 +- source/psychlua/LuaFunction.hx | 62 +++++ source/psychlua/LuaUtils.hx | 21 +- 16 files changed, 443 insertions(+), 105 deletions(-) delete mode 100644 source/psychlua/CallbackHandler.hx create mode 100644 source/psychlua/Convert.hx create mode 100644 source/psychlua/LuaFunction.hx diff --git a/Project.xml b/Project.xml index f60790e76c2..7686581b716 100644 --- a/Project.xml +++ b/Project.xml @@ -124,10 +124,8 @@ - + - - diff --git a/hmm.json b/hmm.json index 5d92add16c9..354ea150ac0 100644 --- a/hmm.json +++ b/hmm.json @@ -75,11 +75,11 @@ "url": "https://github.com/MAJigsaw77/hxdiscord_rpc" }, { - "name": "linc_luajit", + "name": "hxluajit", "type": "git", "dir": null, "ref": "master", - "url": "https://github.com/JS-Engine-things/linc_luajit" + "url": "https://github.com/JS-Engine-things/hxluajit" }, { "name": "funkin.vis", diff --git a/setup/unix.sh b/setup/unix.sh index 8bc2e0cbd8a..3e7d7a5a451 100644 --- a/setup/unix.sh +++ b/setup/unix.sh @@ -18,7 +18,7 @@ haxelib git away3d https://github.com/moxie-coder/away3d --quiet haxelib git tjson https://github.com/moxie-coder/tjson --quiet haxelib git hxcpp https://github.com/FunkinCrew/hxcpp --quiet haxelib git flxanimate https://github.com/Dot-Stuff/flxanimate 768740a56b26aa0c072720e0d1236b94afe68e3e --quiet -haxelib git linc_luajit https://github.com/JS-Engine-things/linc_luajit --quiet +haxelib git hxluajit https://github.com/JS-Engine-things/hxluajit --quiet haxelib git funkin.vis https://github.com/JS-Engine-things/funkVis-FrequencyFixed --quiet haxelib git grig.audio https://github.com/JS-Engine-things/grig.audio --quiet haxelib git hxdiscord_rpc https://github.com/MAJigsaw77/hxdiscord_rpc --quiet --skip-dependencies diff --git a/setup/windows.bat b/setup/windows.bat index c8f5ba4b92a..6efa93cb477 100644 --- a/setup/windows.bat +++ b/setup/windows.bat @@ -16,7 +16,7 @@ haxelib git away3d https://github.com/moxie-coder/away3d --quiet haxelib git tjson https://github.com/moxie-coder/tjson --quiet haxelib git hxcpp https://github.com/FunkinCrew/hxcpp --quiet haxelib git flxanimate https://github.com/Dot-Stuff/flxanimate 768740a56b26aa0c072720e0d1236b94afe68e3e --quiet -haxelib git linc_luajit https://github.com/JS-Engine-things/linc_luajit --quiet +haxelib git hxluajit https://github.com/JS-Engine-things/hxluajit --quiet haxelib git funkin.vis https://github.com/JS-Engine-things/funkVis-FrequencyFixed --quiet haxelib git grig.audio https://github.com/JS-Engine-things/grig.audio --quiet haxelib git hxdiscord_rpc https://github.com/MAJigsaw77/hxdiscord_rpc --quiet --skip-dependencies diff --git a/source/Achievements.hx b/source/Achievements.hx index 2195a731c6e..a9c9539f433 100644 --- a/source/Achievements.hx +++ b/source/Achievements.hx @@ -3,6 +3,9 @@ import flixel.FlxCamera; #if ACHIEVEMENTS_ALLOWED import haxe.Exception; import objects.AchievementPopup; +#if LUA_ALLOWED +import psychlua.FunkinLua.State; +#end typedef Achievement = { @@ -261,7 +264,7 @@ class Achievements { #if LUA_ALLOWED public static function addLuaCallbacks(lua:State) { - Lua_helper.add_callback(lua, "getAchievementScore", function(name:String):Float + Convert.addCallback(lua, "getAchievementScore", function(name:String):Float { if(!achievements.exists(name)) { @@ -270,7 +273,7 @@ class Achievements { } return getScore(name); }); - Lua_helper.add_callback(lua, "setAchievementScore", function(name:String, ?value:Float = 1, ?saveIfNotUnlocked:Bool = true):Float + Convert.addCallback(lua, "setAchievementScore", function(name:String, ?value:Float = 1, ?saveIfNotUnlocked:Bool = true):Float { if(!achievements.exists(name)) { @@ -279,7 +282,7 @@ class Achievements { } return setScore(name, value, saveIfNotUnlocked); }); - Lua_helper.add_callback(lua, "addAchievementScore", function(name:String, ?value:Float = 1, ?saveIfNotUnlocked:Bool = true):Float + Convert.addCallback(lua, "addAchievementScore", function(name:String, ?value:Float = 1, ?saveIfNotUnlocked:Bool = true):Float { if(!achievements.exists(name)) { @@ -288,7 +291,7 @@ class Achievements { } return addScore(name, value, saveIfNotUnlocked); }); - Lua_helper.add_callback(lua, "unlockAchievement", function(name:String):Dynamic + Convert.addCallback(lua, "unlockAchievement", function(name:String):Dynamic { if(!achievements.exists(name)) { @@ -297,7 +300,7 @@ class Achievements { } return unlock(name); }); - Lua_helper.add_callback(lua, "isAchievementUnlocked", function(name:String):Dynamic + Convert.addCallback(lua, "isAchievementUnlocked", function(name:String):Dynamic { if(!achievements.exists(name)) { @@ -306,7 +309,7 @@ class Achievements { } return isUnlocked(name); }); - Lua_helper.add_callback(lua, "achievementExists", function(name:String) return achievements.exists(name)); + Convert.addCallback(lua, "achievementExists", function(name:String) return achievements.exists(name)); } #end #end diff --git a/source/DiscordClient.hx b/source/DiscordClient.hx index 88b5b94373a..e560476aaa9 100644 --- a/source/DiscordClient.hx +++ b/source/DiscordClient.hx @@ -6,6 +6,9 @@ import cpp.ConstCharStar; import cpp.Function; import cpp.RawConstPointer; #end +#if LUA_ALLOWED +import psychlua.FunkinLua.State; +#end import hxdiscord_rpc.Discord; import hxdiscord_rpc.Types; @@ -150,8 +153,8 @@ class DiscordClient #if LUA_ALLOWED public static function addLuaCallbacks(lua:State) { - Lua_helper.add_callback(lua, "changeDiscordPresence", changePresence); - Lua_helper.add_callback(lua, "changeDiscordClientID", function(?newID:String) { + Convert.addCallback(lua, "changeDiscordPresence", changePresence); + Convert.addCallback(lua, "changeDiscordClientID", function(?newID:String) { if(newID == null) newID = _defaultID; clientID = newID; }); diff --git a/source/Main.hx b/source/Main.hx index 9f7e6cbde75..32afc2e02f0 100644 --- a/source/Main.hx +++ b/source/Main.hx @@ -78,8 +78,6 @@ class Main extends Sprite { FlxG.sound = soundFrontEnd; funkinGame._customSoundTray = objects.CustomSoundTray.CustomSoundTray; } - // turns out I forgot this, I'm a bit dumb for that - #if LUA_ALLOWED Lua.set_callbacks_function(cpp.Callable.fromStaticFunction(psychlua.CallbackHandler.call)); #end addChild(funkinGame); diff --git a/source/import.hx b/source/import.hx index 53a88efb676..6f3748d47f9 100644 --- a/source/import.hx +++ b/source/import.hx @@ -8,8 +8,8 @@ import sys.io.*; #end #if LUA_ALLOWED -import llua.*; -import llua.Lua; +import hxluajit.*; +import hxluajit.Types; import psychlua.*; #else import psychlua.FunkinLua; // TODO: test and seperate this into LuaUtils diff --git a/source/psychlua/CallbackHandler.hx b/source/psychlua/CallbackHandler.hx deleted file mode 100644 index 82df5876d8d..00000000000 --- a/source/psychlua/CallbackHandler.hx +++ /dev/null @@ -1,54 +0,0 @@ -package psychlua; - -class CallbackHandler -{ - public static inline function call(l:State, fname:String):Int - { - try - { - //trace('calling $fname'); - var cbf:Dynamic = Lua_helper.callbacks.get(fname); - - //Local functions have the lowest priority - //This is to prevent a "for" loop being called in every single operation, - //so that it only loops on reserved/special functions - if(cbf == null) - { - //trace('looping thru scripts'); - for (script in PlayState.instance.luaArray) - if(script != null && script.lua == l) - { - //trace('found script'); - cbf = script.callbacks.get(fname); - break; - } - } - - if(cbf == null) return 0; - - var nparams:Int = Lua.gettop(l); - var args:Array = []; - - for (i in 0...nparams) { - args[i] = Convert.fromLua(l, i + 1); - } - - var ret:Dynamic = null; - /* return the number of results */ - - ret = Reflect.callMethod(null,cbf,args); - - if(ret != null){ - Convert.toLua(l, ret); - return 1; - } - } - catch(e:Dynamic) - { - if(Lua_helper.sendErrorsToLua) {LuaL.error(l, 'CALLBACK ERROR! ${if(e.message != null) e.message else e}');return 0;} - trace(e); - throw(e); - } - return 0; - } -} \ No newline at end of file diff --git a/source/psychlua/Convert.hx b/source/psychlua/Convert.hx new file mode 100644 index 00000000000..35d89a0d1e8 --- /dev/null +++ b/source/psychlua/Convert.hx @@ -0,0 +1,306 @@ +package psychlua; + +import haxe.ds.*; +import psychlua.FunkinLua.State; +import hxluajit.Types; + + +/** + * Some borrowed code from hxluajit-wrapper. + * @see https://github.com/MAJigsaw77/hxluajit-wrapper/blob/main/hxluajit/wrapper/LuaConverter.hx + * + * We didn't use hxluajit-wrapper because we wanted to have our functions and methods as similar as possible to linc_luajit + */ +class Convert +{ + public static function addCallback(l:State, name:String, func:Dynamic) + { + // PsychLua expects the function to be null for local callbacks, too lazy to do something about that + if (Type.typeof(func) != TFunction || func == null) + return; + + callbacks.set(name, func); + + Lua.pushstring(l, name); + Lua.pushcclosure(l, cpp.Callable.fromStaticFunction(handleCallback), 1); + Lua.setglobal(l, name); + } + + public static function removeCallback(l:State, name:String) + { + if (!callbacks.exists(name)) + return; + + callbacks.remove(name); + + Lua.pushnil(l); + Lua.setglobal(l, name); + } + + public static function toLua(l:State, v:Dynamic):Bool + { + switch (Type.typeof(v)) + { + case TInt: + Lua.pushinteger(l, cast(v, Int)); + case TFloat: + Lua.pushnumber(l, cast(v, Float)); + case TBool: + Lua.pushboolean(l, v == true ? 1 : 0); + case TObject: + final fields:Array = Reflect.fields(v); + + Lua.createtable(l, fields.length, 0); + + for (field in fields) + { + Lua.pushstring(l, field); + toLua(l, Reflect.field(v, field)); + Lua.settable(l, -3); + } + case TClass(String): + Lua.pushstring(l, cast(v, String)); + case TClass(Array): + final elements:Array = v; + + Lua.createtable(l, elements.length, 0); + + for (i in 0...elements.length) + { + Lua.pushinteger(l, i + 1); + toLua(l, elements[i]); + Lua.settable(l, -3); + } + case TClass(IntMap) | TClass(StringMap) | TClass(ObjectMap): + final values:Map = v; + + Lua.createtable(l, Lambda.count(values), 0); + + for (key => value in values) + { + Lua.pushstring(l, key); + toLua(l, value); + Lua.settable(l, -3); + } + case TNull: + Lua.pushnil(l); + default: + Lua.pushnil(l); + return false; + } + return true; + } + + public static function fromLua(l:State, idx:Int):Dynamic + { + var ret:Dynamic = null; + + switch (Lua.type(l, idx)) + { + case type if (type == Lua.TNUMBER): + ret = Lua.tonumber(l, idx); + case type if (type == Lua.TSTRING): + ret = Lua.tostring(l, idx).toString(); + case type if (type == Lua.TBOOLEAN): + ret = Lua.toboolean(l, idx) == 1; + case type if (type == Lua.TTABLE): + ret = convertTable(l, idx); + case type if (type == Lua.TFUNCTION): + ret = new LuaFunction(cpp.Pointer.fromRaw(l), LuaL.ref(l, Lua.REGISTRYINDEX)); + case type if (type == Lua.TUSERDATA || type == Lua.TLIGHTUSERDATA): + ret = cpp.Pointer.fromRaw(Lua.touserdata(l, idx)); + default: + ret = null; + } + + return ret; + } + + public static function callFunctionWithoutName(l:State, args:Array):Array + { + for (arg in args) + toLua(l, arg); + + final status:Int = Lua.pcall(l, args.length, Lua.MULTRET, 0); + + if (status != Lua.OK) + { + var error = Lua.tostring(l, -1); + trace('Error calling a function without name: $error'); + Lua.pop(l, 1); + + return []; + } + + final args:Array = []; + + { + final count:Int = Lua.gettop(l); + + for (i in 0...count) + args.push(fromLua(l, i + 1)); + + Lua.pop(l, count); + } + + return args; + } + + @:noCompletion + private static function convertTable(l:State, idx:Int):Dynamic + { + var isArray:Bool = true; + + var count:Int = 0; + + iterateTable(l, idx, function():Void + { + if (isArray) + { + if (Lua.type(l, -2) == Lua.TNUMBER) + { + final index:Lua_Integer = Lua.tointeger(l, -2); + + if (index < 0) + isArray = false; + } + else + isArray = false; + } + + count++; + }); + + if (count == 0) + return {}; + + if (isArray) + { + final obj:Array = []; + + iterateTable(l, idx, function():Void + { + obj[Lua.tointeger(l, -2) - 1] = fromLua(l, -1); + }); + + return obj; + } + else + { + final obj:haxe.DynamicAccess = {}; + + iterateTable(l, idx, function():Void + { + obj.set(Std.string(fromLua(l, -2)), fromLua(l, -1)); + }); + + return obj; + } + } + + @:noCompletion + private static function iterateTable(l:State, idx:Int, fn:Void->Void):Void + { + Lua.pushnil(l); + + while (Lua.next(l, idx < 0 ? idx - 1 : idx) != 0) + { + fn(); + + Lua.pop(l, 1); + } + } + + @:noCompletion + private static var funcs = []; + + @:noCompletion + private static function handleMethod(l:State):Int + { + var argsLength:Int = Lua.gettop(l); + var method = funcs[cast Lua.tonumber(l, Lua.upvalueindex(1))]; + var args = []; + + for (i in 0...argsLength) + args[i] = fromLua(l, i + 1); + + try + { + var result = Reflect.callMethod(null, method, args); + if (result != null) + return toLua(l, result) ? 1 : 0; + else + return 0; + } + catch (e) + { + LuaL.error(l, 'METHOD ERROR!\n${e.stack}'); + return 0; + } + } + + @:noCompletion + private static var callbacks:Map = new Map(); + + @:noCompletion + private static function handleCallback(l:State):Int + { + try + { + var callbackName:String = Lua.tostring(l, Lua.upvalueindex(1)); + var callbackMethod:Dynamic = callbacks.get(callbackName); + + if (callbackMethod == null) + { + // trace('checking last script'); + var last:FunkinLua = FunkinLua.lastCalledScript; + if (last == null || last.lua != l) + { + // trace('looping thru scripts'); + for (script in PlayState.instance.luaArray) + { + if (script != FunkinLua.lastCalledScript && script != null && script.lua == l) + { + // trace('found script'); + callbackMethod = script.callbacks.get(callbackName); + break; + } + } + } + else + { + callbackMethod = last.callbacks.get(callbackName); + } + } + + if (callbackMethod == null) + return 0; + + var nparams:Int = Lua.gettop(l); + var args:Array = []; + + for (i in 0...nparams) + { + args[i] = fromLua(l, i + 1); + } + + var ret:Dynamic = null; + /* return the number of results */ + + ret = Reflect.callMethod(null, callbackMethod, args); + + if (ret != null) + { + toLua(l, ret); + return 1; + } + } + catch (e:Dynamic) + { + LuaL.error(l, 'CALLBACK ERROR! ${if (e.message != null) e.message else e}'); + return 0; + } + + return 0; + } +} diff --git a/source/psychlua/CustomSubstate.hx b/source/psychlua/CustomSubstate.hx index 22032221c02..c8afd44c8e3 100644 --- a/source/psychlua/CustomSubstate.hx +++ b/source/psychlua/CustomSubstate.hx @@ -11,9 +11,9 @@ class CustomSubstate extends MusicBeatSubstate public static function implement(funk:FunkinLua) { var lua = funk.lua; - Lua_helper.add_callback(lua, "openCustomSubstate", openCustomSubstate); - Lua_helper.add_callback(lua, "closeCustomSubstate", closeCustomSubstate); - Lua_helper.add_callback(lua, "insertToCustomSubstate", insertToCustomSubstate); + Convert.addCallback(lua, "openCustomSubstate", openCustomSubstate); + Convert.addCallback(lua, "closeCustomSubstate", closeCustomSubstate); + Convert.addCallback(lua, "insertToCustomSubstate", insertToCustomSubstate); } #end diff --git a/source/psychlua/FlxAnimateFunctions.hx b/source/psychlua/FlxAnimateFunctions.hx index 565ef3c3493..24a4f937a07 100644 --- a/source/psychlua/FlxAnimateFunctions.hx +++ b/source/psychlua/FlxAnimateFunctions.hx @@ -2,13 +2,17 @@ package psychlua; import openfl.utils.Assets; +#if LUA_ALLOWED +import psychlua.FunkinLua.State; +#end + #if (LUA_ALLOWED && flxanimate) class FlxAnimateFunctions { public static function implement(funk:FunkinLua) { final lua:State = funk.lua; - Lua_helper.add_callback(lua, "makeFlxAnimateSprite", function(tag:String, ?x:Float = 0, ?y:Float = 0, ?loadFolder:String = null) { + Convert.addCallback(lua, "makeFlxAnimateSprite", function(tag:String, ?x:Float = 0, ?y:Float = 0, ?loadFolder:String = null) { tag = tag.replace('.', ''); var lastSprite = MusicBeatState.getVariables().get(tag); if(lastSprite != null) @@ -24,12 +28,12 @@ class FlxAnimateFunctions mySprite.active = true; }); - Lua_helper.add_callback(lua, "loadAnimateAtlas", function(tag:String, folderOrImg:String, ?spriteJson:String = null, ?animationJson:String = null) { + Convert.addCallback(lua, "loadAnimateAtlas", function(tag:String, folderOrImg:String, ?spriteJson:String = null, ?animationJson:String = null) { var spr:FlxAnimate = MusicBeatState.getVariables().get(tag); if(spr != null) Paths.loadAnimateAtlas(spr, folderOrImg, spriteJson, animationJson); }); - Lua_helper.add_callback(lua, "addAnimationBySymbol", function(tag:String, name:String, symbol:String, ?framerate:Float = 24, ?loop:Bool = false, ?matX:Float = 0, ?matY:Float = 0) + Convert.addCallback(lua, "addAnimationBySymbol", function(tag:String, name:String, symbol:String, ?framerate:Float = 24, ?loop:Bool = false, ?matX:Float = 0, ?matY:Float = 0) { var obj:FlxAnimate = cast MusicBeatState.getVariables().get(tag); if(obj == null) return false; @@ -44,7 +48,7 @@ class FlxAnimateFunctions return true; }); - Lua_helper.add_callback(lua, "addAnimationBySymbolIndices", function(tag:String, name:String, symbol:String, ?indices:Any = null, ?framerate:Float = 24, ?loop:Bool = false, ?matX:Float = 0, ?matY:Float = 0) + Convert.addCallback(lua, "addAnimationBySymbolIndices", function(tag:String, name:String, symbol:String, ?indices:Any = null, ?framerate:Float = 24, ?loop:Bool = false, ?matX:Float = 0, ?matY:Float = 0) { var obj:FlxAnimate = cast MusicBeatState.getVariables().get(tag); if(obj == null) return false; diff --git a/source/psychlua/FunkinLua.hx b/source/psychlua/FunkinLua.hx index b0f800b4748..a83b9ac332d 100644 --- a/source/psychlua/FunkinLua.hx +++ b/source/psychlua/FunkinLua.hx @@ -211,7 +211,7 @@ class FunkinLua { for (name => func in customFunctions) { if (func != null) - Lua_helper.add_callback(lua, name, func); + Convert.addCallback(lua, name, func); } // shader shit @@ -605,11 +605,11 @@ class FunkinLua { if(luaInstance.scriptName == cervix) { Lua.getglobal(luaInstance.lua, global); - if(Lua.isnumber(luaInstance.lua,-1)){ + if(Lua.isnumber(luaInstance.lua,-1) == 1){ Lua.pushnumber(lua, Lua.tonumber(luaInstance.lua, -1)); - }else if(Lua.isstring(luaInstance.lua,-1)){ + }else if(Lua.isstring(luaInstance.lua,-1) == 1){ Lua.pushstring(lua, Lua.tostring(luaInstance.lua, -1)); - }else if(Lua.isboolean(luaInstance.lua,-1)){ + }else if(Lua.isboolean(luaInstance.lua,-1) == 1){ Lua.pushboolean(lua, Lua.toboolean(luaInstance.lua, -1)); }else{ Lua.pushnil(lua); @@ -2259,7 +2259,7 @@ class FunkinLua { }); for (name => func in registeredFunctions) { if (func != null) - Lua_helper.add_callback(lua, name, func); + Convert.addCallback(lua, name, func); } #if ACHIEVEMENTS_ALLOWED Achievements.addLuaCallbacks(lua); #end #if HSCRIPT_ALLOWED HScript.implement(this); #end @@ -2890,7 +2890,7 @@ class FunkinLua { public function addLocalCallback(name:String, myFunction:Dynamic) { callbacks.set(name, myFunction); - Lua_helper.add_callback(lua, name, null); // just so that it gets called + Convert.addCallback(lua, name, null); // just so that it gets called } public static function registerFunction(name:String, func:Dynamic):Void @@ -3185,8 +3185,8 @@ class FunkinLua { Lua.getglobal(lua, func); var type:Int = Lua.type(lua, -1); - if (type != Lua.LUA_TFUNCTION) { - if (type > Lua.LUA_TNIL) + if (type != Lua.TFUNCTION) { + if (type > Lua.TNIL) LuaUtils.luaTrace(lua, "ERROR (" + func + "): attempt to call a " + typeToString(type) + " value", false, false, FlxColor.RED); Lua.pop(lua, 1); @@ -3197,7 +3197,7 @@ class FunkinLua { var status:Int = Lua.pcall(lua, args.length, 1, 0); // Checks if it's not successful, then show a error. - if (status != Lua.LUA_OK) { + if (status != Lua.OK) { var error:String = LuaUtils.getErrorMessage(lua, status); LuaUtils.luaTrace(lua, "ERROR (" + func + "): " + error, false, false, FlxColor.RED); return Function_Continue; @@ -3266,16 +3266,24 @@ class FunkinLua { return coverMeInPiss; } - function typeToString(type:Int):String { + public static function typeToString(type:Int):String + { #if LUA_ALLOWED - switch(type) { - case Lua.LUA_TBOOLEAN: return "boolean"; - case Lua.LUA_TNUMBER: return "number"; - case Lua.LUA_TSTRING: return "string"; - case Lua.LUA_TTABLE: return "table"; - case Lua.LUA_TFUNCTION: return "function"; + switch (type) + { + case type if (type == Lua.TBOOLEAN): + return "boolean"; + case type if (type == Lua.TNUMBER): + return "number"; + case type if (type == Lua.TSTRING): + return "string"; + case type if (type == Lua.TTABLE): + return "table"; + case type if (type == Lua.TFUNCTION): + return "function"; + case type if (type <= Lua.TNIL): + return "nil"; } - if (type <= Lua.LUA_TNIL) return "nil"; #end return "unknown"; } @@ -3287,7 +3295,7 @@ class FunkinLua { if (Reflect.isFunction(data)) { // Bind as a callable Lua function - Lua_helper.add_callback(lua, variable, data); + Convert.addCallback(lua, variable, data); return; } @@ -3317,4 +3325,5 @@ class FunkinLua { return PlayState.instance.isDead ? GameOverSubstate.instance : PlayState.instance; } } +typedef State = cpp.RawPointer; // hi guys, my name is "secret"! diff --git a/source/psychlua/HScript.hx b/source/psychlua/HScript.hx index 149f15c4fb2..d2cf43c2b12 100644 --- a/source/psychlua/HScript.hx +++ b/source/psychlua/HScript.hx @@ -95,7 +95,7 @@ class HScript #if HSCRIPT_ALLOWED for (script in PlayState.instance.luaArray) if(script != null && script.lua != null && !script.closed) - Lua_helper.add_callback(script.lua, name, func); + Convert.addCallback(script.lua, name, func); #end FunkinLua.customFunctions.set(name, func); }); diff --git a/source/psychlua/LuaFunction.hx b/source/psychlua/LuaFunction.hx new file mode 100644 index 00000000000..4bd988f3a2a --- /dev/null +++ b/source/psychlua/LuaFunction.hx @@ -0,0 +1,62 @@ +package psychlua; + +import hxluajit.Types; + +/** + * Holds a Lua function that can be called from Haxe. + * + * @see https://github.com/DragShot/linc_luajit/blob/master/llua/LuaCallback.hx + * + * @author DragShot + */ +@:allow(psychlua.Convert) +class LuaFunction +{ + @:noCompletion + private var l:Null>; + + @:noCompletion + private var ref:Int; + + /** + * Creates a new LuaFunction instance. + * + * @param l The Lua state pointer. + * @param ref The Lua function reference. + */ + private function new(l:cpp.Pointer, ref:Int):Void + { + this.l = l; + this.ref = ref; + } + + /** + * Calls the Lua function. + * + * @param args The function arguments. + * @return The function results as a Haxe array. + */ + public function call(args:Array):Array + { + if (l != null) + { + Lua.rawgeti(l.raw, Lua.REGISTRYINDEX, ref); + + return Convert.callFunctionWithoutName(l.raw, args); + } + + return []; + } + + /** + * Disposes of the Lua function reference. + */ + public function dispose():Void + { + if (l != null) + { + LuaL.unref(l.raw, Lua.REGISTRYINDEX, ref); + l = null; + } + } +} \ No newline at end of file diff --git a/source/psychlua/LuaUtils.hx b/source/psychlua/LuaUtils.hx index f168d49d88b..4b90a30a6c6 100644 --- a/source/psychlua/LuaUtils.hx +++ b/source/psychlua/LuaUtils.hx @@ -6,6 +6,10 @@ import flixel.util.FlxColor; import openfl.display.BlendMode; +#if LUA_ALLOWED +import psychlua.FunkinLua.State; +#end + using StringTools; @:allow(psychlua.FunkinLua) @@ -106,13 +110,18 @@ class LuaUtils { Lua.pop(lua, 1); if (v != null) v = v.trim(); - if (v == null || v == "") { - return switch(status) { - case Lua.LUA_ERRRUN: return "Runtime Error"; - case Lua.LUA_ERRMEM: return "Memory Allocation Error"; - case Lua.LUA_ERRERR: return "Critical Error"; - case _: return "Unknown Error"; + if (v == null || v == "") + { + switch (status) + { + case type if (type == Lua.ERRRUN): + return "Runtime Error"; + case type if (type == Lua.ERRMEM): + return "Memory Allocation Error"; + case type if (type == Lua.ERRERR): + return "Critical Error"; } + return "Unknown Error"; } return v; From a8b7fd9f08e919ade259ec56bab7dec8447bb554 Mon Sep 17 00:00:00 2001 From: "Vinegar (Moxie)" <124418090+moxie-coder@users.noreply.github.com> Date: Mon, 3 Nov 2025 14:40:36 -0500 Subject: [PATCH 2/2] Refactor addCallback to check function type before adding Simplifies the addCallback method by only adding the callback if the provided function is of type TFunction, aligning with PsychLua's expectations and removing unnecessary null checks. Co-Authored-By: Homura <124677802+homuhomu833@users.noreply.github.com> --- source/psychlua/Convert.hx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/source/psychlua/Convert.hx b/source/psychlua/Convert.hx index 35d89a0d1e8..7726b925ccd 100644 --- a/source/psychlua/Convert.hx +++ b/source/psychlua/Convert.hx @@ -15,11 +15,9 @@ class Convert { public static function addCallback(l:State, name:String, func:Dynamic) { - // PsychLua expects the function to be null for local callbacks, too lazy to do something about that - if (Type.typeof(func) != TFunction || func == null) - return; - - callbacks.set(name, func); + // PsychLua expects the function to be null for local callbacks so if func is not TFunction we don't add the callback here + if (Type.typeof(func) == TFunction) + callbacks.set(name, func); Lua.pushstring(l, name); Lua.pushcclosure(l, cpp.Callable.fromStaticFunction(handleCallback), 1);