From cbed2bac9583bfe5a86d23cf8717a4c6464dd574 Mon Sep 17 00:00:00 2001 From: maelstrom Date: Fri, 25 Apr 2025 10:19:11 +0200 Subject: [PATCH] feat(lua): added instance property and child access via reference --- core/src/datatypes/meta.cpp | 6 +++ core/src/datatypes/meta.h | 1 + core/src/datatypes/ref.cpp | 63 ++++++++++++++++++++++++++++-- core/src/objects/base/instance.cpp | 8 ++++ core/src/objects/base/instance.h | 1 + core/src/objects/script.cpp | 14 ++++++- 6 files changed, 88 insertions(+), 5 deletions(-) diff --git a/core/src/datatypes/meta.cpp b/core/src/datatypes/meta.cpp index f6b3801..01cf5a3 100644 --- a/core/src/datatypes/meta.cpp +++ b/core/src/datatypes/meta.cpp @@ -17,6 +17,12 @@ void Data::Variant::Serialize(pugi::xml_node node) const { }, this->wrapped); } +void Data::Variant::PushLuaValue(lua_State* state) const { + return std::visit([&](auto&& it) { + return it.PushLuaValue(state); + }, this->wrapped); +} + Data::Variant Data::Variant::Deserialize(pugi::xml_node node) { if (Data::TYPE_MAP.count(node.name()) == 0) { Logger::fatalErrorf("Unknown type for instance: '%s'", node.name()); diff --git a/core/src/datatypes/meta.h b/core/src/datatypes/meta.h index 2d43028..dea11b0 100644 --- a/core/src/datatypes/meta.h +++ b/core/src/datatypes/meta.h @@ -37,6 +37,7 @@ namespace Data { Data::String ToString() const; void Serialize(pugi::xml_node node) const; + void PushLuaValue(lua_State* state) const; static Data::Variant Deserialize(pugi::xml_node node); }; diff --git a/core/src/datatypes/ref.cpp b/core/src/datatypes/ref.cpp index fb6a4e7..616a537 100644 --- a/core/src/datatypes/ref.cpp +++ b/core/src/datatypes/ref.cpp @@ -1,8 +1,11 @@ #include "datatypes/ref.h" -#include "color3.h" +#include "datatypes/base.h" +#include "logger.h" #include "meta.h" // IWYU pragma: keep #include +#include #include "objects/base/instance.h" +#include "lua.h" Data::InstanceRef::InstanceRef() {}; Data::InstanceRef::InstanceRef(std::weak_ptr instance) : ref(instance) {}; @@ -33,7 +36,61 @@ void Data::InstanceRef::Serialize(pugi::xml_node node) const { // return Color3::FromHex(node.text().get()); // } +static int inst_gc(lua_State*); +static int inst_index(lua_State*); +static const struct luaL_Reg metatable [] = { + {"__gc", inst_gc}, + {"__index", inst_index}, + {NULL, NULL} /* end of array */ +}; + void Data::InstanceRef::PushLuaValue(lua_State* L) const { - // TODO: - panic(); + if (ref.expired()) return lua_pushnil(L); + + int n = lua_gettop(L); + + auto userdata = (std::shared_ptr**)lua_newuserdata(L, sizeof(void*)); + + // Create new pointer, and assign userdata a pointer to it + std::shared_ptr* ptr = new std::shared_ptr(ref); + *userdata = ptr; + + // Create the instance's metatable + luaL_newmetatable(L, "__mt_instance"); + luaL_register(L, NULL, metatable); + + lua_setmetatable(L, n+1); +} + +static int inst_gc(lua_State* L) { + // Destroy the contained shared_ptr + auto userdata = (std::shared_ptr**)lua_touserdata(L, -1); + delete *userdata; + lua_pop(L, 1); + + return 0; +} + +static int inst_index(lua_State* L) { + auto userdata = (std::shared_ptr**)lua_touserdata(L, 1); + std::shared_ptr inst = **userdata; + std::string key(lua_tostring(L, 2)); + lua_pop(L, 2); + + // Read property + std::optional meta = inst->GetPropertyMeta(key); + if (meta) { + Data::Variant value = inst->GetPropertyValue(key).expect(); + value.PushLuaValue(L); + return 1; + } + + // Look for child + std::optional> child = inst->FindFirstChild(key); + if (child) { + Data::InstanceRef(child.value()).PushLuaValue(L); + return 1; + } + + return luaL_error(L, "'%s' is not a valid member of %s", key.c_str(), inst->GetClass()->className.c_str()); } \ No newline at end of file diff --git a/core/src/objects/base/instance.cpp b/core/src/objects/base/instance.cpp index dbe5460..dde7c7f 100644 --- a/core/src/objects/base/instance.cpp +++ b/core/src/objects/base/instance.cpp @@ -154,6 +154,14 @@ bool Instance::IsA(std::string className) { return cur != nullptr; } +std::optional> Instance::FindFirstChild(std::string name) { + for (auto child : children) { + if (child->name == name) + return child; + } + return std::nullopt; +} + static std::shared_ptr DUMMY_INSTANCE; DescendantsIterator Instance::GetDescendantsStart() { return DescendantsIterator(GetChildren().size() > 0 ? GetChildren()[0] : DUMMY_INSTANCE); diff --git a/core/src/objects/base/instance.h b/core/src/objects/base/instance.h index d4de5f3..44fda8c 100644 --- a/core/src/objects/base/instance.h +++ b/core/src/objects/base/instance.h @@ -102,6 +102,7 @@ public: DescendantsIterator GetDescendantsEnd(); // Utility functions inline void AddChild(std::shared_ptr object) { object->SetParent(this->shared_from_this()); } + std::optional> FindFirstChild(std::string); // Properties result GetPropertyValue(std::string name); diff --git a/core/src/objects/script.cpp b/core/src/objects/script.cpp index 3d91a4b..576d2fb 100644 --- a/core/src/objects/script.cpp +++ b/core/src/objects/script.cpp @@ -3,8 +3,8 @@ #include "objects/base/instance.h" #include "objects/base/member.h" #include "objects/script/scriptcontext.h" -#include -#include +#include "objects/workspace.h" +#include "lua.h" const InstanceType Script::TYPE = { .super = &Instance::TYPE, @@ -40,7 +40,17 @@ void Script::Run() { lua_State* L = dataModel().value()->GetService()->state; // Initialize script globals + lua_getglobal(L, "_G"); + lua_pushstring(L, "game"); + Data::InstanceRef(dataModel().value()).PushLuaValue(L); + lua_rawset(L, -3); + + lua_pushstring(L, "workspace"); + Data::InstanceRef(dataModel().value()->GetService()).PushLuaValue(L); + lua_rawset(L, -3); + + lua_pop(L, 1); luaL_loadstring(L, source.c_str()); int status = lua_pcall(L, 0, LUA_MULTRET, 0);