Skip to content

Commit

Permalink
Add LuaFuntionRef type (#172)
Browse files Browse the repository at this point in the history
  • Loading branch information
Trey2k authored Oct 9, 2023
1 parent f5016f7 commit 954e452
Show file tree
Hide file tree
Showing 10 changed files with 161 additions and 8 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/runner.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
branches:
- "*"
paths-ignore:
- "README.md"
- "**/*.md"
- "LICENSE"
- "**/*.png"
- ".github/ISSUE_TEMPLATE/*"
Expand All @@ -16,7 +16,7 @@ on:
branches:
- "main"
paths-ignore:
- "**/README.md"
- "**/*.md"
- "**/*.png"
- "**/LICENSE"
- ".github/ISSUE_TEMPLATE/*"
Expand Down
1 change: 1 addition & 0 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def get_doc_classes():
"LuaError",
"LuaTuple",
"LuaCallableExtra",
"LuaFunctionRef",
"LuaObjectMetatable",
"LuaDefaultObjectMetatable",
]
Expand Down
5 changes: 4 additions & 1 deletion doc_classes/LuaAPI.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
</description>
<tutorials>
</tutorials>w
</tutorials>
<methods>
<method name="new_coroutine">
<return type="LuaCoroutine" />
Expand Down Expand Up @@ -131,6 +131,9 @@
<member name="memory_limit" type="int" setter="set_memory_limit" getter="get_memory_limit" default="0">
Sets the memory limit for the state in bytes. If the limit is 0, there is no limit.
</member>
<member name="use_callables" type="bool" setter="set_use_callables" getter="get_use_callables" default="true">
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.
</members>
<constants>
<constant name="GC_STOP" value="1" enum="HookMask">
Expand Down
20 changes: 20 additions & 0 deletions doc_classes/LuaFunctionRef.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="LuaFunctionRef" inherits="RefCounted" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Lua Function Reference.
</brief_description>
<description>
Reference to a Lua function. This class is used to pass Lua functions to Godot.
</description>
<tutorials>
</tutorials>
<methods>
<method name="inoke">
<return type="Variant" />
<param index="0" name="Arguments" type="Array" />
<description>
Invoke the Lua function being referenced.
</description>
</method>
</methods>
</class>
2 changes: 2 additions & 0 deletions register_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -19,6 +20,7 @@ void initialize_luaAPI_module(ModuleInitializationLevel p_level) {
ClassDB::register_class<LuaCallableExtra>();
ClassDB::register_class<LuaCoroutine>();
ClassDB::register_class<LuaError>();
ClassDB::register_class<LuaFunctionRef>();
ClassDB::register_class<LuaObjectMetatable>();
ClassDB::register_class<LuaDefaultObjectMetatable>();
ClassDB::register_class<LuaTuple>();
Expand Down
12 changes: 12 additions & 0 deletions src/classes/luaAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down Expand Up @@ -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<LuaObjectMetatable> value) {
objectMetatable = value;
}
Expand Down
5 changes: 5 additions & 0 deletions src/classes/luaAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<LuaObjectMetatable> value);
Ref<LuaObjectMetatable> getObjectMetatable() const;

Expand Down Expand Up @@ -82,6 +85,8 @@ class LuaAPI : public RefCounted {
};

private:
bool useCallables = true;

LuaState state;
lua_State *lState = nullptr;

Expand Down
46 changes: 46 additions & 0 deletions src/classes/luaFunctionRef.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#include "luaFunctionRef.h"

#include <luaState.h>

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;
}
40 changes: 40 additions & 0 deletions src/classes/luaFunctionRef.h
Original file line number Diff line number Diff line change
@@ -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 <godot_cpp/classes/ref.hpp>
#endif

#include <lua/lua.hpp>

#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
34 changes: 29 additions & 5 deletions src/luaState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <classes/luaAPI.h>
#include <classes/luaCallableExtra.h>
#include <classes/luaCoroutine.h>
#include <classes/luaFunctionRef.h>
#include <classes/luaTuple.h>

#ifndef LAPI_GDXTENSION
Expand Down Expand Up @@ -463,6 +464,20 @@ Ref<LuaError> 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<LuaFunctionRef> funcRef = Object::cast_to<LuaFunctionRef>(var.operator Object *()); funcRef.is_valid()) {
#else
// blame this on https://github.com/godotengine/godot-cpp/issues/995
if (Ref<LuaFunctionRef> funcRef = dynamic_cast<LuaFunctionRef *>(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<LuaCallableExtra> func = Object::cast_to<LuaCallableExtra>(var.operator Object *()); func.is_valid()) {
Expand Down Expand Up @@ -595,16 +610,25 @@ Variant LuaState::getVariant(lua_State *state, int index) {
break;
}
case LUA_TFUNCTION: {
Ref<LuaAPI> 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<LuaAPI>(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<LuaFunctionRef> funcRef;
funcRef.instantiate();
funcRef->setRef(luaL_ref(state, LUA_REGISTRYINDEX));
funcRef->setLuaState(state);
result = funcRef;
}
break;
}
case LUA_TTHREAD: {
Expand Down

0 comments on commit 954e452

Please sign in to comment.