From c9f05af6c9e42e4d692efb23eac33c5d5152f061 Mon Sep 17 00:00:00 2001 From: Dreaded_X Date: Wed, 13 May 2020 01:17:25 +0200 Subject: [PATCH] We can properly use range based for loops with view and interaction with Lua is significantly improved --- ecs-lua/include/ecs-lua.h | 4 +- ecs-lua/src/ecs-lua.cpp | 167 ++++++++++++++++---------------- ecs/include/ecs.h | 124 ++++++++++++++++++------ ecs/src/ecs.cpp | 10 +- test/generated/ecs_components.h | 12 ++- test/src/main.cpp | 36 +++---- test/test.lua | 32 +++--- test/test2.lua | 21 ++-- 8 files changed, 234 insertions(+), 172 deletions(-) diff --git a/ecs-lua/include/ecs-lua.h b/ecs-lua/include/ecs-lua.h index 2251a9d..aeea9f1 100644 --- a/ecs-lua/include/ecs-lua.h +++ b/ecs-lua/include/ecs-lua.h @@ -7,9 +7,9 @@ #include namespace ecs::lua { - struct Wrapper : Component { + struct LuaComponent : Component { // @todo Figure out a more elegant way - Wrapper() {} + LuaComponent() {} sol::table table; }; diff --git a/ecs-lua/src/ecs-lua.cpp b/ecs-lua/src/ecs-lua.cpp index 8d44e4c..10c08b5 100644 --- a/ecs-lua/src/ecs-lua.cpp +++ b/ecs-lua/src/ecs-lua.cpp @@ -18,8 +18,80 @@ namespace ecs::lua { preload["ecs"] = [&lua] { sol::table ecs = lua.create_table(); - ecs.set_function("register", [&lua] (std::string name, sol::table table) { - size_t id = ComponentID::regist(name); + ecs.new_usertype("Entity", + "__tostring", [] (Entity* thiz) { + return uuids::to_string(thiz->uuid); + }, + "add_component", [] (Entity* thiz, size_t id, Component* component) { + return thiz->add_component(id, component); + }, + "has_components", [] (Entity* thiz, sol::variadic_args args) { + std::vector ids; + for (sol::table table : args) { + // @todo Check that the table has id and convert() + ids.push_back(table["_id"]); + } + return thiz->has_components(ids); + }, + "get_component", [&lua] (Entity* thiz, sol::table table) -> sol::object { + // @todo Check that the table has id and convert() + + Component* component = thiz->get_component(table["_id"]); + auto f1 = table["_convert"]; + if (f1.valid()) { + return f1(component); + } + throw std::runtime_error("Unknown component"); + } + ); + + ecs.new_usertype>("View", + "for_each", &View<>::for_each + ); + + ecs.new_usertype("Manager", + "create_entity", [] (Manager* thiz) { + // @todo Allow to construct with given uuid + return thiz->create_entity(); + }, + "remove_entity", &Manager::remove_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 ids; + for (sol::table table : args) { + // @todo Check that the table has id and convert() + ids.push_back(table["_id"]); + } + return thiz->view(ids); + } + ); + + return ecs; + }; + + preload["ecs.LuaComponent"] = [&lua] { + lua.new_usertype("LuaComponent", + "__index", [] (LuaComponent* thiz, std::string key) { + return thiz->table[key]; + }, + "__newindex", [] (LuaComponent* thiz, std::string key, sol::object value) { + thiz->table[key] = value; + }, + sol::base_classes, sol::bases() + ); + + sol::table comp = lua.create_table(); + comp.set_function("create", [&lua] (std::string name, sol::table table) { + std::cout << "Creating new component: " << name << '\n'; + size_t id = ComponentID::add(name); + std::cout << "ID: " << id << '\n'; std::unordered_map map; @@ -38,8 +110,7 @@ namespace ecs::lua { #ifdef _OPTIONAL_ECS_SERIAL auto serialize = [map, name] (std::ostream& os, ecs::Component* component) { - std::cout << "SERIALIZE: " << name << '\n'; - Wrapper* wrapper = (Wrapper*)component; + LuaComponent* wrapper = (LuaComponent*)component; // Write each of the entries io::write(os, map.size()); @@ -65,9 +136,7 @@ namespace ecs::lua { }; auto deserialize = [map] (std::istream& is, Component* component) { - std::cout << "DESERIALIZE\n"; - - Wrapper* wrapper = (Wrapper*)component; + LuaComponent* wrapper = (LuaComponent*)component; // Write each of the entries size_t size = io::read(is); @@ -80,17 +149,14 @@ namespace ecs::lua { switch (type) { case sol::type::string: - std::cout << "STRING\n"; wrapper->table[key] = io::read(is); break; case sol::type::number: - std::cout << "NUMBER\n"; wrapper->table[key] = io::read(is); break; case sol::type::boolean: - std::cout << "BOOL\n"; wrapper->table[key] = io::read(is); break; @@ -101,8 +167,7 @@ namespace ecs::lua { }; auto create = [&lua, map] { - std::cout << "CREATE\n"; - Wrapper* wrapper = new Wrapper(); + LuaComponent* wrapper = new LuaComponent(); wrapper->table = lua.create_table(); for (auto [key, type] : map) { @@ -130,14 +195,11 @@ namespace ecs::lua { ecs::serial::register_component_custom(id, serialize, deserialize, create); #endif - lua.set_function("_internal_to_" + name, [] (ecs::Component* component) { - return (Wrapper*)component; - }); - sol::table component = lua.create_table(); + component["_id"] = id; component.set_function("new", sol::factories([&lua, id, map] (sol::table table) { - Wrapper* wrapper = new Wrapper(); + LuaComponent* wrapper = new LuaComponent(); wrapper->table = lua.create_table(); for (auto [key, type] : map) { @@ -147,75 +209,14 @@ namespace ecs::lua { return std::make_pair(id, wrapper); })); + component.set_function("_convert", [] (ecs::Component* component) { + return (LuaComponent*)component; + }); + return component; }); - ecs.new_usertype("Entity", - "__tostring", [] (Entity* thiz) { - return uuids::to_string(thiz->uuid); - }, - "add_component", [] (Entity* thiz, size_t id, Component* component) { - return thiz->add_component(id, component); - }, - "has_components", [] (Entity* thiz, sol::variadic_args args) { - std::vector names; - for (std::string name : args) { - names.push_back(name); - } - return thiz->has_components(ComponentID::resolve(names)); - }, - "get_component", [&lua] (Entity* thiz, std::string name) -> sol::object { - // Convert to the correct component type - Component* component = thiz->get_component(ComponentID::resolve(name)); - // @todo Figure out a more elegant way to convert - auto f1 = lua["_internal_to_" + name]; - if (f1.valid()) { - return f1(component); - } - throw std::runtime_error("Unknown component"); - } - ); - - ecs.new_usertype>("View", - "for_each", &View<>::for_each - ); - - ecs.new_usertype("Manager", - "create_entity", [] (Manager* thiz) { - // @todo Allow to construct with given uuid - return thiz->create_entity(); - }, - "remove_entity", &Manager::remove_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) { - names.push_back(name); - } - return thiz->view(ComponentID::resolve(names)); - } - ); - - return ecs; - }; - - preload["ecs.Wrapper"] = [&lua] { - lua.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; - }, - sol::base_classes, sol::bases() - ); + return comp; }; } } diff --git a/ecs/include/ecs.h b/ecs/include/ecs.h index ac62cd3..4ecd4ff 100644 --- a/ecs/include/ecs.h +++ b/ecs/include/ecs.h @@ -20,13 +20,13 @@ namespace ecs { public: template - static size_t regist(std::string name) { + static size_t add(std::string name) { _ids.insert({name, id}); // @todo Do some other register stuff return id; } - static size_t regist(std::string name) { + static size_t add(std::string name) { size_t id = next_id++; _ids.insert({name, id}); return id; @@ -84,23 +84,16 @@ namespace ecs { Entity(uuids::uuid _uuid) : uuid(_uuid) {} ~Entity(); - void add_component(size_t id, Component* component); + Component* add_component(size_t id, Component* component); bool has_components(std::vector ids) const; - bool has_components(size_t id) const; Component* get_component(size_t id) const; template - void add_component(Args... args) { + T* add_component(Args... args) { size_t id = ComponentID::id; + T* component = new T(args...); - std::cout << "ID: " << id << '\n'; - - if (_components.find(id) != _components.end()) { - throw std::runtime_error("Component already exists"); - } - - // @todo Also register the component to the manager - _components[id] = new T(args...); + return (T*)add_component(id, component); } template @@ -140,34 +133,106 @@ namespace ecs { } } + class ViewIterator; + size_t size() const { return _entities.size(); } auto begin() { - return _entities.begin(); + return ViewIterator(_entities.begin()); } auto begin() const { - return _entities.begin(); + return ConstViewIterator(_entities.begin()); } auto cbegin() const { - return _entities.cbegin(); + return ConstViewIterator(_entities.cbegin()); } auto end() { - return _entities.end(); + return ViewIterator(_entities.end()); } auto end() const { - return _entities.end(); + return ConstViewIterator(_entities.end()); } auto cend() const { - return _entities.cend(); + return ConstViewIterator(_entities.cend()); } + class ViewIterator { + public: + using Iiterator = std::unordered_map::iterator; + + ViewIterator(Iiterator it) : it(it) {} + + ViewIterator& operator=(Iiterator it) { + this->it = it; + return *this; + } + + ViewIterator& operator++() { + it++; + return *this; + } + + ViewIterator operator++(int) { + ViewIterator iterator = *this; + ++*this; + return iterator; + } + + bool operator!=(const ViewIterator& other) const { + return it != other.it; + } + + auto operator*() { + return std::make_tuple(it->second, it->second->get_component()...); + } + + + private: + Iiterator it; + }; + + class ConstViewIterator { + public: + using Iiterator = std::unordered_map::const_iterator; + + ConstViewIterator(Iiterator it) : it(it) {} + + ConstViewIterator& operator=(Iiterator it) { + this->it = it; + return *this; + } + + ConstViewIterator& operator++() { + it++; + return *this; + } + + ConstViewIterator operator++(int) { + ViewIterator iterator = *this; + ++*this; + return iterator; + } + + bool operator!=(const ViewIterator& other) const { + return it != other.it; + } + + const auto operator*() const { + return std::make_tuple(it->second, it->second->get_component()...); + } + + + private: + Iiterator it; + }; + private: std::unordered_map _entities; }; @@ -190,18 +255,6 @@ namespace ecs { return View(entities); } - View<> view(size_t id) { - std::unordered_map entities; - - for (auto [uuid, entity] : _entities) { - if (entity->has_components(id)) { - entities.insert({uuid, entity}); - } - } - - return View<>(entities); - } - View<> view(std::vector ids) { std::unordered_map entities; @@ -217,6 +270,15 @@ namespace ecs { Entity* create_entity(uuids::uuid uuid); Entity* create_entity(); + template + T* create_entity(Args... args) { + uuids::uuid uuid = uuids::uuid_system_generator{}(); + + T* entity = new T(uuid, args...); + _entities.insert({uuid, entity}); + return entity; + } + void remove_entity(ecs::Entity* entity); bool has_entity(uuids::uuid uuid) { diff --git a/ecs/src/ecs.cpp b/ecs/src/ecs.cpp index 93b569c..f814202 100644 --- a/ecs/src/ecs.cpp +++ b/ecs/src/ecs.cpp @@ -15,7 +15,7 @@ namespace ecs { _components.clear(); } - void Entity::add_component(size_t id, Component* component) { + Component* Entity::add_component(size_t id, Component* component) { if (id == 0) { throw std::runtime_error("Unknown component!"); } @@ -24,14 +24,8 @@ namespace ecs { } _components[id] = component; - } - bool Entity::has_components(size_t id) const { - if (_components.find(id) == _components.end()) { - return false; - } - - return true; + return component; } bool Entity::has_components(std::vector ids) const { diff --git a/test/generated/ecs_components.h b/test/generated/ecs_components.h index 19c9351..84e2ae2 100644 --- a/test/generated/ecs_components.h +++ b/test/generated/ecs_components.h @@ -9,6 +9,8 @@ namespace generated { preload["ecs.Position"] = [&lua] { sol::table component = lua.create_table(); + component["_id"] = ecs::ComponentID::id; + component.new_usertype("Position", "x", &Position::x, "y", &Position::y, sol::base_classes, sol::bases() @@ -18,7 +20,7 @@ namespace generated { return std::make_pair(ecs::ComponentID::id, new Position(_x, _y)); })); - lua.set_function("_internal_to_Position", [] (ecs::Component* component) { + component.set_function("_convert", [] (ecs::Component* component) { return (Position*)component; }); @@ -27,6 +29,8 @@ namespace generated { preload["ecs.Velocity"] = [&lua] { sol::table component = lua.create_table(); + component["_id"] = ecs::ComponentID::id; + component.new_usertype("Velocity", "x", &Velocity::x, "y", &Velocity::y, sol::base_classes, sol::bases() @@ -36,7 +40,7 @@ namespace generated { return std::make_pair(ecs::ComponentID::id, new Velocity(_x, _y)); })); - lua.set_function("_internal_to_Velocity", [] (ecs::Component* component) { + component.set_function("_convert", [] (ecs::Component* component) { return (Velocity*)component; }); @@ -45,6 +49,8 @@ namespace generated { preload["ecs.Meta"] = [&lua] { sol::table component = lua.create_table(); + component["_id"] = ecs::ComponentID::id; + component.new_usertype("Meta", "name", &Meta::name, sol::base_classes, sol::bases() @@ -54,7 +60,7 @@ namespace generated { return std::make_pair(ecs::ComponentID::id, new Meta(_name)); })); - lua.set_function("_internal_to_Meta", [] (ecs::Component* component) { + component.set_function("_convert", [] (ecs::Component* component) { return (Meta*)component; }); diff --git a/test/src/main.cpp b/test/src/main.cpp index b9b24b5..e5312fb 100644 --- a/test/src/main.cpp +++ b/test/src/main.cpp @@ -14,6 +14,14 @@ #include "ecs.h" #include "ecs_components.h" +class Orb : public ecs::Entity { + public: + Orb(uuids::uuid uuid, float x, float y) : Entity(uuid) { + auto position = add_component(x, y); + auto velocity = add_component(0, 0); + } +}; + inline void handle_error(sol::optional maybe_msg) { std::cerr << "Lua error, aborting!\n"; if (maybe_msg) { @@ -38,10 +46,9 @@ int main(int argc, const char** argv) { return -1; } - ecs::ComponentID::regist("Wrapper"); - ecs::ComponentID::regist("Position"); - ecs::ComponentID::regist("Velocity"); - ecs::ComponentID::regist("Meta"); + ecs::ComponentID::add("Position"); + ecs::ComponentID::add("Velocity"); + ecs::ComponentID::add("Meta"); sol::state lua(sol::c_call); lua.open_libraries(sol::lib::base, sol::lib::package); @@ -83,19 +90,12 @@ int main(int argc, const char** argv) { std::cout << "Y: " << pos->y << '\n'; }); - // These are really just an internal api that should not be used - for (auto [uuid, entity] : manager.view(ecs::ComponentID::resolve("Random"))) { - sol::table random = ((ecs::lua::Wrapper*)entity->get_component(ecs::ComponentID::resolve("Random")))->table; - - random["a"] = 21; - std::cout << random["a"].get() << '\n'; - }; - - for (auto [uuid, entity] : manager.view(ecs::ComponentID::resolve("Random"))) { - sol::table random = ((ecs::lua::Wrapper*)entity->get_component(ecs::ComponentID::resolve("Random")))->table; - - std::cout << random["a"].get() << '\n'; - }; + std::cout << "Adding Orb!\n"; + manager.create_entity(1.2f, 3.4f); + for (auto [ent, pos, vel] : manager.view()) { + std::cout << "X: " << pos->x << " + " << vel->x << '\n'; + std::cout << "Y: " << pos->y << " + " << vel->y << '\n'; + } } // Test serialization @@ -122,7 +122,7 @@ int main(int argc, const char** argv) { ecs::serial::serialize_ids(file); io::write(file, manager.view<>().size()); - for (auto [uuid, entity] : manager.view<>()) { + for (auto [entity] : manager.view<>()) { ecs::serial::serialize(file, entity); } file.close(); diff --git a/test/test.lua b/test/test.lua index ad636ee..0b76234 100644 --- a/test/test.lua +++ b/test/test.lua @@ -2,17 +2,17 @@ local ecs = require "ecs" local Position = require "ecs.Position" local Velocity = require "ecs.Velocity" local Meta = require "ecs.Meta" -local Wrapper = require 'ecs.Wrapper' +local LuaComponent = require 'ecs.LuaComponent' function init() - print("Registering components") + print("Creating LuaComponents") - LuaData = ecs.register("LuaData", { + LuaData = LuaComponent.create("LuaData", { speed = "number", something = "string", alive = "boolean" }) - TestThing = ecs.register("TestThing", { + TestThing = LuaComponent.create("TestThing", { test = "string" }) end @@ -43,18 +43,18 @@ function run() -- -- ent:add_component(ecs.Wrapper.new("Stats", stats)) - print(ent:has_components("Position", "Velocity")) - pos = ent:get_component("Position") + print(ent:has_components(Position, Velocity)) + pos = ent:get_component(Position) print("x: " .. pos.x) print("y: " .. pos.y) - vel = ent:get_component("Velocity") + vel = ent:get_component(Velocity) print("v_x: " .. vel.x) print("v_y: " .. vel.y) print("View test") - manager:view("Position", "Velocity"):for_each(function(ent) - pos = ent:get_component("Position") - vel = ent:get_component("Velocity") + manager:view(Position, Velocity):for_each(function(ent) + pos = ent:get_component(Position) + vel = ent:get_component(Velocity) pos.x = pos.x + vel.x pos.y = pos.y + vel.y @@ -67,8 +67,8 @@ function run() print("v_x: " .. vel.x) print("v_y: " .. vel.y) - if ent:has_components("Meta") then - meta = ent:get_component("Meta") + if ent:has_components(Meta) then + meta = ent:get_component(Meta) print("name: " .. meta.name) end end) @@ -78,8 +78,8 @@ function run() -- print "TEST" -- end - manager:view("LuaData"):for_each(function(ent) - d = ent:get_component("LuaData") + manager:view(LuaData):for_each(function(ent) + d = ent:get_component(LuaData) print("speed: " .. d.speed) d.speed = 11 @@ -92,8 +92,8 @@ function run() print("Something: " .. d.something) end) - manager:view("TestThing"):for_each(function(ent) - t = ent:get_component("TestThing") + manager:view(TestThing):for_each(function(ent) + t = ent:get_component(TestThing) print("test: " .. t.test) end) diff --git a/test/test2.lua b/test/test2.lua index aeb1df4..bbd471d 100644 --- a/test/test2.lua +++ b/test/test2.lua @@ -1,17 +1,18 @@ local ecs = require "ecs" -require "ecs.Wrapper" +local LuaComponent = require "ecs.LuaComponent" function init() print("Registering components") - LuaData = ecs.register("LuaData", { + -- Swapped compared to test.lua + TestThing = LuaComponent.create("TestThing", { + test = "string" + }) + LuaData = LuaComponent.create("LuaData", { speed = "number", something = "string", alive = "boolean" }) - TestThing = ecs.register("TestThing", { - test = "string" - }) end function run() @@ -19,17 +20,15 @@ function run() manager = get_manager() - print("AAA") - - manager:view("TestThing"):for_each(function(ent) - data = ent:get_component("TestThing") + manager:view(TestThing):for_each(function(ent) + data = ent:get_component(TestThing) print("test: " .. data.test) end) - manager:view("LuaData"):for_each(function(ent) + 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") + data = ent:get_component(LuaData) print("speed: " .. data.speed) print("something: " .. data.something) -- @todo You cannot concatenate bool to string