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>
|
||||
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>
|
||||
Index & index(Args && ... args);
|
||||
|
||||
|
|
@ -412,10 +436,14 @@ namespace psemek::ecs
|
|||
util::object_pool<std::vector<util::uuid>> uuid_list_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);
|
||||
void do_destroy(handle entity);
|
||||
void remove_row(detail::table & table, std::uint32_t row, util::span<detail::entity_data> entities);
|
||||
void finalize_iteration(detail::table & table);
|
||||
void finalize_method();
|
||||
};
|
||||
|
||||
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");
|
||||
|
||||
++method_recursion_depth_;
|
||||
|
||||
(register_component<std::remove_cvref_t<Components>>(), ...);
|
||||
|
||||
detail::component_uuid_helper<std::remove_cvref_t<Components>...> uuids;
|
||||
|
|
@ -456,6 +486,10 @@ namespace psemek::ecs
|
|||
|
||||
table->trigger_constructors(*this, row);
|
||||
|
||||
finalize_method();
|
||||
|
||||
--method_recursion_depth_;
|
||||
|
||||
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");
|
||||
|
||||
++method_recursion_depth_;
|
||||
|
||||
(register_component<Components>(), ...);
|
||||
|
||||
auto uuids = uuid_list_pool_.get();
|
||||
|
|
@ -518,6 +554,10 @@ namespace psemek::ecs
|
|||
|
||||
uuid_set_pool_.put(std::move(attached_uuid_set));
|
||||
uuid_list_pool_.put(std::move(uuids));
|
||||
|
||||
finalize_method();
|
||||
|
||||
--method_recursion_depth_;
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
++method_recursion_depth_;
|
||||
|
||||
auto detached_uuid_set = uuid_set_pool_.get();
|
||||
(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_set_pool_.put(std::move(detached_uuid_set));
|
||||
|
||||
finalize_method();
|
||||
|
||||
--method_recursion_depth_;
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
++method_recursion_depth_;
|
||||
|
||||
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");
|
||||
|
|
@ -622,6 +670,10 @@ namespace psemek::ecs
|
|||
finalize_iteration(*entry.table);
|
||||
}
|
||||
|
||||
finalize_method();
|
||||
|
||||
--method_recursion_depth_;
|
||||
|
||||
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");
|
||||
|
||||
++method_recursion_depth_;
|
||||
|
||||
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");
|
||||
|
|
@ -654,6 +708,10 @@ namespace psemek::ecs
|
|||
finalize_iteration(*entry.table);
|
||||
}
|
||||
|
||||
finalize_method();
|
||||
|
||||
--method_recursion_depth_;
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
|
|
@ -732,6 +790,25 @@ namespace psemek::ecs
|
|||
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>
|
||||
Index & container::index(Args && ... args)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -15,11 +15,17 @@ namespace psemek::ecs
|
|||
{
|
||||
assert(alive(entity));
|
||||
|
||||
++method_recursion_depth_;
|
||||
|
||||
auto const data = entity_list_.get_entities()[entity.id];
|
||||
data.table->trigger_destructors(*this, data.row);
|
||||
|
||||
do_destroy(entity);
|
||||
entity_list_.destroy(entity.id);
|
||||
|
||||
finalize_method();
|
||||
|
||||
--method_recursion_depth_;
|
||||
}
|
||||
|
||||
accessor container::get(handle entity)
|
||||
|
|
@ -52,6 +58,8 @@ namespace psemek::ecs
|
|||
|
||||
std::optional<handle> container::try_clone(handle entity)
|
||||
{
|
||||
++method_recursion_depth_;
|
||||
|
||||
auto const data = entity_list_.get_entities()[entity.id];
|
||||
if (!data.table->non_copyable_components().empty())
|
||||
return std::nullopt;
|
||||
|
|
@ -68,6 +76,10 @@ namespace psemek::ecs
|
|||
|
||||
table->trigger_constructors(*this, row);
|
||||
|
||||
finalize_method();
|
||||
|
||||
--method_recursion_depth_;
|
||||
|
||||
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()
|
||||
{
|
||||
return entity_list_.size();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue