Add ecs::container::finally mechanism
This commit is contained in:
parent
0034e6a9f1
commit
083e5841aa
2 changed files with 104 additions and 0 deletions
|
|
@ -385,6 +385,30 @@ namespace psemek::ecs
|
||||||
template <typename ... Components, typename Function>
|
template <typename ... Components, typename Function>
|
||||||
void watch(Function && function);
|
void watch(Function && function);
|
||||||
|
|
||||||
|
/** Call a callback after exiting all currently executing ECS container methods.
|
||||||
|
*
|
||||||
|
* If no ECS container method is currently executed, call the callback immediately instead.
|
||||||
|
*
|
||||||
|
* The function must have one of the following signatures:
|
||||||
|
* void()
|
||||||
|
* void(container)
|
||||||
|
*
|
||||||
|
* This method is meant to be called from inside other callbacks (typically
|
||||||
|
* constructors and destructors) and can be used to add extra modifications
|
||||||
|
* to entities that don't clash with other callbacks and archetype changes.
|
||||||
|
*
|
||||||
|
* Generally, if a constructor, destructor, apply or batch apply function
|
||||||
|
* changes the archetype of some entities, it should do so using `finally`
|
||||||
|
* instead of doing that directly.
|
||||||
|
*
|
||||||
|
* @param function A callback to be called before the topmost currently executing
|
||||||
|
* ECS container method returns.
|
||||||
|
* @warning If a callback adds new callbacks via `finally`, those will also get executed,
|
||||||
|
* which potentially leads to an infinite loop.
|
||||||
|
*/
|
||||||
|
template <typename Function>
|
||||||
|
void finally(Function && function);
|
||||||
|
|
||||||
template <typename Index, typename ... Args>
|
template <typename Index, typename ... Args>
|
||||||
Index & index(Args && ... args);
|
Index & index(Args && ... args);
|
||||||
|
|
||||||
|
|
@ -412,10 +436,14 @@ namespace psemek::ecs
|
||||||
util::object_pool<std::vector<util::uuid>> uuid_list_pool_;
|
util::object_pool<std::vector<util::uuid>> uuid_list_pool_;
|
||||||
util::object_pool<util::hash_set<util::uuid>> uuid_set_pool_;
|
util::object_pool<util::hash_set<util::uuid>> uuid_set_pool_;
|
||||||
|
|
||||||
|
std::size_t method_recursion_depth_ = 0;
|
||||||
|
std::vector<util::function<void(container &)>> finally_callbacks_;
|
||||||
|
|
||||||
detail::table * insert_table(std::vector<std::unique_ptr<detail::column>> columns);
|
detail::table * insert_table(std::vector<std::unique_ptr<detail::column>> columns);
|
||||||
void do_destroy(handle entity);
|
void do_destroy(handle entity);
|
||||||
void remove_row(detail::table & table, std::uint32_t row, util::span<detail::entity_data> entities);
|
void remove_row(detail::table & table, std::uint32_t row, util::span<detail::entity_data> entities);
|
||||||
void finalize_iteration(detail::table & table);
|
void finalize_iteration(detail::table & table);
|
||||||
|
void finalize_method();
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Component>
|
template <typename Component>
|
||||||
|
|
@ -429,6 +457,8 @@ namespace psemek::ecs
|
||||||
{
|
{
|
||||||
static_assert(detail::all_different_types_v<std::remove_cvref_t<Components>...>, "all component types must be different");
|
static_assert(detail::all_different_types_v<std::remove_cvref_t<Components>...>, "all component types must be different");
|
||||||
|
|
||||||
|
++method_recursion_depth_;
|
||||||
|
|
||||||
(register_component<std::remove_cvref_t<Components>>(), ...);
|
(register_component<std::remove_cvref_t<Components>>(), ...);
|
||||||
|
|
||||||
detail::component_uuid_helper<std::remove_cvref_t<Components>...> uuids;
|
detail::component_uuid_helper<std::remove_cvref_t<Components>...> uuids;
|
||||||
|
|
@ -456,6 +486,10 @@ namespace psemek::ecs
|
||||||
|
|
||||||
table->trigger_constructors(*this, row);
|
table->trigger_constructors(*this, row);
|
||||||
|
|
||||||
|
finalize_method();
|
||||||
|
|
||||||
|
--method_recursion_depth_;
|
||||||
|
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -464,6 +498,8 @@ namespace psemek::ecs
|
||||||
{
|
{
|
||||||
static_assert(detail::all_different_types_v<std::remove_cvref_t<Components>...>, "all component types must be different");
|
static_assert(detail::all_different_types_v<std::remove_cvref_t<Components>...>, "all component types must be different");
|
||||||
|
|
||||||
|
++method_recursion_depth_;
|
||||||
|
|
||||||
(register_component<Components>(), ...);
|
(register_component<Components>(), ...);
|
||||||
|
|
||||||
auto uuids = uuid_list_pool_.get();
|
auto uuids = uuid_list_pool_.get();
|
||||||
|
|
@ -518,6 +554,10 @@ namespace psemek::ecs
|
||||||
|
|
||||||
uuid_set_pool_.put(std::move(attached_uuid_set));
|
uuid_set_pool_.put(std::move(attached_uuid_set));
|
||||||
uuid_list_pool_.put(std::move(uuids));
|
uuid_list_pool_.put(std::move(uuids));
|
||||||
|
|
||||||
|
finalize_method();
|
||||||
|
|
||||||
|
--method_recursion_depth_;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ... Components>
|
template <typename ... Components>
|
||||||
|
|
@ -525,6 +565,8 @@ namespace psemek::ecs
|
||||||
{
|
{
|
||||||
static_assert(detail::all_different_types_v<std::remove_const_t<Components>...>, "all component types must be different");
|
static_assert(detail::all_different_types_v<std::remove_const_t<Components>...>, "all component types must be different");
|
||||||
|
|
||||||
|
++method_recursion_depth_;
|
||||||
|
|
||||||
auto detached_uuid_set = uuid_set_pool_.get();
|
auto detached_uuid_set = uuid_set_pool_.get();
|
||||||
(detached_uuid_set.insert(std::remove_const_t<Components>::uuid()), ...);
|
(detached_uuid_set.insert(std::remove_const_t<Components>::uuid()), ...);
|
||||||
|
|
||||||
|
|
@ -579,6 +621,10 @@ namespace psemek::ecs
|
||||||
|
|
||||||
uuid_list_pool_.put(std::move(uuids));
|
uuid_list_pool_.put(std::move(uuids));
|
||||||
uuid_set_pool_.put(std::move(detached_uuid_set));
|
uuid_set_pool_.put(std::move(detached_uuid_set));
|
||||||
|
|
||||||
|
finalize_method();
|
||||||
|
|
||||||
|
--method_recursion_depth_;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ... Components>
|
template <typename ... Components>
|
||||||
|
|
@ -595,6 +641,8 @@ namespace psemek::ecs
|
||||||
{
|
{
|
||||||
static_assert(detail::all_different_types_v<std::remove_const_t<Components>...>, "all component types must be different");
|
static_assert(detail::all_different_types_v<std::remove_const_t<Components>...>, "all component types must be different");
|
||||||
|
|
||||||
|
++method_recursion_depth_;
|
||||||
|
|
||||||
using invocable_type = typename detail::filter_with<detail::invocable, std::tuple<Components...>, Function>::type;
|
using invocable_type = typename detail::filter_with<detail::invocable, std::tuple<Components...>, Function>::type;
|
||||||
|
|
||||||
static_assert(invocable_type::value, "function is not invocable with these components");
|
static_assert(invocable_type::value, "function is not invocable with these components");
|
||||||
|
|
@ -622,6 +670,10 @@ namespace psemek::ecs
|
||||||
finalize_iteration(*entry.table);
|
finalize_iteration(*entry.table);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
finalize_method();
|
||||||
|
|
||||||
|
--method_recursion_depth_;
|
||||||
|
|
||||||
return cache;
|
return cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -630,6 +682,8 @@ namespace psemek::ecs
|
||||||
{
|
{
|
||||||
static_assert(detail::all_different_types_v<std::remove_const_t<Components>...>, "all component types must be different");
|
static_assert(detail::all_different_types_v<std::remove_const_t<Components>...>, "all component types must be different");
|
||||||
|
|
||||||
|
++method_recursion_depth_;
|
||||||
|
|
||||||
using invocable_type = typename detail::filter_with<detail::batch_invocable, std::tuple<Components...>, Function>::type;
|
using invocable_type = typename detail::filter_with<detail::batch_invocable, std::tuple<Components...>, Function>::type;
|
||||||
|
|
||||||
static_assert(invocable_type::value, "function is not batch-invocable with these components");
|
static_assert(invocable_type::value, "function is not batch-invocable with these components");
|
||||||
|
|
@ -654,6 +708,10 @@ namespace psemek::ecs
|
||||||
finalize_iteration(*entry.table);
|
finalize_iteration(*entry.table);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
finalize_method();
|
||||||
|
|
||||||
|
--method_recursion_depth_;
|
||||||
|
|
||||||
return cache;
|
return cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -732,6 +790,25 @@ namespace psemek::ecs
|
||||||
entry.table->add_destructor({id, destructor_factory(entry.columns_indices)});
|
entry.table->add_destructor({id, destructor_factory(entry.columns_indices)});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Function>
|
||||||
|
void container::finally(Function && function)
|
||||||
|
{
|
||||||
|
util::function<void(container &)> wrapper;
|
||||||
|
if constexpr (std::is_invocable_v<Function, container &>)
|
||||||
|
{
|
||||||
|
wrapper = std::move(function);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wrapper = [function = std::move(function)](ecs::container &){ function(); };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method_recursion_depth_ == 0)
|
||||||
|
wrapper(*this);
|
||||||
|
else
|
||||||
|
finally_callbacks_.push_back(std::move(wrapper));
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Index, typename ... Args>
|
template <typename Index, typename ... Args>
|
||||||
Index & container::index(Args && ... args)
|
Index & container::index(Args && ... args)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,17 @@ namespace psemek::ecs
|
||||||
{
|
{
|
||||||
assert(alive(entity));
|
assert(alive(entity));
|
||||||
|
|
||||||
|
++method_recursion_depth_;
|
||||||
|
|
||||||
auto const data = entity_list_.get_entities()[entity.id];
|
auto const data = entity_list_.get_entities()[entity.id];
|
||||||
data.table->trigger_destructors(*this, data.row);
|
data.table->trigger_destructors(*this, data.row);
|
||||||
|
|
||||||
do_destroy(entity);
|
do_destroy(entity);
|
||||||
entity_list_.destroy(entity.id);
|
entity_list_.destroy(entity.id);
|
||||||
|
|
||||||
|
finalize_method();
|
||||||
|
|
||||||
|
--method_recursion_depth_;
|
||||||
}
|
}
|
||||||
|
|
||||||
accessor container::get(handle entity)
|
accessor container::get(handle entity)
|
||||||
|
|
@ -52,6 +58,8 @@ namespace psemek::ecs
|
||||||
|
|
||||||
std::optional<handle> container::try_clone(handle entity)
|
std::optional<handle> container::try_clone(handle entity)
|
||||||
{
|
{
|
||||||
|
++method_recursion_depth_;
|
||||||
|
|
||||||
auto const data = entity_list_.get_entities()[entity.id];
|
auto const data = entity_list_.get_entities()[entity.id];
|
||||||
if (!data.table->non_copyable_components().empty())
|
if (!data.table->non_copyable_components().empty())
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
@ -68,6 +76,10 @@ namespace psemek::ecs
|
||||||
|
|
||||||
table->trigger_constructors(*this, row);
|
table->trigger_constructors(*this, row);
|
||||||
|
|
||||||
|
finalize_method();
|
||||||
|
|
||||||
|
--method_recursion_depth_;
|
||||||
|
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -125,6 +137,21 @@ namespace psemek::ecs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void container::finalize_method()
|
||||||
|
{
|
||||||
|
if (method_recursion_depth_ > 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
while (!finally_callbacks_.empty())
|
||||||
|
{
|
||||||
|
auto callbacks = std::move(finally_callbacks_);
|
||||||
|
finally_callbacks_.clear();
|
||||||
|
|
||||||
|
for (auto & callback : callbacks)
|
||||||
|
callback(*this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::size_t container::entity_count()
|
std::size_t container::entity_count()
|
||||||
{
|
{
|
||||||
return entity_list_.size();
|
return entity_list_.size();
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue