feat(lua): added instance property and child access via reference

This commit is contained in:
maelstrom 2025-04-25 10:19:11 +02:00
parent 6bb1d8b3a4
commit cbed2bac95
6 changed files with 88 additions and 5 deletions

View file

@ -17,6 +17,12 @@ void Data::Variant::Serialize(pugi::xml_node node) const {
}, this->wrapped); }, 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) { Data::Variant Data::Variant::Deserialize(pugi::xml_node node) {
if (Data::TYPE_MAP.count(node.name()) == 0) { if (Data::TYPE_MAP.count(node.name()) == 0) {
Logger::fatalErrorf("Unknown type for instance: '%s'", node.name()); Logger::fatalErrorf("Unknown type for instance: '%s'", node.name());

View file

@ -37,6 +37,7 @@ namespace Data {
Data::String ToString() const; Data::String ToString() const;
void Serialize(pugi::xml_node node) const; void Serialize(pugi::xml_node node) const;
void PushLuaValue(lua_State* state) const;
static Data::Variant Deserialize(pugi::xml_node node); static Data::Variant Deserialize(pugi::xml_node node);
}; };

View file

@ -1,8 +1,11 @@
#include "datatypes/ref.h" #include "datatypes/ref.h"
#include "color3.h" #include "datatypes/base.h"
#include "logger.h"
#include "meta.h" // IWYU pragma: keep #include "meta.h" // IWYU pragma: keep
#include <memory> #include <memory>
#include <optional>
#include "objects/base/instance.h" #include "objects/base/instance.h"
#include "lua.h"
Data::InstanceRef::InstanceRef() {}; Data::InstanceRef::InstanceRef() {};
Data::InstanceRef::InstanceRef(std::weak_ptr<Instance> instance) : ref(instance) {}; Data::InstanceRef::InstanceRef(std::weak_ptr<Instance> instance) : ref(instance) {};
@ -33,7 +36,61 @@ void Data::InstanceRef::Serialize(pugi::xml_node node) const {
// return Color3::FromHex(node.text().get()); // 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 { void Data::InstanceRef::PushLuaValue(lua_State* L) const {
// TODO: if (ref.expired()) return lua_pushnil(L);
panic();
int n = lua_gettop(L);
auto userdata = (std::shared_ptr<Instance>**)lua_newuserdata(L, sizeof(void*));
// Create new pointer, and assign userdata a pointer to it
std::shared_ptr<Instance>* ptr = new std::shared_ptr<Instance>(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<Instance>**)lua_touserdata(L, -1);
delete *userdata;
lua_pop(L, 1);
return 0;
}
static int inst_index(lua_State* L) {
auto userdata = (std::shared_ptr<Instance>**)lua_touserdata(L, 1);
std::shared_ptr<Instance> inst = **userdata;
std::string key(lua_tostring(L, 2));
lua_pop(L, 2);
// Read property
std::optional<PropertyMeta> meta = inst->GetPropertyMeta(key);
if (meta) {
Data::Variant value = inst->GetPropertyValue(key).expect();
value.PushLuaValue(L);
return 1;
}
// Look for child
std::optional<std::shared_ptr<Instance>> 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());
} }

View file

@ -154,6 +154,14 @@ bool Instance::IsA(std::string className) {
return cur != nullptr; return cur != nullptr;
} }
std::optional<std::shared_ptr<Instance>> Instance::FindFirstChild(std::string name) {
for (auto child : children) {
if (child->name == name)
return child;
}
return std::nullopt;
}
static std::shared_ptr<Instance> DUMMY_INSTANCE; static std::shared_ptr<Instance> DUMMY_INSTANCE;
DescendantsIterator Instance::GetDescendantsStart() { DescendantsIterator Instance::GetDescendantsStart() {
return DescendantsIterator(GetChildren().size() > 0 ? GetChildren()[0] : DUMMY_INSTANCE); return DescendantsIterator(GetChildren().size() > 0 ? GetChildren()[0] : DUMMY_INSTANCE);

View file

@ -102,6 +102,7 @@ public:
DescendantsIterator GetDescendantsEnd(); DescendantsIterator GetDescendantsEnd();
// Utility functions // Utility functions
inline void AddChild(std::shared_ptr<Instance> object) { object->SetParent(this->shared_from_this()); } inline void AddChild(std::shared_ptr<Instance> object) { object->SetParent(this->shared_from_this()); }
std::optional<std::shared_ptr<Instance>> FindFirstChild(std::string);
// Properties // Properties
result<Data::Variant, MemberNotFound> GetPropertyValue(std::string name); result<Data::Variant, MemberNotFound> GetPropertyValue(std::string name);

View file

@ -3,8 +3,8 @@
#include "objects/base/instance.h" #include "objects/base/instance.h"
#include "objects/base/member.h" #include "objects/base/member.h"
#include "objects/script/scriptcontext.h" #include "objects/script/scriptcontext.h"
#include <luajit-2.1/lauxlib.h> #include "objects/workspace.h"
#include <luajit-2.1/lua.h> #include "lua.h"
const InstanceType Script::TYPE = { const InstanceType Script::TYPE = {
.super = &Instance::TYPE, .super = &Instance::TYPE,
@ -40,7 +40,17 @@ void Script::Run() {
lua_State* L = dataModel().value()->GetService<ScriptContext>()->state; lua_State* L = dataModel().value()->GetService<ScriptContext>()->state;
// Initialize script globals // 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<Workspace>()).PushLuaValue(L);
lua_rawset(L, -3);
lua_pop(L, 1);
luaL_loadstring(L, source.c_str()); luaL_loadstring(L, source.c_str());
int status = lua_pcall(L, 0, LUA_MULTRET, 0); int status = lua_pcall(L, 0, LUA_MULTRET, 0);