From 7d55563ec8b1f32053d9b81bfd7b1a79056ef68f Mon Sep 17 00:00:00 2001 From: Dreaded_X Date: Wed, 26 Jun 2019 20:29:28 +0000 Subject: [PATCH] Lua Wrapper component can now be serialized --- ecs-lua/include/ecs-lua.h | 20 +++---------- ecs-lua/src/ecs-lua.cpp | 55 ++++++++++++++++++++++++++++++----- ecs-serial/src/ecs-serial.cpp | 54 +++++++++++++++++++++++++--------- ecs/include/ecs.h | 10 +++++++ test/src/main.cpp | 38 +++++++++++++++++------- test/test.lua | 38 ++++++++++++++---------- test/test2.lua | 21 +++++++++++++ 7 files changed, 174 insertions(+), 62 deletions(-) create mode 100644 test/test2.lua diff --git a/ecs-lua/include/ecs-lua.h b/ecs-lua/include/ecs-lua.h index 62fe37e..ce75e35 100644 --- a/ecs-lua/include/ecs-lua.h +++ b/ecs-lua/include/ecs-lua.h @@ -7,26 +7,14 @@ #include 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()), table(_table) {} + Wrapper() : TaggedComponent(get_typename()) {} - std::string name; sol::table table; }; - template - void register_component(sol::state& lua, sol::table& table, Args... args) { - table.new_usertype(get_typename(), - "new", sol::factories([](Constructor... constructor) { - return std::make_pair(new T(constructor...), get_typename()); - }), args... - ); - lua.set_function("_internal_to_" + get_typename(), [] (Component* component) { - return (T*)component; - }); - } - void init(sol::state& lua); } diff --git a/ecs-lua/src/ecs-lua.cpp b/ecs-lua/src/ecs-lua.cpp index 7c4e002..45e76b3 100644 --- a/ecs-lua/src/ecs-lua.cpp +++ b/ecs-lua/src/ecs-lua.cpp @@ -1,6 +1,12 @@ #include "ecs-lua.h" #include "ecs.h" + +namespace sol { + template<> + struct is_container> : 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 names; for (std::string name : args) { @@ -50,12 +74,29 @@ namespace ecs::lua { } ); - register_component(lua, ecs, - "name", &Wrapper::name, - "table", &Wrapper::table + return ecs; + }; + + preload["ecs.Wrapper"] = [&lua] { + sol::table component = lua.create_table(); + component.new_usertype("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; }; } } diff --git a/ecs-serial/src/ecs-serial.cpp b/ecs-serial/src/ecs-serial.cpp index d865e1b..f2e8563 100644 --- a/ecs-serial/src/ecs-serial.cpp +++ b/ecs-serial/src/ecs-serial.cpp @@ -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(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(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(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(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); } } diff --git a/ecs/include/ecs.h b/ecs/include/ecs.h index 83b54ae..e8889e2 100644 --- a/ecs/include/ecs.h +++ b/ecs/include/ecs.h @@ -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 { diff --git a/test/src/main.cpp b/test/src/main.cpp index 25c4e7c..ce3a8e5 100644 --- a/test/src/main.cpp +++ b/test/src/main.cpp @@ -16,8 +16,16 @@ #include "ecs.h" #include "ecs_components.h" +inline void handle_error(sol::optional 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); 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(os, wrapper->name); + // iohelper::write(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(os, b.as()); + // @todo These should be doubles instead of floats + iohelper::write(os, b.as()); 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(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(is) << '\n'; + wrapper->table[name] = iohelper::read(is); break; case sol::type::number: - std::cout << "Value: " << std::dec << iohelper::read(is) << '\n'; + // @todo Handle floats + wrapper->table[name] = iohelper::read(is); break; case sol::type::boolean: - std::cout << "Value: " << std::dec << iohelper::read(is) << '\n'; + wrapper->table[name] = iohelper::read(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().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"); } } diff --git a/test/test.lua b/test/test.lua index ce61f55..a96b4bb 100644 --- a/test/test.lua +++ b/test/test.lua @@ -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) diff --git a/test/test2.lua b/test/test2.lua new file mode 100644 index 0000000..444cee1 --- /dev/null +++ b/test/test2.lua @@ -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"))