From 954e452c2a2b2a1823a997803d9a8bce52748fac Mon Sep 17 00:00:00 2001 From: Trey Moller Date: Mon, 9 Oct 2023 17:55:11 -0500 Subject: [PATCH] Add LuaFuntionRef type (#172) --- .github/workflows/runner.yml | 4 +-- config.py | 1 + doc_classes/LuaAPI.xml | 5 +++- doc_classes/LuaFunctionRef.xml | 20 +++++++++++++++ register_types.cpp | 2 ++ src/classes/luaAPI.cpp | 12 +++++++++ src/classes/luaAPI.h | 5 ++++ src/classes/luaFunctionRef.cpp | 46 ++++++++++++++++++++++++++++++++++ src/classes/luaFunctionRef.h | 40 +++++++++++++++++++++++++++++ src/luaState.cpp | 34 +++++++++++++++++++++---- 10 files changed, 161 insertions(+), 8 deletions(-) create mode 100644 doc_classes/LuaFunctionRef.xml create mode 100644 src/classes/luaFunctionRef.cpp create mode 100644 src/classes/luaFunctionRef.h diff --git a/.github/workflows/runner.yml b/.github/workflows/runner.yml index 75f3e351..1423e335 100644 --- a/.github/workflows/runner.yml +++ b/.github/workflows/runner.yml @@ -6,7 +6,7 @@ on: branches: - "*" paths-ignore: - - "README.md" + - "**/*.md" - "LICENSE" - "**/*.png" - ".github/ISSUE_TEMPLATE/*" @@ -16,7 +16,7 @@ on: branches: - "main" paths-ignore: - - "**/README.md" + - "**/*.md" - "**/*.png" - "**/LICENSE" - ".github/ISSUE_TEMPLATE/*" diff --git a/config.py b/config.py index 6b1396d4..4bcd08c0 100644 --- a/config.py +++ b/config.py @@ -26,6 +26,7 @@ def get_doc_classes(): "LuaError", "LuaTuple", "LuaCallableExtra", + "LuaFunctionRef", "LuaObjectMetatable", "LuaDefaultObjectMetatable", ] diff --git a/doc_classes/LuaAPI.xml b/doc_classes/LuaAPI.xml index 4806a254..3fb84801 100644 --- a/doc_classes/LuaAPI.xml +++ b/doc_classes/LuaAPI.xml @@ -7,7 +7,7 @@ This class represents a lua state and allows you to interact with lua at runtime. You can load files and strings code. Push Callable's as lua functions. And push any Variant as a lua variable. - w + @@ -131,6 +131,9 @@ Sets the memory limit for the state in bytes. If the limit is 0, there is no limit. + + When true, Lua functions passed to Godot will use the LuaCallable type. This type is a CallableCustom which has issues currently with GDExtension and C# + When false, Lua functions passed to Godot will use the LuaFunctionRef type. This type is a RefCounted which behaves the same as a LuaCallable. But uses Inoke instead of Call. diff --git a/doc_classes/LuaFunctionRef.xml b/doc_classes/LuaFunctionRef.xml new file mode 100644 index 00000000..fba58160 --- /dev/null +++ b/doc_classes/LuaFunctionRef.xml @@ -0,0 +1,20 @@ + + + + Lua Function Reference. + + + Reference to a Lua function. This class is used to pass Lua functions to Godot. + + + + + + + + + Invoke the Lua function being referenced. + + + + diff --git a/register_types.cpp b/register_types.cpp index c1ece786..8e09f00f 100644 --- a/register_types.cpp +++ b/register_types.cpp @@ -3,6 +3,7 @@ #include "src/classes/luaCallableExtra.h" #include "src/classes/luaCoroutine.h" #include "src/classes/luaError.h" +#include "src/classes/luaFunctionRef.h" #include "src/classes/luaObjectMetatable.h" #include "src/classes/luaTuple.h" @@ -19,6 +20,7 @@ void initialize_luaAPI_module(ModuleInitializationLevel p_level) { ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); + ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); diff --git a/src/classes/luaAPI.cpp b/src/classes/luaAPI.cpp index aa0d9787..c6b4fdd7 100644 --- a/src/classes/luaAPI.cpp +++ b/src/classes/luaAPI.cpp @@ -45,12 +45,16 @@ void LuaAPI::_bind_methods() { ClassDB::bind_method(D_METHOD("new_coroutine"), &LuaAPI::newCoroutine); ClassDB::bind_method(D_METHOD("get_running_coroutine"), &LuaAPI::getRunningCoroutine); + ClassDB::bind_method(D_METHOD("set_use_callables", "value"), &LuaAPI::setUseCallables); + ClassDB::bind_method(D_METHOD("get_use_callables"), &LuaAPI::getUseCallables); + ClassDB::bind_method(D_METHOD("set_object_metatable", "value"), &LuaAPI::setObjectMetatable); ClassDB::bind_method(D_METHOD("get_object_metatable"), &LuaAPI::getObjectMetatable); ClassDB::bind_method(D_METHOD("set_memory_limit", "limit"), &LuaAPI::setMemoryLimit); ClassDB::bind_method(D_METHOD("get_memory_limit"), &LuaAPI::getMemoryLimit); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_callables"), "set_use_callables", "get_use_callables"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "object_metatable"), "set_object_metatable", "get_object_metatable"); ADD_PROPERTY(PropertyInfo(Variant::INT, "memory_limit"), "set_memory_limit", "get_memory_limit"); @@ -82,6 +86,14 @@ int LuaAPI::configureGC(int what, int data) { return lua_gc(lState, what, data); } +void LuaAPI::setUseCallables(bool value) { + useCallables = value; +} + +bool LuaAPI::getUseCallables() const { + return useCallables; +} + void LuaAPI::setObjectMetatable(Ref value) { objectMetatable = value; } diff --git a/src/classes/luaAPI.h b/src/classes/luaAPI.h index 098385c1..9ef093e6 100644 --- a/src/classes/luaAPI.h +++ b/src/classes/luaAPI.h @@ -33,6 +33,9 @@ class LuaAPI : public RefCounted { void bindLibraries(Array libs); void setHook(Callable hook, int mask, int count); + void setUseCallables(bool value); + bool getUseCallables() const; + void setObjectMetatable(Ref value); Ref getObjectMetatable() const; @@ -82,6 +85,8 @@ class LuaAPI : public RefCounted { }; private: + bool useCallables = true; + LuaState state; lua_State *lState = nullptr; diff --git a/src/classes/luaFunctionRef.cpp b/src/classes/luaFunctionRef.cpp new file mode 100644 index 00000000..397dab7d --- /dev/null +++ b/src/classes/luaFunctionRef.cpp @@ -0,0 +1,46 @@ +#include "luaFunctionRef.h" + +#include + +void LuaFunctionRef::_bind_methods() { + ClassDB::bind_method(D_METHOD("invoke", "args"), &LuaFunctionRef::invoke); +} + +LuaFunctionRef::LuaFunctionRef() { + L = nullptr; + ref = LUA_NOREF; +} + +LuaFunctionRef::~LuaFunctionRef() { + luaL_unref(L, LUA_REGISTRYINDEX, ref); +} + +void LuaFunctionRef::setLuaState(lua_State *state) { + L = state; +} + +void LuaFunctionRef::setRef(int ref) { + this->ref = ref; +} + +Variant LuaFunctionRef::invoke(Array args) { + lua_pushcfunction(L, LuaState::luaErrorHandler); + lua_rawgeti(L, LUA_REGISTRYINDEX, ref); + + for (int i = 0; i < args.size(); i++) { + Variant arg = args[i]; + LuaState::pushVariant(L, arg); + } + + int err = lua_pcall(L, args.size(), 1, -2 - args.size()); + Variant ret; + if (err) { + ret = LuaState::handleError(L, ret); + } else { + ret = LuaState::getVariant(L, -1); + } + + lua_pop(L, 1); + + return ret; +} \ No newline at end of file diff --git a/src/classes/luaFunctionRef.h b/src/classes/luaFunctionRef.h new file mode 100644 index 00000000..020e8d16 --- /dev/null +++ b/src/classes/luaFunctionRef.h @@ -0,0 +1,40 @@ +#ifndef LUAFUNCTIONREF_H +#define LUAFUNCTIONREF_H + +#ifndef LAPI_GDEXTENSION +#include "core/core_bind.h" +#include "core/object/ref_counted.h" +#else +#include +#endif + +#include + +#ifdef LAPI_GDEXTENSION +using namespace godot; +#endif + +class LuaFunctionRef : public RefCounted { + GDCLASS(LuaFunctionRef, RefCounted); + +protected: + static void _bind_methods(); + +public: + LuaFunctionRef(); + ~LuaFunctionRef(); + + void setLuaState(lua_State *state); + void setRef(int ref); + + Variant invoke(Array args); + + inline int getRef() const { return ref; } + inline lua_State *getLuaState() const { return L; } + +private: + lua_State *L; + int ref; +}; + +#endif \ No newline at end of file diff --git a/src/luaState.cpp b/src/luaState.cpp index 97a02fd3..2aefeccc 100644 --- a/src/luaState.cpp +++ b/src/luaState.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #ifndef LAPI_GDXTENSION @@ -463,6 +464,20 @@ Ref LuaState::pushVariant(lua_State *state, Variant var) { break; } + // If the type being pushed is a thread, push a LUA_TTHREAD state. +#ifndef LAPI_GDEXTENSION + if (Ref funcRef = Object::cast_to(var.operator Object *()); funcRef.is_valid()) { +#else + // blame this on https://github.com/godotengine/godot-cpp/issues/995 + if (Ref funcRef = dynamic_cast(var.operator Object *()); funcRef.is_valid()) { +#endif + lua_rawgeti(state, LUA_REGISTRYINDEX, funcRef->getRef()); + if (funcRef->getLuaState() != state) { + lua_xmove(funcRef->getLuaState(), state, 1); + } + break; + } + // If the type being pushed is a LuaCallableExtra. use mt_CallableExtra instead #ifndef LAPI_GDEXTENSION if (Ref func = Object::cast_to(var.operator Object *()); func.is_valid()) { @@ -595,16 +610,25 @@ Variant LuaState::getVariant(lua_State *state, int index) { break; } case LUA_TFUNCTION: { + Ref api = getAPI(state); // Put function on the top of the stack and get a ref to it. This will create a copy of the function. lua_pushvalue(state, index); + if (api->getUseCallables()) { #ifndef LAPI_GDEXTENSION - LuaCallable *callable = memnew(LuaCallable(Ref(getAPI(state)), luaL_ref(state, LUA_REGISTRYINDEX), state)); - result = Callable(callable); + LuaCallable *callable = memnew(LuaCallable(api, luaL_ref(state, LUA_REGISTRYINDEX), state)); + result = Callable(callable); #else - Array binds; - binds.push_back(luaL_ref(state, LUA_REGISTRYINDEX)); - result = Callable(getAPI(state), "call_function_ref").bindv(binds); + Array binds; + binds.push_back(luaL_ref(state, LUA_REGISTRYINDEX)); + result = Callable(getAPI(state), "call_function_ref").bindv(binds); #endif + } else { + Ref funcRef; + funcRef.instantiate(); + funcRef->setRef(luaL_ref(state, LUA_REGISTRYINDEX)); + funcRef->setLuaState(state); + result = funcRef; + } break; } case LUA_TTHREAD: {