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> #include <utility>
namespace ecs::lua { namespace ecs::lua {
struct Wrapper : Component { struct Wrapper : TaggedComponent {
Wrapper(std::string _name, sol::table _table) : name(_name), table(_table) {} // @todo Figure out a more elegant way
Wrapper() {} Wrapper(sol::table _table) : TaggedComponent(get_typename<Wrapper>()), table(_table) {}
Wrapper() : TaggedComponent(get_typename<Wrapper>()) {}
std::string name;
sol::table table; 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); void init(sol::state& lua);
} }

View File

@ -1,6 +1,12 @@
#include "ecs-lua.h" #include "ecs-lua.h"
#include "ecs.h" #include "ecs.h"
namespace sol {
template<>
struct is_container<ecs::View<>> : std::true_type {};
};
namespace ecs::lua { namespace ecs::lua {
void init(sol::state& lua) { void init(sol::state& lua) {
// Add a preloader that loads all the ecs stuff // 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 { "get_component", [&lua] (Entity* thiz, std::string name) -> sol::object {
// Convert to the correct component type // Convert to the correct component type
auto f1 = lua["_internal_to_" + name]; Component* component = thiz->get_component(ComponentID::get_id({name})[0]);
if (f1.valid()) { if (!component->_tagged()) {
return f1(thiz->get_component(ComponentID::get_id({name})[0])); // @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"); throw std::runtime_error("Unknown component");
} }
@ -41,6 +57,14 @@ namespace ecs::lua {
// @todo Allow to construct with given uuid // @todo Allow to construct with given uuid
return thiz->create_entity(); 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) { "view", [] (Manager* thiz, sol::variadic_args args) {
std::vector<std::string> names; std::vector<std::string> names;
for (std::string name : args) { for (std::string name : args) {
@ -50,12 +74,29 @@ namespace ecs::lua {
} }
); );
register_component<Wrapper, std::string, sol::object>(lua, ecs, return ecs;
"name", &Wrapper::name, };
"table", &Wrapper::table
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(); auto components = entity->get_components();
iohelper::write_length(os, components.size()); iohelper::write_length(os, components.size());
for (auto [id, component] : components) { for (auto [id, component] : components) {
auto functions = internal::functions.find(id); if (!component->_tagged()) {
if (functions == internal::functions.end()) { auto functions = internal::functions.find(id);
throw std::runtime_error("Unknown id"); if (functions == internal::functions.end()) {
} throw std::runtime_error("No known serializer for id");
}
iohelper::write_length(os, id); iohelper::write_length(os, id);
std::get<0>(internal::functions[id])(os, component); 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); size_t component_count = iohelper::read_length(is);
// std::cout << "Updating " << component_count << " components in entity: " << uuid << '\n'; // std::cout << "Updating " << component_count << " components in entity: " << uuid << '\n';
for (size_t i = 0; i < component_count; ++i) { 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 // @todo We also need to be able to remove components
// Sending a component with length 0 -> remove component // Sending a component with length 0 -> remove component
// However we might have components that have no data and are just like tags // However we might have components that have no data and are just like tags
@ -49,12 +71,12 @@ namespace ecs::serial {
// Update the component // Update the component
std::cout << "Updating component: " << id << '\n'; std::cout << "Updating component: " << id << '\n';
Component* component = entity->get_component(id); Component* component = entity->get_component(id);
std::get<1>(internal::functions[id])(is, component); std::get<1>(internal::functions[new_id])(is, component);
} else { } else {
// Add new component // Add new component
std::cout << "Adding component: " << id << '\n'; std::cout << "Adding component: " << id << '\n';
Component* component = std::get<2>(internal::functions[id])(); Component* component = std::get<2>(internal::functions[new_id])();
std::get<1>(internal::functions[id])(is, component); std::get<1>(internal::functions[new_id])(is, component);
entity->add_component(id, component); entity->add_component(id, component);
} }
} }
@ -64,10 +86,16 @@ namespace ecs::serial {
size_t component_count = iohelper::read_length(is); size_t component_count = iohelper::read_length(is);
// std::cout << "Creating entity with " << component_count << " components: " << uuid << '\n'; // std::cout << "Creating entity with " << component_count << " components: " << uuid << '\n';
for (size_t i = 0; i < component_count; ++i) { 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'; std::cout << "Adding component: " << id << '\n';
Component* component = std::get<2>(internal::functions[id])(); Component* component = std::get<2>(internal::functions[new_id])();
std::get<1>(internal::functions[id])(is, component); std::get<1>(internal::functions[new_id])(is, component);
entity->add_component(id, component); entity->add_component(id, component);
} }
} }

View File

@ -47,6 +47,16 @@ namespace ecs {
struct Component { struct Component {
virtual ~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 { class Entity {

View File

@ -16,8 +16,16 @@
#include "ecs.h" #include "ecs.h"
#include "ecs_components.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() { 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); lua.open_libraries(sol::lib::base, sol::lib::package);
ecs::lua::init(lua); ecs::lua::init(lua);
@ -31,7 +39,7 @@ int main() {
[](std::ostream& os, ecs::Component* component) { [](std::ostream& os, ecs::Component* component) {
ecs::lua::Wrapper* wrapper = (ecs::lua::Wrapper*)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 // #todo .size() does not work
size_t size = 0; size_t size = 0;
@ -55,7 +63,8 @@ int main() {
break; break;
case sol::type::number: 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; break;
case sol::type::boolean: 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; ecs::lua::Wrapper* wrapper = (ecs::lua::Wrapper*)component;
// @todo Only do this if table is not created yet
wrapper->name = iohelper::read<std::string>(is); wrapper->table = lua.create_table();
std::cout << wrapper->name << '\n';
size_t size = iohelper::read_length(is); size_t size = iohelper::read_length(is);
std::cout << "Size: " << size << '\n'; std::cout << "Size: " << size << '\n';
@ -95,15 +103,16 @@ int main() {
break; break;
case sol::type::string: case sol::type::string:
std::cout << "Value: " << iohelper::read<std::string>(is) << '\n'; wrapper->table[name] = iohelper::read<std::string>(is);
break; break;
case sol::type::number: 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; break;
case sol::type::boolean: case sol::type::boolean:
std::cout << "Value: " << std::dec << iohelper::read<bool>(is) << '\n'; wrapper->table[name] = iohelper::read<bool>(is);
break; break;
default: default:
@ -120,7 +129,7 @@ int main() {
return manager; return manager;
}); });
lua.script_file("test.lua"); lua.safe_script_file("test.lua");
std::cout << "Update position\n"; std::cout << "Update position\n";
manager.view<Position, Velocity>().for_each([](ecs::Entity*, Position* pos, Velocity* vel) { 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 // This is needed to avoid id conflicts between clients
std::ofstream ids2("ids", std::ios::out | std::ios::trunc); std::ofstream ids2("ids", std::ios::out | std::ios::trunc);
ecs::serial::serialize_ids(ids2); ecs::serial::serialize_ids(ids2);
ids2.close();
} }
{ {
@ -218,5 +228,11 @@ int main() {
std::cout << "x: " << pos->x << '\n'; std::cout << "x: " << pos->x << '\n';
std::cout << "y: " << pos->y << '\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 Position = require "ecs.Position"
local Velocity = require "ecs.Velocity" local Velocity = require "ecs.Velocity"
local Meta = require "ecs.Meta" local Meta = require "ecs.Meta"
local Wrapper = require 'ecs.Wrapper'
manager = get_manager() manager = get_manager()
ent = manager:create_entity() 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(Velocity.new(0.2, 0.3))
ent:add_component(Meta.new("Soldier")) ent:add_component(Meta.new("Soldier"))
random = { data = {
speed = 10, speed = 10,
something = "Hello, World!", something = "Hello, World!",
alive = true 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... -- @todo Make this happen...
-- stats = { -- stats = {
@ -60,20 +62,26 @@ end)
-- print "TEST" -- print "TEST"
-- end -- end
manager:view("Wrapper"):for_each(function(ent) manager:view("LuaData"):for_each(function(ent)
wrapped = ent:get_component("Wrapper").table d = ent:get_component("LuaData")
print(wrapped.speed) print(d.speed)
wrapped.speed = 11 d.speed = 11
print(wrapped.speed) print(d.speed)
print(random.speed) print(data.speed)
random.speed = 20 data.speed = 20
print(wrapped.speed) print(d.speed)
print(random.speed) print(data.speed)
print(wrapped.alive) print(d.alive)
wrapped.alive = false d.alive = false
print(wrapped.alive) 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) 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"))