ECS constructors wip
This commit is contained in:
parent
0b1522722c
commit
028b4e1296
10 changed files with 192 additions and 30 deletions
|
|
@ -19,7 +19,6 @@ namespace psemek::ecs
|
||||||
{
|
{
|
||||||
|
|
||||||
using query_cache = std::shared_ptr<detail::query_cache>;
|
using query_cache = std::shared_ptr<detail::query_cache>;
|
||||||
using token = std::shared_ptr<void>;
|
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// - Fully document which functions can be called from which callbacks
|
// - Fully document which functions can be called from which callbacks
|
||||||
|
|
@ -283,12 +282,11 @@ namespace psemek::ecs
|
||||||
* When attaching components to an entity, the constructor is called
|
* When attaching components to an entity, the constructor is called
|
||||||
* exactly when the entity didn't match the constructor's component types
|
* exactly when the entity didn't match the constructor's component types
|
||||||
* before attaching new components, and does match them after attaching.
|
* before attaching new components, and does match them after attaching.
|
||||||
|
* TODO: implement this behavior
|
||||||
*
|
*
|
||||||
* The constructor function must have the same signature as a function
|
* The constructor function must have the same signature as a function
|
||||||
* passed to the `apply<Components...>()` call.
|
* passed to the `apply<Components...>()` call.
|
||||||
*
|
*
|
||||||
* The constructor can immediately destroy the created entity.
|
|
||||||
*
|
|
||||||
* The constuctor is not considered to be a modification of the entity, i.e. it
|
* The constuctor is not considered to be a modification of the entity, i.e. it
|
||||||
* doesn't trigger modification callbacks.
|
* doesn't trigger modification callbacks.
|
||||||
*
|
*
|
||||||
|
|
@ -298,13 +296,11 @@ namespace psemek::ecs
|
||||||
* @warning If any two of the passed component types are equal, the call fails with
|
* @warning If any two of the passed component types are equal, the call fails with
|
||||||
* a compilation error
|
* a compilation error
|
||||||
* @warning If the constructor modifies the entity's archetype (i.e. attaches or
|
* @warning If the constructor modifies the entity's archetype (i.e. attaches or
|
||||||
* detaches components), it might be called recursively, leading to
|
* detaches components), the behavior is undefined
|
||||||
* infinite recursion. It is best not to change the archetype from the
|
* @warning If the constructor destroys the entity, the behavior is undefined
|
||||||
* constructor.
|
|
||||||
*/
|
*/
|
||||||
// TODO: implement
|
|
||||||
template <typename ... Components, typename Function>
|
template <typename ... Components, typename Function>
|
||||||
token constructor(Function && function);
|
void constructor(Function && function);
|
||||||
|
|
||||||
/** Register a destructor. Each time an entity is destroyed that has
|
/** Register a destructor. Each time an entity is destroyed that has
|
||||||
* the specified set of components, the destructor is called for
|
* the specified set of components, the destructor is called for
|
||||||
|
|
@ -322,6 +318,7 @@ namespace psemek::ecs
|
||||||
* When detaching components from an entity, the destructor is called
|
* When detaching components from an entity, the destructor is called
|
||||||
* exactly when the entity did match the destructor's component types
|
* exactly when the entity did match the destructor's component types
|
||||||
* before detaching components, and doesn't match them after detaching.
|
* before detaching components, and doesn't match them after detaching.
|
||||||
|
* TODO: implement this behavior
|
||||||
*
|
*
|
||||||
* The destructor function must have the same signature as a function
|
* The destructor function must have the same signature as a function
|
||||||
* passed to the `apply<Components...>()` call.
|
* passed to the `apply<Components...>()` call.
|
||||||
|
|
@ -337,10 +334,11 @@ namespace psemek::ecs
|
||||||
* a compilation error
|
* a compilation error
|
||||||
* @warning If the destructor modifies the entity's archetype (i.e. attaches or
|
* @warning If the destructor modifies the entity's archetype (i.e. attaches or
|
||||||
* detaches components), the behavior is undefined
|
* detaches components), the behavior is undefined
|
||||||
|
* @warning If the destructor destroys the entity recursively, the behavior is undefined
|
||||||
*/
|
*/
|
||||||
// TODO: implement
|
// TODO: implement
|
||||||
template <typename ... Components, typename Function>
|
template <typename ... Components, typename Function>
|
||||||
token destructor(Function && function);
|
void destructor(Function && function);
|
||||||
|
|
||||||
/** Register a component modification callback. Each time an entity that has
|
/** Register a component modification callback. Each time an entity that has
|
||||||
* the specified set of components is modified, and the modification affects
|
* the specified set of components is modified, and the modification affects
|
||||||
|
|
@ -376,7 +374,7 @@ namespace psemek::ecs
|
||||||
*/
|
*/
|
||||||
// TODO: implement
|
// TODO: implement
|
||||||
template <typename ... Components, typename Function>
|
template <typename ... Components, typename Function>
|
||||||
token watch(Function && function);
|
void watch(Function && function);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
detail::entity_list entity_list_;
|
detail::entity_list entity_list_;
|
||||||
|
|
@ -387,6 +385,8 @@ namespace psemek::ecs
|
||||||
std::vector<util::uuid> uuid_helper_;
|
std::vector<util::uuid> uuid_helper_;
|
||||||
util::hash_set<util::uuid> uuid_set_helper_;
|
util::hash_set<util::uuid> uuid_set_helper_;
|
||||||
|
|
||||||
|
std::vector<query_cache> callback_caches_;
|
||||||
|
|
||||||
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 const & entity);
|
void do_destroy(handle const & 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);
|
||||||
|
|
@ -420,13 +420,16 @@ namespace psemek::ecs
|
||||||
if (table->get_iteration_data())
|
if (table->get_iteration_data())
|
||||||
table = table->get_delayed_table();
|
table = table->get_delayed_table();
|
||||||
|
|
||||||
auto id = entity_list_.create(table, table->row_count());
|
auto row = table->row_count();
|
||||||
|
auto id = entity_list_.create(table, row);
|
||||||
handle handle{id, entity_list_.get_entities()[id].epoch};
|
handle handle{id, entity_list_.get_entities()[id].epoch};
|
||||||
[[maybe_unused]] accessor accessor = get(handle);
|
[[maybe_unused]] accessor accessor = get(handle);
|
||||||
|
|
||||||
table->push_row(handle);
|
table->push_row(handle);
|
||||||
((accessor.get<std::remove_cvref_t<Components>>() = std::forward<Components>(components)), ...);
|
((accessor.get<std::remove_cvref_t<Components>>() = std::forward<Components>(components)), ...);
|
||||||
|
|
||||||
|
table->trigger_constructors(*this, row);
|
||||||
|
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -477,9 +480,9 @@ namespace psemek::ecs
|
||||||
template <typename ... Components>
|
template <typename ... Components>
|
||||||
void container::detach(handle const & entity)
|
void container::detach(handle const & entity)
|
||||||
{
|
{
|
||||||
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_const_t<Components>...>, "all component types must be different");
|
||||||
|
|
||||||
(uuid_set_helper_.insert(std::remove_cvref_t<Components>::uuid()), ...);
|
(uuid_set_helper_.insert(std::remove_const_t<Components>::uuid()), ...);
|
||||||
|
|
||||||
auto & data = entity_list_.get_entities()[entity.id];
|
auto & data = entity_list_.get_entities()[entity.id];
|
||||||
for (auto const & column : data.table->columns())
|
for (auto const & column : data.table->columns())
|
||||||
|
|
@ -534,7 +537,7 @@ 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");
|
||||||
|
|
||||||
using invocable_type = typename detail::filter_with<detail::invocable, std::tuple<std::remove_cvref_t<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");
|
||||||
|
|
||||||
|
|
@ -582,7 +585,7 @@ 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");
|
||||||
|
|
||||||
using invocable_type = typename detail::filter_with<detail::batch_invocable, std::tuple<std::remove_cvref_t<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");
|
||||||
|
|
||||||
|
|
@ -602,4 +605,30 @@ namespace psemek::ecs
|
||||||
return cache;
|
return cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename ... Components, typename Function>
|
||||||
|
void container::constructor(Function && function)
|
||||||
|
{
|
||||||
|
static_assert(detail::all_different_types_v<std::remove_const_t<Components>...>, "all component types must be different");
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
query_cache cache = this->cache<Components...>();
|
||||||
|
|
||||||
|
cache->constructor_factory = [function = std::move(function)](std::vector<std::uint32_t> const & column_indices) -> detail::table_callback {
|
||||||
|
return [function, column_indices](container & container, detail::table & table, std::uint32_t row){
|
||||||
|
typename detail::filter_with<detail::static_apply_helper, std::tuple<Components ...>>::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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,8 @@ namespace psemek::ecs::detail
|
||||||
template <typename ... Components>
|
template <typename ... Components>
|
||||||
struct static_apply_helper
|
struct static_apply_helper
|
||||||
{
|
{
|
||||||
|
static constexpr std::size_t column_count = sizeof...(Components);
|
||||||
|
|
||||||
container & parent;
|
container & parent;
|
||||||
std::size_t row_count;
|
std::size_t row_count;
|
||||||
handle const * entity_handles_pointer;
|
handle const * entity_handles_pointer;
|
||||||
|
|
@ -132,6 +134,14 @@ namespace psemek::ecs::detail
|
||||||
std::size_t i = 0;
|
std::size_t i = 0;
|
||||||
((pointers[i++] += stride<Components>()), ...);
|
((pointers[i++] += stride<Components>()), ...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void advance(std::size_t rows)
|
||||||
|
{
|
||||||
|
entity_handles_pointer += rows;
|
||||||
|
|
||||||
|
std::size_t i = 0;
|
||||||
|
((pointers[i++] += stride<Components>() * rows), ...);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
22
libs/ecs/include/psemek/ecs/detail/callback.hpp
Normal file
22
libs/ecs/include/psemek/ecs/detail/callback.hpp
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <psemek/ecs/handle.hpp>
|
||||||
|
#include <psemek/util/function.hpp>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace psemek::ecs
|
||||||
|
{
|
||||||
|
|
||||||
|
struct container;
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
struct table;
|
||||||
|
|
||||||
|
using table_callback = util::function<void(container &, table &, std::uint32_t)>;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <psemek/ecs/detail/callback.hpp>
|
||||||
#include <psemek/util/uuid.hpp>
|
#include <psemek/util/uuid.hpp>
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
@ -13,7 +14,7 @@ namespace psemek::ecs::detail
|
||||||
struct query_cache_entry
|
struct query_cache_entry
|
||||||
{
|
{
|
||||||
struct table * table = nullptr;
|
struct table * table = nullptr;
|
||||||
std::vector<column *> columns;
|
std::vector<std::uint32_t> columns_indices;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct query_cache
|
struct query_cache
|
||||||
|
|
@ -22,6 +23,8 @@ namespace psemek::ecs::detail
|
||||||
std::vector<util::uuid> without_uuids;
|
std::vector<util::uuid> without_uuids;
|
||||||
std::vector<query_cache_entry> entries;
|
std::vector<query_cache_entry> entries;
|
||||||
|
|
||||||
|
util::function<table_callback(std::vector<std::uint32_t> const &)> constructor_factory;
|
||||||
|
|
||||||
void add(table * table);
|
void add(table * table);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#include <psemek/ecs/handle.hpp>
|
#include <psemek/ecs/handle.hpp>
|
||||||
#include <psemek/ecs/detail/component_hash.hpp>
|
#include <psemek/ecs/detail/component_hash.hpp>
|
||||||
#include <psemek/ecs/detail/column.hpp>
|
#include <psemek/ecs/detail/column.hpp>
|
||||||
|
#include <psemek/ecs/detail/callback.hpp>
|
||||||
#include <psemek/util/uuid.hpp>
|
#include <psemek/util/uuid.hpp>
|
||||||
#include <psemek/util/span.hpp>
|
#include <psemek/util/span.hpp>
|
||||||
#include <psemek/util/hash_table.hpp>
|
#include <psemek/util/hash_table.hpp>
|
||||||
|
|
@ -26,7 +27,8 @@ namespace psemek::ecs::detail
|
||||||
return hash_;
|
return hash_;
|
||||||
}
|
}
|
||||||
|
|
||||||
detail::column * column(util::uuid const & uuid) const;
|
std::optional<std::size_t> column_index(util::uuid const & uuid) const;
|
||||||
|
struct column * column(util::uuid const & uuid) const;
|
||||||
|
|
||||||
util::span<handle const> entity_handles() const
|
util::span<handle const> entity_handles() const
|
||||||
{
|
{
|
||||||
|
|
@ -38,11 +40,6 @@ namespace psemek::ecs::detail
|
||||||
return columns_;
|
return columns_;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t column_count() const
|
|
||||||
{
|
|
||||||
return component_uuid_to_column_.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t row_count() const
|
std::size_t row_count() const
|
||||||
{
|
{
|
||||||
return entity_handles_.size();
|
return entity_handles_.size();
|
||||||
|
|
@ -75,10 +72,14 @@ namespace psemek::ecs::detail
|
||||||
|
|
||||||
std::vector<std::type_index> const & non_copyable_components() const;
|
std::vector<std::type_index> const & non_copyable_components() const;
|
||||||
|
|
||||||
|
void add_constructor(table_callback callback);
|
||||||
|
|
||||||
|
void trigger_constructors(container & container, std::uint32_t row);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::size_t hash_;
|
std::size_t hash_;
|
||||||
std::vector<std::unique_ptr<detail::column>> columns_;
|
std::vector<std::unique_ptr<detail::column>> columns_;
|
||||||
util::hash_map<util::uuid, detail::column *> component_uuid_to_column_;
|
util::hash_map<util::uuid, std::uint32_t> component_uuid_to_column_index_;
|
||||||
|
|
||||||
std::vector<handle> entity_handles_;
|
std::vector<handle> entity_handles_;
|
||||||
|
|
||||||
|
|
@ -87,6 +88,8 @@ namespace psemek::ecs::detail
|
||||||
std::unique_ptr<table> delayed_table_;
|
std::unique_ptr<table> delayed_table_;
|
||||||
|
|
||||||
std::vector<std::type_index> non_copyable_components_;
|
std::vector<std::type_index> non_copyable_components_;
|
||||||
|
|
||||||
|
std::shared_ptr<std::vector<table_callback>> constructors_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ namespace psemek::ecs::detail
|
||||||
{
|
{
|
||||||
bool operator()(util::span<util::uuid const> const & uuids, std::unique_ptr<table> const & table) const
|
bool operator()(util::span<util::uuid const> const & uuids, std::unique_ptr<table> const & table) const
|
||||||
{
|
{
|
||||||
if (uuids.size() != table->column_count())
|
if (uuids.size() != table->columns().size())
|
||||||
return false;
|
return false;
|
||||||
for (auto const & uuid : uuids)
|
for (auto const & uuid : uuids)
|
||||||
if (!table->column(uuid))
|
if (!table->column(uuid))
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,12 @@ namespace psemek::ecs::detail
|
||||||
using type = typename filter_with_impl<MetaFunction, std::tuple<ExtraArgs...>, std::tuple<FilteredComponents...>, RemainingComponents...>::type;
|
using type = typename filter_with_impl<MetaFunction, std::tuple<ExtraArgs...>, std::tuple<FilteredComponents...>, RemainingComponents...>::type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <template <typename ...> typename MetaFunction, typename ... ExtraArgs, typename ... FilteredComponents, typename Component, typename ... RemainingComponents>
|
||||||
|
struct filter_with_impl<MetaFunction, std::tuple<ExtraArgs...>, std::tuple<FilteredComponents...>, ecs::without<Component> const, RemainingComponents...>
|
||||||
|
{
|
||||||
|
using type = typename filter_with_impl<MetaFunction, std::tuple<ExtraArgs...>, std::tuple<FilteredComponents...>, RemainingComponents...>::type;
|
||||||
|
};
|
||||||
|
|
||||||
template <template <typename ...> typename MetaFunction, typename ExtraArgsTuple, typename FilteredComponentsTuple, typename ... Components>
|
template <template <typename ...> typename MetaFunction, typename ExtraArgsTuple, typename FilteredComponentsTuple, typename ... Components>
|
||||||
struct filter_without_impl;
|
struct filter_without_impl;
|
||||||
|
|
||||||
|
|
@ -49,6 +55,12 @@ namespace psemek::ecs::detail
|
||||||
using type = typename filter_without_impl<MetaFunction, std::tuple<ExtraArgs...>, std::tuple<FilteredComponents..., Component>, RemainingComponents...>::type;
|
using type = typename filter_without_impl<MetaFunction, std::tuple<ExtraArgs...>, std::tuple<FilteredComponents..., Component>, RemainingComponents...>::type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <template <typename ...> typename MetaFunction, typename ... ExtraArgs, typename ... FilteredComponents, typename Component, typename ... RemainingComponents>
|
||||||
|
struct filter_without_impl<MetaFunction, std::tuple<ExtraArgs...>, std::tuple<FilteredComponents...>, const ecs::without<Component>, RemainingComponents...>
|
||||||
|
{
|
||||||
|
using type = typename filter_without_impl<MetaFunction, std::tuple<ExtraArgs...>, std::tuple<FilteredComponents..., Component>, RemainingComponents...>::type;
|
||||||
|
};
|
||||||
|
|
||||||
template <template <typename ...> typename MetaFunction, typename ComponentsTuple, typename ... ExtraArgs>
|
template <template <typename ...> typename MetaFunction, typename ComponentsTuple, typename ... ExtraArgs>
|
||||||
struct filter_with;
|
struct filter_with;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,10 @@ namespace psemek::ecs::detail
|
||||||
entry.table = table;
|
entry.table = table;
|
||||||
|
|
||||||
for (auto const & uuid : with_uuids)
|
for (auto const & uuid : with_uuids)
|
||||||
entry.columns.push_back(table->column(uuid));
|
entry.columns_indices.push_back(*table->column_index(uuid));
|
||||||
|
|
||||||
|
if (constructor_factory)
|
||||||
|
table->add_constructor(constructor_factory(entry.columns_indices));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,23 +7,33 @@ namespace psemek::ecs::detail
|
||||||
table::table(std::vector<std::unique_ptr<detail::column>> columns)
|
table::table(std::vector<std::unique_ptr<detail::column>> columns)
|
||||||
{
|
{
|
||||||
component_hasher<true> hasher;
|
component_hasher<true> hasher;
|
||||||
for (auto & column : columns)
|
for (std::size_t i = 0; i < columns.size(); ++i)
|
||||||
{
|
{
|
||||||
|
auto const & column = columns[i];
|
||||||
auto uuid = column->uuid();
|
auto uuid = column->uuid();
|
||||||
hasher(uuid);
|
hasher(uuid);
|
||||||
component_uuid_to_column_.insert({uuid, column.get()});
|
component_uuid_to_column_index_.insert({uuid, i});
|
||||||
if (!column->copy_constructible())
|
if (!column->copy_constructible())
|
||||||
non_copyable_components_.push_back(column->type());
|
non_copyable_components_.push_back(column->type());
|
||||||
}
|
}
|
||||||
hash_ = hasher.result;
|
hash_ = hasher.result;
|
||||||
|
|
||||||
columns_ = std::move(columns);
|
columns_ = std::move(columns);
|
||||||
|
|
||||||
|
constructors_ = std::make_shared<std::vector<table_callback>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
detail::column * table::column(util::uuid const & uuid) const
|
std::optional<std::size_t> table::column_index(util::uuid const & uuid) const
|
||||||
{
|
{
|
||||||
if (auto it = component_uuid_to_column_.find(uuid); it != component_uuid_to_column_.end())
|
if (auto it = component_uuid_to_column_index_.find(uuid); it != component_uuid_to_column_index_.end())
|
||||||
return it->second;
|
return it->second;
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
column * table::column(util::uuid const & uuid) const
|
||||||
|
{
|
||||||
|
if (auto index = column_index(uuid))
|
||||||
|
return columns_[*index].get();
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -95,7 +105,10 @@ namespace psemek::ecs::detail
|
||||||
std::vector<std::unique_ptr<detail::column>> columns;
|
std::vector<std::unique_ptr<detail::column>> columns;
|
||||||
for (auto const & column : columns_)
|
for (auto const & column : columns_)
|
||||||
columns.push_back(column->clone());
|
columns.push_back(column->clone());
|
||||||
return std::make_unique<table>(std::move(columns));
|
|
||||||
|
auto result = std::make_unique<table>(std::move(columns));
|
||||||
|
result->constructors_ = constructors_;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
table * table::get_delayed_table()
|
table * table::get_delayed_table()
|
||||||
|
|
@ -128,4 +141,17 @@ namespace psemek::ecs::detail
|
||||||
return non_copyable_components_;
|
return non_copyable_components_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void table::add_constructor(table_callback callback)
|
||||||
|
{
|
||||||
|
constructors_->push_back(std::move(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
void table::trigger_constructors(container & container, std::uint32_t row)
|
||||||
|
{
|
||||||
|
for (auto const & callback : *constructors_)
|
||||||
|
{
|
||||||
|
callback(container, *this, row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
54
libs/ecs/tests/callback.cpp
Normal file
54
libs/ecs/tests/callback.cpp
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
#include <psemek/test/test.hpp>
|
||||||
|
|
||||||
|
#include <psemek/ecs/container.hpp>
|
||||||
|
#include <psemek/ecs/declare_uuid.hpp>
|
||||||
|
|
||||||
|
using namespace psemek;
|
||||||
|
using namespace psemek::ecs;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
struct component_1
|
||||||
|
{
|
||||||
|
int value;
|
||||||
|
|
||||||
|
psemek_ecs_declare_uuid("component_1")
|
||||||
|
};
|
||||||
|
|
||||||
|
struct component_2
|
||||||
|
{
|
||||||
|
int value;
|
||||||
|
|
||||||
|
psemek_ecs_declare_uuid("component_2")
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
test_case(ecs_callback_constructor_create)
|
||||||
|
{
|
||||||
|
container container;
|
||||||
|
|
||||||
|
int value = 0;
|
||||||
|
container.constructor<component_1>([&value](component_1 const & c1){
|
||||||
|
value = c1.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
container.create(component_1{10});
|
||||||
|
expect_equal(value, 10);
|
||||||
|
|
||||||
|
container.create(component_1{20});
|
||||||
|
expect_equal(value, 20);
|
||||||
|
|
||||||
|
container.create(component_2{100});
|
||||||
|
expect_equal(value, 20);
|
||||||
|
|
||||||
|
container.create();
|
||||||
|
expect_equal(value, 20);
|
||||||
|
|
||||||
|
container.create(component_1{30});
|
||||||
|
expect_equal(value, 30);
|
||||||
|
|
||||||
|
container.create(component_1{40}, component_2{200});
|
||||||
|
expect_equal(value, 40);
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue