feat(lua): deserializing values and setting instance properties
This commit is contained in:
parent
cbed2bac95
commit
9be6c103de
9 changed files with 120 additions and 8 deletions
|
@ -1,4 +1,5 @@
|
|||
#include "base.h"
|
||||
#include "error/data.h"
|
||||
#include "meta.h"
|
||||
#include <ios>
|
||||
#include <sstream>
|
||||
|
@ -7,7 +8,12 @@
|
|||
#define IMPL_WRAPPER_CLASS(CLASS_NAME, WRAPPED_TYPE, TYPE_NAME) Data::CLASS_NAME::CLASS_NAME(WRAPPED_TYPE in) : value(in) {} \
|
||||
Data::CLASS_NAME::~CLASS_NAME() = default; \
|
||||
Data::CLASS_NAME::operator const WRAPPED_TYPE() const { return value; } \
|
||||
const Data::TypeInfo Data::CLASS_NAME::TYPE = { .name = TYPE_NAME, .deserializer = &Data::CLASS_NAME::Deserialize, .fromString = &Data::CLASS_NAME::FromString }; \
|
||||
const Data::TypeInfo Data::CLASS_NAME::TYPE = { \
|
||||
.name = TYPE_NAME, \
|
||||
.deserializer = &Data::CLASS_NAME::Deserialize, \
|
||||
.fromString = &Data::CLASS_NAME::FromString, \
|
||||
.fromLuaValue = &Data::CLASS_NAME::FromLuaValue, \
|
||||
}; \
|
||||
const Data::TypeInfo& Data::CLASS_NAME::GetType() const { return Data::CLASS_NAME::TYPE; }; \
|
||||
void Data::CLASS_NAME::Serialize(pugi::xml_node node) const { node.text().set(std::string(this->ToString())); }
|
||||
|
||||
|
@ -37,6 +43,10 @@ void Data::Null::PushLuaValue(lua_State* L) const {
|
|||
lua_pushnil(L);
|
||||
}
|
||||
|
||||
result<Data::Variant, LuaCastError> Data::Null::FromLuaValue(lua_State* L, int idx) {
|
||||
return Data::Variant(Data::Null());
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
IMPL_WRAPPER_CLASS(Bool, bool, "bool")
|
||||
|
@ -122,4 +132,30 @@ void Data::Float::PushLuaValue(lua_State* L) const {
|
|||
|
||||
void Data::String::PushLuaValue(lua_State* L) const {
|
||||
lua_pushstring(L, value.c_str());
|
||||
}
|
||||
|
||||
// FromLuaValue
|
||||
|
||||
result<Data::Variant, LuaCastError> Data::Bool::FromLuaValue(lua_State* L, int idx) {
|
||||
if (!lua_isboolean(L, idx))
|
||||
return LuaCastError(lua_typename(L, idx), "boolean");
|
||||
return Data::Variant(Data::Bool(lua_toboolean(L, idx)));
|
||||
}
|
||||
|
||||
result<Data::Variant, LuaCastError> Data::Int::FromLuaValue(lua_State* L, int idx) {
|
||||
if (!lua_isnumber(L, idx))
|
||||
return LuaCastError(lua_typename(L, idx), "integer");
|
||||
return Data::Variant(Data::Int((int)lua_tonumber(L, idx)));
|
||||
}
|
||||
|
||||
result<Data::Variant, LuaCastError> Data::Float::FromLuaValue(lua_State* L, int idx) {
|
||||
if (!lua_isnumber(L, idx))
|
||||
return LuaCastError(lua_typename(L, idx), "float");
|
||||
return Data::Variant(Data::Float((float)lua_tonumber(L, idx)));
|
||||
}
|
||||
|
||||
result<Data::Variant, LuaCastError> Data::String::FromLuaValue(lua_State* L, int idx) {
|
||||
if (!lua_tostring(L, idx))
|
||||
return LuaCastError(lua_typename(L, idx), "string");
|
||||
return Data::Variant(Data::String(lua_tostring(L, idx)));
|
||||
}
|
|
@ -4,6 +4,8 @@
|
|||
#include <functional>
|
||||
#include <optional>
|
||||
#include <pugixml.hpp>
|
||||
#include "error/result.h"
|
||||
#include "error/data.h"
|
||||
|
||||
extern "C" { typedef struct lua_State lua_State; }
|
||||
|
||||
|
@ -22,17 +24,20 @@ public: \
|
|||
\
|
||||
static Data::Variant Deserialize(pugi::xml_node node); \
|
||||
static std::optional<Data::Variant> FromString(std::string); \
|
||||
static result<Data::Variant, LuaCastError> FromLuaValue(lua_State*, int idx); \
|
||||
};
|
||||
|
||||
namespace Data {
|
||||
class Variant;
|
||||
typedef std::function<Data::Variant(pugi::xml_node)> Deserializer;
|
||||
typedef std::function<std::optional<Data::Variant>(std::string)> FromString;
|
||||
typedef std::function<result<Data::Variant, LuaCastError>(lua_State*, int idx)> FromLuaValue;
|
||||
|
||||
struct TypeInfo {
|
||||
std::string name;
|
||||
Deserializer deserializer;
|
||||
FromString fromString;
|
||||
FromLuaValue fromLuaValue;
|
||||
};
|
||||
|
||||
class String;
|
||||
|
@ -57,6 +62,7 @@ namespace Data {
|
|||
virtual void PushLuaValue(lua_State*) const override;
|
||||
|
||||
static Data::Variant Deserialize(pugi::xml_node node);
|
||||
static result<Data::Variant, LuaCastError> FromLuaValue(lua_State*, int idx);
|
||||
};
|
||||
|
||||
DEF_WRAPPER_CLASS(Bool, bool)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "meta.h"
|
||||
#include "datatypes/base.h"
|
||||
#include "datatypes/cframe.h"
|
||||
#include "datatypes/ref.h"
|
||||
#include "logger.h"
|
||||
#include "panic.h"
|
||||
#include <variant>
|
||||
|
@ -25,7 +26,7 @@ void Data::Variant::PushLuaValue(lua_State* state) const {
|
|||
|
||||
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());
|
||||
Logger::fatalErrorf("Unknown type for property: '%s'", node.name());
|
||||
panic();
|
||||
}
|
||||
|
||||
|
@ -42,4 +43,5 @@ std::map<std::string, const Data::TypeInfo*> Data::TYPE_MAP = {
|
|||
{ "Vector3", &Data::Vector3::TYPE },
|
||||
{ "CoordinateFrame", &Data::CFrame::TYPE },
|
||||
{ "Color3", &Data::Color3::TYPE },
|
||||
{ "Ref", &Data::InstanceRef::TYPE },
|
||||
};
|
|
@ -1,11 +1,13 @@
|
|||
#include "datatypes/ref.h"
|
||||
#include "datatypes/base.h"
|
||||
#include "error/data.h"
|
||||
#include "logger.h"
|
||||
#include "meta.h" // IWYU pragma: keep
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include "objects/base/instance.h"
|
||||
#include "lua.h"
|
||||
#include "objects/base/member.h"
|
||||
|
||||
Data::InstanceRef::InstanceRef() {};
|
||||
Data::InstanceRef::InstanceRef(std::weak_ptr<Instance> instance) : ref(instance) {};
|
||||
|
@ -13,7 +15,8 @@ Data::InstanceRef::~InstanceRef() = default;
|
|||
|
||||
const Data::TypeInfo Data::InstanceRef::TYPE = {
|
||||
.name = "Instance",
|
||||
// .deserializer = &Data::InstanceRef::Deserialize,
|
||||
.deserializer = &Data::InstanceRef::Deserialize,
|
||||
.fromLuaValue = &Data::InstanceRef::FromLuaValue,
|
||||
};
|
||||
|
||||
const Data::TypeInfo& Data::InstanceRef::GetType() const { return Data::InstanceRef::TYPE; };
|
||||
|
@ -32,15 +35,17 @@ void Data::InstanceRef::Serialize(pugi::xml_node node) const {
|
|||
// node.text().set(this->ToHex());
|
||||
}
|
||||
|
||||
// Data::Variant Color3::Deserialize(pugi::xml_node node) {
|
||||
// return Color3::FromHex(node.text().get());
|
||||
// }
|
||||
Data::Variant Data::InstanceRef::Deserialize(pugi::xml_node node) {
|
||||
return Data::InstanceRef();
|
||||
}
|
||||
|
||||
static int inst_gc(lua_State*);
|
||||
static int inst_index(lua_State*);
|
||||
static int inst_newindex(lua_State*);
|
||||
static const struct luaL_Reg metatable [] = {
|
||||
{"__gc", inst_gc},
|
||||
{"__index", inst_index},
|
||||
{"__newindex", inst_newindex},
|
||||
{NULL, NULL} /* end of array */
|
||||
};
|
||||
|
||||
|
@ -62,6 +67,16 @@ void Data::InstanceRef::PushLuaValue(lua_State* L) const {
|
|||
lua_setmetatable(L, n+1);
|
||||
}
|
||||
|
||||
result<Data::Variant, LuaCastError> Data::InstanceRef::FromLuaValue(lua_State* L, int idx) {
|
||||
if (lua_isnil(L, idx))
|
||||
return Data::Variant(Data::InstanceRef());
|
||||
if (!lua_isuserdata(L, idx))
|
||||
return LuaCastError(lua_typename(L, idx), "Instance");
|
||||
// TODO: overhaul this to support other types
|
||||
auto userdata = (std::shared_ptr<Instance>**)lua_touserdata(L, idx);
|
||||
return Data::Variant(Data::InstanceRef(**userdata));
|
||||
}
|
||||
|
||||
static int inst_gc(lua_State* L) {
|
||||
// Destroy the contained shared_ptr
|
||||
auto userdata = (std::shared_ptr<Instance>**)lua_touserdata(L, -1);
|
||||
|
@ -71,6 +86,7 @@ static int inst_gc(lua_State* L) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
// __index(t,k)
|
||||
static int inst_index(lua_State* L) {
|
||||
auto userdata = (std::shared_ptr<Instance>**)lua_touserdata(L, 1);
|
||||
std::shared_ptr<Instance> inst = **userdata;
|
||||
|
@ -93,4 +109,28 @@ static int inst_index(lua_State* L) {
|
|||
}
|
||||
|
||||
return luaL_error(L, "'%s' is not a valid member of %s", key.c_str(), inst->GetClass()->className.c_str());
|
||||
}
|
||||
|
||||
// __newindex(t,k,v)
|
||||
static int inst_newindex(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));
|
||||
|
||||
// Validate property
|
||||
std::optional<PropertyMeta> meta = inst->GetPropertyMeta(key);
|
||||
if (!meta)
|
||||
return luaL_error(L, "'%s' is not a valid member of %s", key.c_str(), inst->GetClass()->className.c_str());
|
||||
if (meta->flags & PROP_READONLY)
|
||||
return luaL_error(L, "'%s' of %s is read-only", key.c_str(), inst->GetClass()->className.c_str());
|
||||
if (key == "Parent" && inst->IsParentLocked())
|
||||
return luaL_error(L, "Cannot set property Parent (%s) of %s, parent is locked", inst->GetParent() ? inst->GetParent().value()->name.c_str() : "NULL", inst->GetClass()->className.c_str());
|
||||
|
||||
result<Data::Variant, LuaCastError> value = meta->type->fromLuaValue(L, -1);
|
||||
lua_pop(L, 3);
|
||||
|
||||
if (value.isError())
|
||||
return luaL_error(L, "%s", value.errorMessage().value().c_str());
|
||||
inst->SetPropertyValue(key, value.expect()).expect();
|
||||
return 0;
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
#include "error/data.h"
|
||||
#include <memory>
|
||||
|
||||
class Instance;
|
||||
|
@ -21,6 +22,7 @@ namespace Data {
|
|||
virtual const Data::String ToString() const override;
|
||||
virtual void Serialize(pugi::xml_node node) const override;
|
||||
virtual void PushLuaValue(lua_State*) const override;
|
||||
// static Data::Variant Deserialize(pugi::xml_node node);
|
||||
static Data::Variant Deserialize(pugi::xml_node node);
|
||||
static result<Data::Variant, LuaCastError> FromLuaValue(lua_State*, int idx);
|
||||
};
|
||||
}
|
||||
|
|
8
core/src/error/data.h
Normal file
8
core/src/error/data.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "error.h"
|
||||
|
||||
class LuaCastError : public Error {
|
||||
public:
|
||||
inline LuaCastError(std::string sourceType, std::string targetType) : Error("InstanceCastError", "Attempt to cast " + sourceType + " to " + targetType) {}
|
||||
};
|
|
@ -52,6 +52,13 @@ public:
|
|||
}, error().value());
|
||||
}
|
||||
|
||||
std::optional<std::string> errorMessage() {
|
||||
if (isSuccess()) return std::nullopt;
|
||||
return std::visit([&](auto&& it) {
|
||||
return it.message();
|
||||
}, error().value());
|
||||
}
|
||||
|
||||
// Equivalent to .success
|
||||
operator std::optional<T_Result>() { return success(); }
|
||||
operator bool() { return isSuccess(); }
|
||||
|
|
|
@ -48,6 +48,7 @@ Instance::Instance(const InstanceType* type) {
|
|||
.super = std::nullopt,
|
||||
.members = {
|
||||
{ "Name", { .backingField = &name, .type = &Data::String::TYPE, .codec = fieldCodecOf<Data::String, std::string>() } },
|
||||
{ "Parent", { .backingField = &parent, .type = &Data::InstanceRef::TYPE, .codec = fieldCodecOf<Data::InstanceRef, std::weak_ptr<Instance>>() } },
|
||||
{ "ClassName", { .backingField = const_cast<InstanceType*>(type), .type = &Data::String::TYPE, .codec = classNameCodec(), .flags = (PropertyFlags)(PROP_READONLY | PROP_NOSAVE) } },
|
||||
}
|
||||
});
|
||||
|
@ -202,6 +203,14 @@ result<Data::Variant, MemberNotFound> Instance::GetPropertyValue(std::string nam
|
|||
}
|
||||
|
||||
fallible<MemberNotFound, AssignToReadOnlyMember> Instance::SetPropertyValue(std::string name, Data::Variant value) {
|
||||
// Handle special case: Parent
|
||||
if (name == "Parent") {
|
||||
Data::InstanceRef ref = value.get<Data::InstanceRef>();
|
||||
std::weak_ptr<Instance> inst = ref;
|
||||
SetParent(inst.expired() ? std::nullopt : std::make_optional(inst.lock()));
|
||||
return {};
|
||||
}
|
||||
|
||||
auto meta_ = GetPropertyMeta(name);
|
||||
if (!meta_) return MemberNotFound(GetClass()->className, name);
|
||||
auto meta = meta_.expect();
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "objects/script.h"
|
||||
#include <memory>
|
||||
#include <qaction.h>
|
||||
#include <qtreeview.h>
|
||||
|
||||
#define M_mainWindow dynamic_cast<MainWindow*>(window())
|
||||
|
||||
|
@ -74,10 +75,11 @@ void ExplorerView::keyPressEvent(QKeyEvent* event) {
|
|||
void ExplorerView::mouseDoubleClickEvent(QMouseEvent *event) {
|
||||
QModelIndex index = indexAt(event->pos());
|
||||
std::shared_ptr<Instance> inst = model.fromIndex(index);
|
||||
if (!inst->IsA<Script>()) return;
|
||||
if (!inst->IsA<Script>()) return QTreeView::mouseDoubleClickEvent(event);
|
||||
|
||||
MainWindow* mainWnd = dynamic_cast<MainWindow*>(window());
|
||||
mainWnd->openScriptDocument(inst->CastTo<Script>().expect());
|
||||
QTreeView::mouseDoubleClickEvent(event);
|
||||
}
|
||||
|
||||
void ExplorerView::buildContextMenu() {
|
||||
|
|
Loading…
Add table
Reference in a new issue