From a6e5deb502cf04d8281cfabc01b92b8e626acb9d Mon Sep 17 00:00:00 2001 From: Dreaded_X Date: Tue, 5 Feb 2019 22:23:38 +0000 Subject: [PATCH] Switched back to using ids instead of names, added uuids to entities and started work on serialization --- .gitmodules | 3 + ecs-lua/src/ecs-lua.cpp | 10 +-- ecs-serial/src/ecs-serial.cpp | 147 ++++++++++++++++++++++++++++++++++ ecs/include/ecs.h | 84 ++++++++++++++----- ecs/src/ecs.cpp | 23 +++--- flint.lua | 13 ++- test/src/main.cpp | 18 +++-- vendor/stduuid | 1 + 8 files changed, 259 insertions(+), 40 deletions(-) create mode 100644 ecs-serial/src/ecs-serial.cpp create mode 160000 vendor/stduuid diff --git a/.gitmodules b/.gitmodules index 902966f..d6f82b9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "vendor/lua"] path = vendor/lua url = https://github.com/lua/lua +[submodule "vendor/stduuid"] + path = vendor/stduuid + url = https://github.com/mariusbancila/stduuid diff --git a/ecs-lua/src/ecs-lua.cpp b/ecs-lua/src/ecs-lua.cpp index 8b44310..01596c7 100644 --- a/ecs-lua/src/ecs-lua.cpp +++ b/ecs-lua/src/ecs-lua.cpp @@ -6,25 +6,25 @@ namespace ecs::lua { ecs.new_usertype("Entity", "add_component", [] (Entity* thiz, std::string name, Component* component) { - return thiz->add_component(name, component); + return thiz->add_component(ComponentID::get_id({name})[0], 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(names); + return thiz->has_components(ComponentID::get_id(names)); }, "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(name)); + return f1(thiz->get_component(ComponentID::get_id({name})[0])); } // If the type of the component unknown we assume it a lua object and convert it to the wrapper auto f2 = lua["_internal_to_LuaWrapper"]; - return f2(thiz->get_component(name)); + return f2(thiz->get_component(ComponentID::get_id({name})[0])); } ); @@ -39,7 +39,7 @@ namespace ecs::lua { for (std::string name : args) { names.push_back(name); } - return thiz->view(names); + return thiz->view(ComponentID::get_id(names)); } ); diff --git a/ecs-serial/src/ecs-serial.cpp b/ecs-serial/src/ecs-serial.cpp new file mode 100644 index 0000000..494d250 --- /dev/null +++ b/ecs-serial/src/ecs-serial.cpp @@ -0,0 +1,147 @@ +#include + +#include "ecs.h" + +#include "iohelper/memstream.h" +#include "iohelper/write.h" +#include "iohelper/read.h" + +#include + +struct Position : ecs::Component { + Position(float x, float y) : _x(x), _y(y) {} + + float _x; + float _y; +}; + +struct Velocity : ecs::Component{ + Velocity(float vx, float vy) : _vx(vx), _vy(vy) {} + + float _vx; + float _vy; +}; + +std::unordered_map(ecs::Component*)>, std::function)>>> _functions; + +// @todo Add deserializer stuff +template +void register_component(std::function(ecs::Component*)> serializer, std::function)> deserializer) { + _functions.insert({ecs::ComponentID::id, {serializer, deserializer}}); +} + +enum class Marker : uint8_t { + ENTITY, + COMPONENT, + END +}; + +int main(int argc, const char* argv) { + ecs::Manager manager; + + register_component([] (ecs::Component* component) { + Position* pos = (Position*)component; + std::vector data; + iohelper::omemstream stream(data); + + iohelper::write(stream, pos->_x); + iohelper::write(stream, pos->_y); + + return data; + }, [] (std::vector data) { + iohelper::imemstream stream(data); + float x = iohelper::read(stream); + float y = iohelper::read(stream); + return new Position(x, y); + }); + + register_component([] (ecs::Component* component) { + Velocity* vel = (Velocity*)component; + std::vector data; + iohelper::omemstream stream(data); + + iohelper::write(stream, vel->_vx); + iohelper::write(stream, vel->_vy); + + return data; + }, [] (std::vector data) { + iohelper::imemstream stream(data); + float vx = iohelper::read(stream); + float vy = iohelper::read(stream); + return new Velocity(vx, vy); + }); + + if (argc == 1) { + // Create entities and store them + for (int i = 0; i < 10; ++i) { + // We can create entities + ecs::Entity* entity = manager.create_entity(); + // Then we can add components to them + entity->add_component(0.0f, 0.0f); + if (i % 2 == 0) { + entity->add_component(0.1f, 0.2f); + } + } + + manager.view().for_each([] (ecs::Entity*, Position* pos, Velocity* vel) { + pos->_x += vel->_vx; + pos->_y += vel->_vy; + }); + + std::ofstream file("entities", std::ios::out | std::ios::trunc); + for (auto [uuid, entity] : manager.view<>()) { + iohelper::write(file, (uint8_t)Marker::ENTITY); + // iohelper::write_length(file, uuid); + auto uuid_data = reinterpret_cast(uuid.as_bytes().data()); + iohelper::write_vector(file, std::vector(uuid_data, uuid_data + 16), false); + for (auto [id, component] : entity->get_components()) { + iohelper::write(file, (uint8_t)Marker::COMPONENT); + iohelper::write_length(file, id); + auto data = _functions[id].first(component); + iohelper::write_vector(file, data); + } + } + iohelper::write(file, (uint8_t)Marker::END); + file.close(); + } else { + // Load entities from disk as a test + std::ifstream file("entities", std::ios::in); + bool loop = true; + ecs::Entity* entity = nullptr; + while (loop) { + Marker marker = (Marker)iohelper::read(file); + switch (marker) { + case Marker::ENTITY: { + // size_t uuid = iohelper::read_length(file); + auto uuid_vector = iohelper::read_vector(file, 16); + entity = manager.create_entity(uuids::uuid(uuid_vector.begin(), uuid_vector.end())); + break; + } + case Marker::COMPONENT: { + size_t id = iohelper::read_length(file); + std::vector data = iohelper::read_vector(file); + entity->add_component(id, _functions[id].second(data)); + break; + } + case Marker::END: + loop = false; + break; + } + } + + if (false) { + auto ent = manager.get_entity(uuids::uuid::from_string("6d58fdb5-6d8c-4e6f-89d4-f7d7b184f463").value()); + if (ent->has_components()) { + auto pos = ent->get_component(); + pos->_x = 1.2f; + pos->_y = 3.4f; + } + } + } + + manager.view().for_each([] (ecs::Entity* entity, Position* pos) { + std::cout << "uuid: " << entity->uuid << '\n'; + std::cout << "x: " << pos->_x << '\n'; + std::cout << "y: " << pos->_y << '\n'; + }); +} diff --git a/ecs/include/ecs.h b/ecs/include/ecs.h index 0c61a2e..d2c7fad 100644 --- a/ecs/include/ecs.h +++ b/ecs/include/ecs.h @@ -10,6 +10,8 @@ #include #endif +#include "uuid.h" + namespace ecs { template std::string get_typename() { @@ -49,6 +51,7 @@ namespace ecs { class Entity { public: + Entity(uuids::uuid _uuid) : uuid(_uuid) {} ~Entity(); template @@ -63,8 +66,8 @@ namespace ecs { } template - bool has_components() { - auto ids = {ComponentID::id...}; + bool has_components() const { + std::vector ids = {ComponentID::id...}; for (const auto& id : ids) { if (_components.find(id) == _components.end()) { return false; @@ -75,7 +78,7 @@ namespace ecs { } template - T* get_component() { + T* get_component() const { size_t id = ComponentID::id; auto it = _components.find(id); if (it == _components.end()) { @@ -85,9 +88,15 @@ namespace ecs { return (T*)it->second; } - void add_component(std::string name, Component* component); - bool has_components(std::vector names); - Component* get_component(std::string name); + const std::unordered_map get_components() const { + return _components; + } + + void add_component(size_t id, Component* component); + bool has_components(std::vector ids); + Component* get_component(size_t id); + + const uuids::uuid uuid; private: std::unordered_map _components; @@ -96,16 +105,45 @@ namespace ecs { template class View { public: - View(std::vector entities) : _entities(entities) {} + View(std::unordered_map entities) : _entities(entities) {} + // @todo What should we do with the uuid void for_each(std::function...)> function) { - for (auto entity : _entities) { + for (auto [uuid, entity] : _entities) { function(entity, entity->template get_component()...); } } + size_t size() const { + return _entities.size(); + } + + auto begin() { + return _entities.begin(); + } + + const auto begin() const { + return _entities.begin(); + } + + const auto cbegin() const { + return _entities.cbegin(); + } + + auto end() { + return _entities.end(); + } + + const auto end() const { + return _entities.end(); + } + + const auto cend() const { + return _entities.cend(); + } + private: - std::vector _entities; + std::unordered_map _entities; }; class Manager { @@ -114,32 +152,42 @@ namespace ecs { template View view() { - std::vector entities; + std::unordered_map entities; + // @todo Fix this so it uses structed bindings, for some reason it does not work for (auto entity : _entities) { - if (entity->has_components()) { - entities.push_back(entity); + if (entity.second->has_components()) { + entities.insert(entity); } } return View(entities); } - View<> view(std::vector names) { - std::vector entities; + View<> view(std::vector ids) { + std::unordered_map entities; - for (auto entity : _entities) { - if (entity->has_components(names)) { - entities.push_back(entity); + for (auto [uuid, entity] : _entities) { + if (entity->has_components(ids)) { + entities.insert({uuid, entity}); } } return View<>(entities); } + Entity* create_entity(uuids::uuid uuid); Entity* create_entity(); + Entity* get_entity(uuids::uuid uuid) { + auto it = _entities.find(uuid); + if (it == _entities.end()) { + throw std::runtime_error("Unable to find entity with uuid!"); + } + return it->second; + } + private: - std::vector _entities; + std::unordered_map _entities; }; } diff --git a/ecs/src/ecs.cpp b/ecs/src/ecs.cpp index 5fb61cc..cee45f3 100644 --- a/ecs/src/ecs.cpp +++ b/ecs/src/ecs.cpp @@ -36,8 +36,7 @@ namespace ecs { _components.clear(); } - void Entity::add_component(std::string name, Component* component) { - size_t id = ComponentID::get_id({name})[0]; + void Entity::add_component(size_t id, Component* component) { if (_components.find(id) != _components.end()) { throw std::runtime_error("Component already exists"); } @@ -45,8 +44,8 @@ namespace ecs { _components[id] = component; } - bool Entity::has_components(std::vector names) { - for (const auto& id : ComponentID::get_id(names)) { + bool Entity::has_components(std::vector ids) { + for (const auto& id : ids) { if (_components.find(id) == _components.end()) { return false; } @@ -55,8 +54,8 @@ namespace ecs { return true; } - Component* Entity::get_component(std::string name) { - auto it = _components.find(ComponentID::get_id({name})[0]); + Component* Entity::get_component(size_t id) { + auto it = _components.find(id); if (it == _components.end()) { throw std::runtime_error("Component does not exist"); } @@ -66,17 +65,21 @@ namespace ecs { Manager::~Manager() { for (auto entity : _entities) { - delete entity; - entity = nullptr; + delete entity.second; + entity.second = nullptr; } _entities.clear(); } Entity* Manager::create_entity() { - Entity* entity = new Entity; + uuids::uuid uuid = uuids::uuid_system_generator{}(); - _entities.push_back(entity); + return create_entity(uuid); + } + Entity* Manager::create_entity(uuids::uuid uuid) { + Entity* entity = new Entity(uuid); + _entities.insert({uuid, entity}); return entity; } } diff --git a/flint.lua b/flint.lua index 89580c8..296f2eb 100644 --- a/flint.lua +++ b/flint.lua @@ -10,16 +10,27 @@ lib "sol2" dependency "lua" +lib "stduuid" + include "vendor/stduuid/include" + link "uuid" + lib "ecs" path "ecs" - dependency "sol2" + dependency("sol2", "stduuid") lib "ecs-lua" path "ecs-lua" dependency "ecs" +subfile("../iohelper/flint.lua", "iohelper") + +executable "ecs-serial" + path "ecs-serial" + + dependency("ecs", "iohelper") + executable "test" path "test" diff --git a/test/src/main.cpp b/test/src/main.cpp index 0178f7b..dce60a9 100644 --- a/test/src/main.cpp +++ b/test/src/main.cpp @@ -79,6 +79,11 @@ int main() { print("v_y: " .. vel.vy) end) + -- @todo Implement this + -- for uuid,entity in pairs(view) do + -- print "TEST" + -- end + manager:view("Random"):for_each(function(ent) wrapped = ent:get_component("Random").object @@ -123,16 +128,17 @@ int main() { std::cout << "Y: " << pos->_y << '\n'; }); - manager.view({"Random"}).for_each([](ecs::Entity* entity) { - sol::table random = ((ecs::lua::LuaWrapper*)entity->get_component("Random"))->_object; + // These are really just an internal api that should not be used + for (auto [uuid, entity] : manager.view(ecs::ComponentID::get_id({"Random"}))) { + sol::table random = ((ecs::lua::LuaWrapper*)entity->get_component(ecs::ComponentID::get_id({"Random"})[0]))->_object; random["a"] = 21; std::cout << random["a"].get() << '\n'; - }); + }; - manager.view({"Random"}).for_each([](ecs::Entity* entity) { - sol::table random = ((ecs::lua::LuaWrapper*)entity->get_component("Random"))->_object; + for (auto [uuid, entity] : manager.view(ecs::ComponentID::get_id({"Random"}))) { + sol::table random = ((ecs::lua::LuaWrapper*)entity->get_component(ecs::ComponentID::get_id({"Random"})[0]))->_object; std::cout << random["a"].get() << '\n'; - }); + }; } diff --git a/vendor/stduuid b/vendor/stduuid new file mode 160000 index 0000000..dee14f6 --- /dev/null +++ b/vendor/stduuid @@ -0,0 +1 @@ +Subproject commit dee14f660da4d2889e60848b206c1d7b8d1fba99