Lua Wrapper component can now be serialized

This commit is contained in:
Dreaded_X 2019-06-26 20:29:28 +00:00
parent 7ff9e21b4d
commit 7d55563ec8
7 changed files with 174 additions and 62 deletions

View File

@ -7,26 +7,14 @@
#include <utility>
namespace ecs::lua {
struct Wrapper : Component {
Wrapper(std::string _name, sol::table _table) : name(_name), table(_table) {}
Wrapper() {}
struct Wrapper : TaggedComponent {
// @todo Figure out a more elegant way
Wrapper(sol::table _table) : TaggedComponent(get_typename<Wrapper>()), table(_table) {}
Wrapper() : TaggedComponent(get_typename<Wrapper>()) {}
std::string name;
sol::table table;
};
template <typename T, typename... Constructor, typename... Args>
void register_component(sol::state& lua, sol::table& table, Args... args) {
table.new_usertype<T>(get_typename<T>(),
"new", sol::factories([](Constructor... constructor) {
return std::make_pair(new T(constructor...), get_typename<T>());
}), args...
);
lua.set_function("_internal_to_" + get_typename<T>(), [] (Component* component) {
return (T*)component;
});
}
void init(sol::state& lua);
}

View File

@ -1,6 +1,12 @@
#include "ecs-lua.h"
#include "ecs.h"
namespace sol {
template<>
struct is_container<ecs::View<>> : std::true_type {};
};
namespace ecs::lua {
void init(sol::state& lua) {
// Add a preloader that loads all the ecs stuff
@ -24,9 +30,19 @@ namespace ecs::lua {
},
"get_component", [&lua] (Entity* thiz, std::string name) -> sol::object {
// Convert to the correct component type
auto f1 = lua["_internal_to_" + name];
if (f1.valid()) {
return f1(thiz->get_component(ComponentID::get_id({name})[0]));
Component* component = thiz->get_component(ComponentID::get_id({name})[0]);
if (!component->_tagged()) {
// @todo Figure out a more elegant way to convert
auto f1 = lua["_internal_to_" + name];
if (f1.valid()) {
return f1(component);
}
} else {
TaggedComponent* tagged_component = (TaggedComponent*)component;
auto f1 = lua["_internal_to_" + tagged_component->_tag];
if (f1.valid()) {
return f1(component);
}
}
throw std::runtime_error("Unknown component");
}
@ -41,6 +57,14 @@ namespace ecs::lua {
// @todo Allow to construct with given uuid
return thiz->create_entity();
},
"has_entity", [] (Manager* thiz, std::string uuid) {
// @todo Check if valid
return thiz->has_entity(uuids::uuid::from_string(uuid).value());
},
"get_entity", [] (Manager* thiz, std::string uuid) {
// @todo Check if valid
return thiz->get_entity(uuids::uuid::from_string(uuid).value());
},
"view", [] (Manager* thiz, sol::variadic_args args) {
std::vector<std::string> names;
for (std::string name : args) {
@ -50,12 +74,29 @@ namespace ecs::lua {
}
);
register_component<Wrapper, std::string, sol::object>(lua, ecs,
"name", &Wrapper::name,
"table", &Wrapper::table
return ecs;
};
preload["ecs.Wrapper"] = [&lua] {
sol::table component = lua.create_table();
component.new_usertype<Wrapper>("Wrapper",
"__index", [] (Wrapper* thiz, std::string key) {
return thiz->table[key];
},
"__newindex", [] (Wrapper* thiz, std::string key, sol::object value) {
thiz->table[key] = value;
}
);
return ecs;
component.set_function("new", sol::factories([](std::string _name, sol::table _table) {
return std::make_pair(new Wrapper(_table), _name);
}));
lua.set_function("_internal_to_Wrapper", [] (ecs::Component* component) {
return (Wrapper*)component;
});
return component;
};
}
}

View File

@ -19,13 +19,29 @@ namespace ecs::serial {
auto components = entity->get_components();
iohelper::write_length(os, components.size());
for (auto [id, component] : components) {
auto functions = internal::functions.find(id);
if (functions == internal::functions.end()) {
throw std::runtime_error("Unknown id");
}
if (!component->_tagged()) {
auto functions = internal::functions.find(id);
if (functions == internal::functions.end()) {
throw std::runtime_error("No known serializer for id");
}
iohelper::write_length(os, id);
std::get<0>(internal::functions[id])(os, component);
iohelper::write_length(os, id);
iohelper::write<bool>(os, false);
std::get<0>(internal::functions[id])(os, component);
} else {
TaggedComponent* tagged_component = (TaggedComponent*)component;
auto new_id = ComponentID::get_id({tagged_component->_tag})[0];
auto functions = internal::functions.find(new_id);
if (functions == internal::functions.end()) {
throw std::runtime_error("No known serializer for id");
}
iohelper::write_length(os, new_id);
iohelper::write<bool>(os, true);
iohelper::write_length(os, id);
std::get<0>(internal::functions[new_id])(os, component);
}
}
}
@ -38,7 +54,13 @@ namespace ecs::serial {
size_t component_count = iohelper::read_length(is);
// std::cout << "Updating " << component_count << " components in entity: " << uuid << '\n';
for (size_t i = 0; i < component_count; ++i) {
size_t id = iohelper::read_length(is);
size_t new_id = iohelper::read_length(is);
bool tagged = iohelper::read<bool>(is);
size_t id = new_id;
if (tagged) {
id = iohelper::read_length(is);
}
// @todo We also need to be able to remove components
// Sending a component with length 0 -> remove component
// However we might have components that have no data and are just like tags
@ -49,12 +71,12 @@ namespace ecs::serial {
// Update the component
std::cout << "Updating component: " << id << '\n';
Component* component = entity->get_component(id);
std::get<1>(internal::functions[id])(is, component);
std::get<1>(internal::functions[new_id])(is, component);
} else {
// Add new component
std::cout << "Adding component: " << id << '\n';
Component* component = std::get<2>(internal::functions[id])();
std::get<1>(internal::functions[id])(is, component);
Component* component = std::get<2>(internal::functions[new_id])();
std::get<1>(internal::functions[new_id])(is, component);
entity->add_component(id, component);
}
}
@ -64,10 +86,16 @@ namespace ecs::serial {
size_t component_count = iohelper::read_length(is);
// std::cout << "Creating entity with " << component_count << " components: " << uuid << '\n';
for (size_t i = 0; i < component_count; ++i) {
size_t id = iohelper::read_length(is);
size_t new_id = iohelper::read_length(is);
bool tagged = iohelper::read<bool>(is);
size_t id = new_id;
if (tagged) {
id = iohelper::read_length(is);
}
std::cout << "Adding component: " << id << '\n';
Component* component = std::get<2>(internal::functions[id])();
std::get<1>(internal::functions[id])(is, component);
Component* component = std::get<2>(internal::functions[new_id])();
std::get<1>(internal::functions[new_id])(is, component);
entity->add_component(id, component);
}
}

View File

@ -47,6 +47,16 @@ namespace ecs {
struct Component {
virtual ~Component() {}
virtual bool _tagged() { return false; }
};
// Tag stores the real name of a the component for special components that allow duplicates
struct TaggedComponent : Component{
TaggedComponent(std::string tag) : _tag(tag) {}
const std::string _tag;
bool _tagged() override { return true; }
};
class Entity {

View File

@ -16,8 +16,16 @@
#include "ecs.h"
#include "ecs_components.h"
inline void handle_error(sol::optional<std::string> maybe_msg) {
std::cerr << "Lua error, aborting!\n";
if (maybe_msg) {
const std::string& msg = maybe_msg.value();
std::cerr << "Error: " << msg << '\n';
}
}
int main() {
sol::state lua;
sol::state lua(sol::c_call<decltype(&handle_error), &handle_error>);
lua.open_libraries(sol::lib::base, sol::lib::package);
ecs::lua::init(lua);
@ -31,7 +39,7 @@ int main() {
[](std::ostream& os, ecs::Component* component) {
ecs::lua::Wrapper* wrapper = (ecs::lua::Wrapper*)component;
iohelper::write<std::string>(os, wrapper->name);
// iohelper::write<std::string>(os, wrapper->name);
// #todo .size() does not work
size_t size = 0;
@ -55,7 +63,8 @@ int main() {
break;
case sol::type::number:
iohelper::write<int32_t>(os, b.as<int32_t>());
// @todo These should be doubles instead of floats
iohelper::write<float>(os, b.as<float>());
break;
case sol::type::boolean:
@ -73,11 +82,10 @@ int main() {
}
}
},
[](std::istream& is, ecs::Component* component) {
[&lua](std::istream& is, ecs::Component* component) {
ecs::lua::Wrapper* wrapper = (ecs::lua::Wrapper*)component;
wrapper->name = iohelper::read<std::string>(is);
std::cout << wrapper->name << '\n';
// @todo Only do this if table is not created yet
wrapper->table = lua.create_table();
size_t size = iohelper::read_length(is);
std::cout << "Size: " << size << '\n';
@ -95,15 +103,16 @@ int main() {
break;
case sol::type::string:
std::cout << "Value: " << iohelper::read<std::string>(is) << '\n';
wrapper->table[name] = iohelper::read<std::string>(is);
break;
case sol::type::number:
std::cout << "Value: " << std::dec << iohelper::read<int32_t>(is) << '\n';
// @todo Handle floats
wrapper->table[name] = iohelper::read<float>(is);
break;
case sol::type::boolean:
std::cout << "Value: " << std::dec << iohelper::read<bool>(is) << '\n';
wrapper->table[name] = iohelper::read<bool>(is);
break;
default:
@ -120,7 +129,7 @@ int main() {
return manager;
});
lua.script_file("test.lua");
lua.safe_script_file("test.lua");
std::cout << "Update position\n";
manager.view<Position, Velocity>().for_each([](ecs::Entity*, Position* pos, Velocity* vel) {
@ -186,6 +195,7 @@ int main() {
// This is needed to avoid id conflicts between clients
std::ofstream ids2("ids", std::ios::out | std::ios::trunc);
ecs::serial::serialize_ids(ids2);
ids2.close();
}
{
@ -218,5 +228,11 @@ int main() {
std::cout << "x: " << pos->x << '\n';
std::cout << "y: " << pos->y << '\n';
});
lua.set_function("get_manager", [&manager] () -> ecs::Manager& {
return manager;
});
lua.safe_script_file("test2.lua");
}
}

View File

@ -3,6 +3,7 @@ local ecs = require "ecs"
local Position = require "ecs.Position"
local Velocity = require "ecs.Velocity"
local Meta = require "ecs.Meta"
local Wrapper = require 'ecs.Wrapper'
manager = get_manager()
ent = manager:create_entity()
@ -10,12 +11,13 @@ ent:add_component(Position.new(1.9, 9.7))
ent:add_component(Velocity.new(0.2, 0.3))
ent:add_component(Meta.new("Soldier"))
random = {
data = {
speed = 10,
something = "Hello, World!",
alive = true
}
ent:add_component(ecs.Wrapper.new("Random", random))
ent:add_component(Wrapper.new("LuaData", data))
ent:add_component(Wrapper.new("TestThing", { test = 1.23563 }))
-- @todo Make this happen...
-- stats = {
@ -60,20 +62,26 @@ end)
-- print "TEST"
-- end
manager:view("Wrapper"):for_each(function(ent)
wrapped = ent:get_component("Wrapper").table
manager:view("LuaData"):for_each(function(ent)
d = ent:get_component("LuaData")
print(wrapped.speed)
wrapped.speed = 11
print(wrapped.speed)
print(random.speed)
random.speed = 20
print(wrapped.speed)
print(random.speed)
print(d.speed)
d.speed = 11
print(d.speed)
print(data.speed)
data.speed = 20
print(d.speed)
print(data.speed)
print(wrapped.alive)
wrapped.alive = false
print(wrapped.alive)
print(d.alive)
d.alive = false
print(d.alive)
print(wrapped.something)
print(d.something)
end)
manager:view("TestThing"):for_each(function(ent)
t = ent:get_component("TestThing")
print(t.test)
end)

21
test/test2.lua Normal file
View File

@ -0,0 +1,21 @@
manager = get_manager()
manager:view("LuaData"):for_each(function(ent)
-- @todo It would be nice if this could somehow be passed in as function arg
data = ent:get_component("LuaData")
print("speed: " .. data.speed)
print("something: " .. data.something)
-- print("alive: " .. data.alive)
end)
manager:view("TestThing"):for_each(function(ent)
data = ent:get_component("TestThing")
print("test: " .. data.test)
end)
-- @todo Allow this
-- for i, v in pairs(manager:view("TestThing")) do
-- print(i, v)
-- end
print(manager:has_entity("6d58fdb5-6d8c-4e6f-89d4-f7d7b184f463"))