From a85e72a5b6a6a4bd789bfbafd974dbcf7424ed9e Mon Sep 17 00:00:00 2001 From: lisyarus Date: Mon, 18 Dec 2023 12:45:42 +0300 Subject: [PATCH] ECS destructors wip --- libs/ecs/include/psemek/ecs/container.hpp | 27 +++++++++++++++- .../include/psemek/ecs/detail/query_cache.hpp | 1 + libs/ecs/include/psemek/ecs/detail/table.hpp | 3 ++ libs/ecs/source/container.cpp | 2 ++ libs/ecs/source/detail/query_cache.cpp | 3 ++ libs/ecs/source/detail/table.cpp | 15 +++++++-- libs/ecs/tests/callback.cpp | 31 +++++++++++++++++++ 7 files changed, 79 insertions(+), 3 deletions(-) diff --git a/libs/ecs/include/psemek/ecs/container.hpp b/libs/ecs/include/psemek/ecs/container.hpp index 5bce2e31..80a91a70 100644 --- a/libs/ecs/include/psemek/ecs/container.hpp +++ b/libs/ecs/include/psemek/ecs/container.hpp @@ -336,7 +336,6 @@ namespace psemek::ecs * detaches components), the behavior is undefined * @warning If the destructor destroys the entity recursively, the behavior is undefined */ - // TODO: implement template void destructor(Function && function); @@ -631,4 +630,30 @@ namespace psemek::ecs callback_caches_.push_back(cache); } + template + void container::destructor(Function && function) + { + static_assert(detail::all_different_types_v...>, "all component types must be different"); + + using invocable_type = typename detail::filter_with, Function>::type; + + static_assert(invocable_type::value, "function is not invocable with these components"); + + query_cache cache = this->cache(); + + cache->destructor_factory = [function = std::move(function)](std::vector const & column_indices) -> detail::table_callback { + return [function, column_indices](container & container, detail::table & table, std::uint32_t row){ + typename detail::filter_with>::type apply_helper(container, table.entity_handles()); + + for (std::size_t i = 0; i < apply_helper.column_count; ++i) + apply_helper.pointers[i] = table.columns()[column_indices[i]]->data(); + + apply_helper.advance(row); + apply_helper.apply(function); + }; + }; + + callback_caches_.push_back(cache); + } + } diff --git a/libs/ecs/include/psemek/ecs/detail/query_cache.hpp b/libs/ecs/include/psemek/ecs/detail/query_cache.hpp index 8ab00fd9..8ca06753 100644 --- a/libs/ecs/include/psemek/ecs/detail/query_cache.hpp +++ b/libs/ecs/include/psemek/ecs/detail/query_cache.hpp @@ -24,6 +24,7 @@ namespace psemek::ecs::detail std::vector entries; util::function const &)> constructor_factory; + util::function const &)> destructor_factory; void add(table * table); }; diff --git a/libs/ecs/include/psemek/ecs/detail/table.hpp b/libs/ecs/include/psemek/ecs/detail/table.hpp index b205909b..c8fe59d7 100644 --- a/libs/ecs/include/psemek/ecs/detail/table.hpp +++ b/libs/ecs/include/psemek/ecs/detail/table.hpp @@ -73,8 +73,10 @@ namespace psemek::ecs::detail std::vector const & non_copyable_components() const; void add_constructor(table_callback callback); + void add_destructor(table_callback callback); void trigger_constructors(container & container, std::uint32_t row); + void trigger_destructors(container & container, std::uint32_t row); protected: std::size_t hash_; @@ -90,6 +92,7 @@ namespace psemek::ecs::detail std::vector non_copyable_components_; std::shared_ptr> constructors_; + std::shared_ptr> destructors_; }; } diff --git a/libs/ecs/source/container.cpp b/libs/ecs/source/container.cpp index 097329e3..6e74ba5f 100644 --- a/libs/ecs/source/container.cpp +++ b/libs/ecs/source/container.cpp @@ -78,6 +78,8 @@ namespace psemek::ecs auto & data = entities[entity.id]; auto & iteration_data = data.table->get_iteration_data(); + data.table->trigger_destructors(*this, data.row); + if (!iteration_data || iteration_data->current_row < data.row) remove_row(*data.table, data.row, entities); else diff --git a/libs/ecs/source/detail/query_cache.cpp b/libs/ecs/source/detail/query_cache.cpp index 08a4ac19..6c717937 100644 --- a/libs/ecs/source/detail/query_cache.cpp +++ b/libs/ecs/source/detail/query_cache.cpp @@ -14,6 +14,9 @@ namespace psemek::ecs::detail if (constructor_factory) table->add_constructor(constructor_factory(entry.columns_indices)); + + if (destructor_factory) + table->add_destructor(destructor_factory(entry.columns_indices)); } } diff --git a/libs/ecs/source/detail/table.cpp b/libs/ecs/source/detail/table.cpp index 573ccb88..8eb903b0 100644 --- a/libs/ecs/source/detail/table.cpp +++ b/libs/ecs/source/detail/table.cpp @@ -21,6 +21,7 @@ namespace psemek::ecs::detail columns_ = std::move(columns); constructors_ = std::make_shared>(); + destructors_ = std::make_shared>(); } std::optional table::column_index(util::uuid const & uuid) const @@ -108,6 +109,7 @@ namespace psemek::ecs::detail auto result = std::make_unique(std::move(columns)); result->constructors_ = constructors_; + result->destructors_ = destructors_; return result; } @@ -146,12 +148,21 @@ namespace psemek::ecs::detail constructors_->push_back(std::move(callback)); } + void table::add_destructor(table_callback callback) + { + destructors_->push_back(std::move(callback)); + } + void table::trigger_constructors(container & container, std::uint32_t row) { for (auto const & callback : *constructors_) - { callback(container, *this, row); - } + } + + void table::trigger_destructors(container & container, std::uint32_t row) + { + for (auto const & callback : *destructors_) + callback(container, *this, row); } } diff --git a/libs/ecs/tests/callback.cpp b/libs/ecs/tests/callback.cpp index 363fbde2..c7bd67f5 100644 --- a/libs/ecs/tests/callback.cpp +++ b/libs/ecs/tests/callback.cpp @@ -52,3 +52,34 @@ test_case(ecs_callback_constructor_create) container.create(component_1{40}, component_2{200}); expect_equal(value, 40); } + +test_case(ecs_callback_destructor_destroy) +{ + container container; + + int value = 0; + container.destructor([&value](component_1 const & c1){ + value = c1.value; + }); + + auto h1 = container.create(component_1{10}); + auto h2 = container.create(component_1{20}); + auto h3 = container.create(component_2{100}); + auto h4 = container.create(); + auto h5 = container.create(component_1{30}); + auto h6 = container.create(component_1{40}, component_2{200}); + + expect_equal(value, 0); + container.destroy(h1); + expect_equal(value, 10); + container.destroy(h2); + expect_equal(value, 20); + container.destroy(h3); + expect_equal(value, 20); + container.destroy(h4); + expect_equal(value, 20); + container.destroy(h5); + expect_equal(value, 30); + container.destroy(h6); + expect_equal(value, 40); +}