Support ecs::without
This commit is contained in:
parent
08b14ded93
commit
340a5f4254
12 changed files with 260 additions and 57 deletions
|
|
@ -6,7 +6,9 @@
|
|||
#include <psemek/ecs/detail/apply_helper.hpp>
|
||||
#include <psemek/ecs/detail/all_different_types.hpp>
|
||||
#include <psemek/ecs/detail/component_uuid_helper.hpp>
|
||||
#include <psemek/ecs/detail/without.hpp>
|
||||
#include <psemek/ecs/accessor.hpp>
|
||||
#include <psemek/ecs/without.hpp>
|
||||
#include <psemek/util/span.hpp>
|
||||
#include <psemek/util/range.hpp>
|
||||
#include <psemek/util/exception.hpp>
|
||||
|
|
@ -23,8 +25,6 @@ namespace psemek::ecs
|
|||
|
||||
// TODO:
|
||||
// - Fully document which functions can be called from which callbacks
|
||||
// - Const-qualified component access
|
||||
// - Negated component access
|
||||
// - Constructors & destructors implementation
|
||||
// - Modification callbacks implementation
|
||||
// - Refactor query caches
|
||||
|
|
@ -125,6 +125,8 @@ namespace psemek::ecs
|
|||
*
|
||||
* The constness of the component types is ignored.
|
||||
*
|
||||
* The component types can be equal to ecs::without<Component>.
|
||||
*
|
||||
* @tparam Components The component types matching the corresponding `apply()` call
|
||||
* @return A query cache
|
||||
*/
|
||||
|
|
@ -144,6 +146,10 @@ namespace psemek::ecs
|
|||
* that this function doesn't modify specific components, to prevent modification callbacks
|
||||
* from being triggered.
|
||||
*
|
||||
* The component types can be equal to ecs::without<Component>, indicating that entities having
|
||||
* this component type will not be visited by this function. These component types are not
|
||||
* included in the called function signature.
|
||||
*
|
||||
* The function can freely create or destroy entities, and or attach/detach
|
||||
* components to existing entities. It is unspecified whether the function
|
||||
* will or will not visit newly created entities during this `apply()` call.
|
||||
|
|
@ -185,6 +191,10 @@ namespace psemek::ecs
|
|||
* that this function doesn't modify specific components, to prevent modification callbacks
|
||||
* from being triggered.
|
||||
*
|
||||
* The component types can be equal to ecs::without<Component>, indicating that entities having
|
||||
* this component type will not be visited by this function. These component types are not
|
||||
* included in the called function signature.
|
||||
*
|
||||
* The sizes of all spans within a single function call are the same,
|
||||
* except for empty component types (i.e. std::is_empty_v<Component> is true),
|
||||
* each having an unspecified non-zero size. If all components are empty types,
|
||||
|
|
@ -218,6 +228,10 @@ namespace psemek::ecs
|
|||
* The component types can be const-qualified, in which case the corresponding function must
|
||||
* also accept the corresponding components by a const reference.
|
||||
*
|
||||
* The component types can be equal to ecs::without<Component>, indicating that entities having
|
||||
* this component type will not be visited by this constructor. These component types are not
|
||||
* included in the called function signature.
|
||||
*
|
||||
* When attaching components to an entity, the constructor is called
|
||||
* exactly when the entity didn't match the constructor's component types
|
||||
* before attaching new components, and does match them after attaching.
|
||||
|
|
@ -253,6 +267,10 @@ namespace psemek::ecs
|
|||
* The component types can be const-qualified, in which case the corresponding function must
|
||||
* also accept the corresponding components by a const reference.
|
||||
*
|
||||
* The component types can be equal to ecs::without<Component>, indicating that entities having
|
||||
* this component type will not be visited by this destructor. These component types are not
|
||||
* included in the called function signature.
|
||||
*
|
||||
* When detaching components from an entity, the destructor is called
|
||||
* exactly when the entity did match the destructor's component types
|
||||
* before detaching components, and doesn't match them after detaching.
|
||||
|
|
@ -283,6 +301,10 @@ namespace psemek::ecs
|
|||
* The component types can be const-qualified, in which case the corresponding function must
|
||||
* also accept the corresponding components by a const reference.
|
||||
*
|
||||
* The component types can be equal to ecs::without<Component>, indicating that entities having
|
||||
* this component type will not be watched by this callback. These component types are not
|
||||
* included in the called function signature.
|
||||
*
|
||||
* If the modification occurred via an accessor, the callback is called
|
||||
* after the accessor is destroyed, allowing for transaction-like modification.
|
||||
*
|
||||
|
|
@ -436,13 +458,14 @@ namespace psemek::ecs
|
|||
template <typename ... Components>
|
||||
query_cache container::cache()
|
||||
{
|
||||
detail::component_uuid_helper<std::remove_cvref_t<Components>...> uuids;
|
||||
typename detail::filter_with <detail::component_uuid_helper, std::tuple<std::remove_cvref_t<Components>...>>::type with_uuids;
|
||||
typename detail::filter_without<detail::component_uuid_helper, std::tuple<std::remove_cvref_t<Components>...>>::type without_uuids;
|
||||
|
||||
auto result = query_cache_container_.create(uuids.get());
|
||||
auto result = query_cache_container_.create(with_uuids.get(), without_uuids.get());
|
||||
|
||||
table_container_.apply([&](detail::table & table){
|
||||
result->add(&table);
|
||||
}, uuids.get());
|
||||
}, with_uuids.get(), without_uuids.get());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -451,7 +474,10 @@ namespace psemek::ecs
|
|||
query_cache container::apply(Function && function, query_cache cache)
|
||||
{
|
||||
static_assert(detail::all_different_types_v<std::remove_const_t<Components>...>, "all component types must be different");
|
||||
static_assert(detail::invocable<Function, Components...>, "function is not invocable with these components");
|
||||
|
||||
using invocable_type = typename detail::filter_with<detail::invocable, std::tuple<std::remove_cvref_t<Components>...>, Function>::type;
|
||||
|
||||
static_assert(invocable_type::value, "function is not invocable with these components");
|
||||
|
||||
if (!cache)
|
||||
cache = this->cache<Components...>();
|
||||
|
|
@ -460,10 +486,10 @@ namespace psemek::ecs
|
|||
{
|
||||
auto & iteration_data = entry.table->get_iteration_data();
|
||||
iteration_data.emplace();
|
||||
detail::static_apply_helper<Components...> apply_helper(*this, entry.table->entity_handles());
|
||||
typename detail::filter_with<detail::static_apply_helper, std::tuple<Components...>>::type apply_helper(*this, entry.table->entity_handles());
|
||||
|
||||
for (std::size_t i = 0; i < sizeof...(Components); ++i)
|
||||
apply_helper.pointers[i] = entry.table->column(cache->component_uuids[i])->data();
|
||||
for (std::size_t i = 0; i < cache->with_uuids.size(); ++i)
|
||||
apply_helper.pointers[i] = entry.table->column(cache->with_uuids[i])->data();
|
||||
|
||||
for (std::size_t i = 0; i < entry.table->row_count(); ++i)
|
||||
{
|
||||
|
|
@ -496,17 +522,20 @@ namespace psemek::ecs
|
|||
query_cache container::batch_apply(Function && function, query_cache cache)
|
||||
{
|
||||
static_assert(detail::all_different_types_v<std::remove_const_t<Components>...>, "all component types must be different");
|
||||
static_assert(detail::batch_invocable<Function, Components...>, "function is not batch-invocable with these components");
|
||||
|
||||
using invocable_type = typename detail::filter_with<detail::batch_invocable, std::tuple<std::remove_cvref_t<Components>...>, Function>::type;
|
||||
|
||||
static_assert(invocable_type::value, "function is not batch-invocable with these components");
|
||||
|
||||
if (!cache)
|
||||
cache = this->cache<Components...>();
|
||||
|
||||
for (auto const & entry : cache->entries)
|
||||
{
|
||||
detail::static_apply_helper<Components...> apply_helper(*this, entry.table->entity_handles());
|
||||
typename detail::filter_with<detail::static_apply_helper, std::tuple<Components...>>::type apply_helper(*this, entry.table->entity_handles());
|
||||
|
||||
for (std::size_t i = 0; i < sizeof...(Components); ++i)
|
||||
apply_helper.pointers[i] = entry.table->column(cache->component_uuids[i])->data();
|
||||
for (std::size_t i = 0; i < cache->with_uuids.size(); ++i)
|
||||
apply_helper.pointers[i] = entry.table->column(cache->with_uuids[i])->data();
|
||||
|
||||
apply_helper.batch_apply(function);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,12 +16,15 @@ namespace psemek::ecs::detail
|
|||
{
|
||||
|
||||
template <typename Function, typename ... Components>
|
||||
constexpr bool invocable = false
|
||||
|| std::invocable<Function, container &, handle, Components & ...>
|
||||
|| std::invocable<Function, container &, Components & ...>
|
||||
|| std::invocable<Function, handle, Components & ...>
|
||||
|| std::invocable<Function, Components & ...>
|
||||
;
|
||||
struct invocable
|
||||
{
|
||||
static constexpr bool value = false
|
||||
|| std::invocable<Function, container &, handle, Components & ...>
|
||||
|| std::invocable<Function, container &, Components & ...>
|
||||
|| std::invocable<Function, handle, Components & ...>
|
||||
|| std::invocable<Function, Components & ...>
|
||||
;
|
||||
};
|
||||
|
||||
template <typename Function, typename ... Components>
|
||||
void invoke(Function && function, container & parent, handle const & handle, Components & ... components)
|
||||
|
|
@ -45,12 +48,15 @@ namespace psemek::ecs::detail
|
|||
}
|
||||
|
||||
template <typename Function, typename ... Components>
|
||||
constexpr bool batch_invocable = false
|
||||
|| std::invocable<Function, container &, util::span<handle const>, util::span<Components> ...>
|
||||
|| std::invocable<Function, container &, util::span<Components> ...>
|
||||
|| std::invocable<Function, util::span<handle const>, util::span<Components> ...>
|
||||
|| std::invocable<Function, util::span<Components> ...>
|
||||
;
|
||||
struct batch_invocable
|
||||
{
|
||||
static constexpr bool value = false
|
||||
|| std::invocable<Function, container &, util::span<handle const>, util::span<Components> ...>
|
||||
|| std::invocable<Function, container &, util::span<Components> ...>
|
||||
|| std::invocable<Function, util::span<handle const>, util::span<Components> ...>
|
||||
|| std::invocable<Function, util::span<Components> ...>
|
||||
;
|
||||
};
|
||||
|
||||
template <typename Function, typename ... Components>
|
||||
void batch_invoke(Function && function, container & parent, std::size_t count, handle const * handles, Components * ... components)
|
||||
|
|
|
|||
|
|
@ -6,23 +6,33 @@
|
|||
namespace psemek::ecs::detail
|
||||
{
|
||||
|
||||
template <bool With>
|
||||
struct component_hasher
|
||||
{
|
||||
std::size_t result = 0xcd5694d2b3f3443eull;
|
||||
|
||||
void operator()(util::uuid const & uuid)
|
||||
{
|
||||
result ^= std::hash<util::uuid>{}(uuid);
|
||||
if constexpr (With)
|
||||
result ^= std::hash<util::uuid>{}(uuid);
|
||||
else
|
||||
result ^= ~std::hash<util::uuid>{}(uuid);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename UUIDs>
|
||||
template <bool With, typename UUIDs>
|
||||
std::size_t component_hash(UUIDs const & uuids)
|
||||
{
|
||||
component_hasher hasher;
|
||||
component_hasher<With> hasher;
|
||||
for (auto const & uuid : uuids)
|
||||
hasher(uuid);
|
||||
return hasher.result;
|
||||
}
|
||||
|
||||
template <typename WithUUIDs, typename WithoutUUIDs>
|
||||
std::size_t component_hash(WithUUIDs const & with_uuids, WithoutUUIDs const & without_uuids)
|
||||
{
|
||||
return component_hash<true>(with_uuids) ^ component_hash<false>(without_uuids);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ namespace psemek::ecs::detail
|
|||
|
||||
struct query_cache
|
||||
{
|
||||
std::vector<util::uuid> component_uuids;
|
||||
std::vector<util::uuid> with_uuids;
|
||||
std::vector<util::uuid> without_uuids;
|
||||
std::vector<query_cache_entry> entries;
|
||||
|
||||
void add(table * table);
|
||||
|
|
|
|||
|
|
@ -12,15 +12,16 @@ namespace psemek::ecs::detail
|
|||
struct query_cache_set
|
||||
{
|
||||
std::size_t hash;
|
||||
util::hash_set<util::uuid> uuids;
|
||||
util::hash_set<util::uuid> with_uuids;
|
||||
util::hash_set<util::uuid> without_uuids;
|
||||
std::vector<std::weak_ptr<query_cache>> caches;
|
||||
};
|
||||
|
||||
struct query_cache_hash
|
||||
{
|
||||
std::size_t operator()(util::span<util::uuid const> const & uuids) const
|
||||
std::size_t operator()(std::pair<util::span<util::uuid const>, util::span<util::uuid const>> const & uuids) const
|
||||
{
|
||||
return component_hash(uuids);
|
||||
return component_hash(uuids.first, uuids.second);
|
||||
}
|
||||
|
||||
std::size_t operator()(std::unique_ptr<query_cache_set> const & set) const
|
||||
|
|
@ -31,13 +32,20 @@ namespace psemek::ecs::detail
|
|||
|
||||
struct query_cache_equal
|
||||
{
|
||||
bool operator()(util::span<util::uuid const> const & uuids, std::unique_ptr<query_cache_set> const & set) const
|
||||
bool operator()(std::pair<util::span<util::uuid const>, util::span<util::uuid const>> const & uuids, std::unique_ptr<query_cache_set> const & set) const
|
||||
{
|
||||
if (uuids.size() != set->uuids.size())
|
||||
if (uuids.first.size() != set->with_uuids.size())
|
||||
return false;
|
||||
|
||||
for (auto const & uuid : uuids)
|
||||
if (!set->uuids.contains(uuid))
|
||||
if (uuids.second.size() != set->without_uuids.size())
|
||||
return false;
|
||||
|
||||
for (auto const & uuid : uuids.first)
|
||||
if (!set->with_uuids.contains(uuid))
|
||||
return false;
|
||||
|
||||
for (auto const & uuid : uuids.second)
|
||||
if (!set->without_uuids.contains(uuid))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
|
@ -47,23 +55,25 @@ namespace psemek::ecs::detail
|
|||
{
|
||||
return set1.get() == set2.get();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// TODO: store query caches in a bitmask trie balanced by subtree size
|
||||
struct query_cache_container
|
||||
{
|
||||
std::shared_ptr<query_cache> create(util::span<util::uuid const> component_uuids)
|
||||
std::shared_ptr<query_cache> create(util::span<util::uuid const> with_uuids, util::span<util::uuid const> without_uuids)
|
||||
{
|
||||
auto result = std::make_shared<query_cache>();
|
||||
result->component_uuids.assign(component_uuids.begin(), component_uuids.end());
|
||||
auto it = caches_.find(component_uuids);
|
||||
result->with_uuids.assign(with_uuids.begin(), with_uuids.end());
|
||||
result->without_uuids.assign(without_uuids.begin(), without_uuids.end());
|
||||
auto it = caches_.find(std::pair{with_uuids, without_uuids});
|
||||
if (it == caches_.end())
|
||||
{
|
||||
auto value = std::make_unique<query_cache_set>();
|
||||
for (auto const & uuid : component_uuids)
|
||||
value->uuids.insert(uuid);
|
||||
value->hash = component_hash(component_uuids);
|
||||
for (auto const & uuid : with_uuids)
|
||||
value->with_uuids.insert(uuid);
|
||||
for (auto const & uuid : without_uuids)
|
||||
value->without_uuids.insert(uuid);
|
||||
value->hash = component_hash(with_uuids, without_uuids);
|
||||
it = caches_.insert(std::move(value)).first;
|
||||
}
|
||||
it->get()->caches.push_back(result);
|
||||
|
|
@ -76,7 +86,7 @@ namespace psemek::ecs::detail
|
|||
for (auto & cache_set : caches_)
|
||||
{
|
||||
bool good = true;
|
||||
for (auto const & uuid : cache_set->uuids)
|
||||
for (auto const & uuid : cache_set->with_uuids)
|
||||
if (!contains_uuid(uuid))
|
||||
{
|
||||
good = false;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ namespace psemek::ecs::detail
|
|||
{
|
||||
std::size_t operator()(util::span<util::uuid const> const & uuids) const
|
||||
{
|
||||
return component_hash(uuids);
|
||||
return component_hash<true>(uuids);
|
||||
}
|
||||
|
||||
std::size_t operator()(std::unique_ptr<table> const & table) const
|
||||
|
|
@ -47,7 +47,7 @@ namespace psemek::ecs::detail
|
|||
table * insert(std::unique_ptr<table> table);
|
||||
|
||||
template <typename Function>
|
||||
void apply(Function && function, util::span<util::uuid const> component_uuids);
|
||||
void apply(Function && function, util::span<util::uuid const> with_uuids, util::span<util::uuid const> without_uuids);
|
||||
|
||||
private:
|
||||
util::hash_set<std::unique_ptr<table>, table_hashset_hash, table_hashset_equal> tables_;
|
||||
|
|
@ -69,18 +69,26 @@ namespace psemek::ecs::detail
|
|||
}
|
||||
|
||||
template <typename Function>
|
||||
void table_container::apply(Function && function, util::span<util::uuid const> component_uuids)
|
||||
void table_container::apply(Function && function, util::span<util::uuid const> with_uuids, util::span<util::uuid const> without_uuids)
|
||||
{
|
||||
for (auto & table : tables_)
|
||||
{
|
||||
bool good = true;
|
||||
for (auto const & uuid : component_uuids)
|
||||
|
||||
for (auto const & uuid : with_uuids)
|
||||
if (!table->column(uuid))
|
||||
{
|
||||
good = false;
|
||||
break;
|
||||
}
|
||||
|
||||
for (auto const & uuid : without_uuids)
|
||||
if (table->column(uuid))
|
||||
{
|
||||
good = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (good) function(*table);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
70
libs/ecs/include/psemek/ecs/detail/without.hpp
Normal file
70
libs/ecs/include/psemek/ecs/detail/without.hpp
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/ecs/without.hpp>
|
||||
|
||||
#include <tuple>
|
||||
|
||||
namespace psemek::ecs::detail
|
||||
{
|
||||
|
||||
template <template <typename ...> typename MetaFunction, typename ExtraArgsTuple, typename FilteredComponentsTuple, typename ... Components>
|
||||
struct filter_with_impl;
|
||||
|
||||
template <template <typename ...> typename MetaFunction, typename ... ExtraArgs, typename ... FilteredComponents>
|
||||
struct filter_with_impl<MetaFunction, std::tuple<ExtraArgs...>, std::tuple<FilteredComponents...>>
|
||||
{
|
||||
using type = MetaFunction<ExtraArgs..., FilteredComponents...>;
|
||||
};
|
||||
|
||||
template <template <typename ...> typename MetaFunction, typename ... ExtraArgs, typename ... FilteredComponents, typename Component, typename ... RemainingComponents>
|
||||
struct filter_with_impl<MetaFunction, std::tuple<ExtraArgs...>, std::tuple<FilteredComponents...>, Component, RemainingComponents...>
|
||||
{
|
||||
using type = typename filter_with_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_with_impl<MetaFunction, std::tuple<ExtraArgs...>, std::tuple<FilteredComponents...>, ecs::without<Component>, 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>
|
||||
struct filter_without_impl;
|
||||
|
||||
template <template <typename ...> typename MetaFunction, typename ... ExtraArgs, typename ... FilteredComponents>
|
||||
struct filter_without_impl<MetaFunction, std::tuple<ExtraArgs...>, std::tuple<FilteredComponents...>>
|
||||
{
|
||||
using type = MetaFunction<ExtraArgs..., FilteredComponents...>;
|
||||
};
|
||||
|
||||
template <template <typename ...> typename MetaFunction, typename ... ExtraArgs, typename ... FilteredComponents, typename Component, typename ... RemainingComponents>
|
||||
struct filter_without_impl<MetaFunction, std::tuple<ExtraArgs...>, std::tuple<FilteredComponents...>, Component, RemainingComponents...>
|
||||
{
|
||||
using type = typename filter_without_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_without_impl<MetaFunction, std::tuple<ExtraArgs...>, std::tuple<FilteredComponents...>, 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>
|
||||
struct filter_with;
|
||||
|
||||
template <template <typename ...> typename MetaFunction, typename ComponentsTuple, typename ... ExtraArgs>
|
||||
struct filter_without;
|
||||
|
||||
template <template <typename ...> typename MetaFunction, typename ... Components, typename ... ExtraArgs>
|
||||
struct filter_with<MetaFunction, std::tuple<Components...>, ExtraArgs...>
|
||||
{
|
||||
using type = typename filter_with_impl<MetaFunction, std::tuple<ExtraArgs...>, std::tuple<>, Components...>::type;
|
||||
};
|
||||
|
||||
template <template <typename ...> typename MetaFunction, typename ... Components, typename ... ExtraArgs>
|
||||
struct filter_without<MetaFunction, std::tuple<Components...>, ExtraArgs...>
|
||||
{
|
||||
using type = typename filter_without_impl<MetaFunction, std::tuple<ExtraArgs...>, std::tuple<>, Components...>::type;
|
||||
};
|
||||
|
||||
}
|
||||
16
libs/ecs/include/psemek/ecs/without.hpp
Normal file
16
libs/ecs/include/psemek/ecs/without.hpp
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace psemek::ecs
|
||||
{
|
||||
|
||||
template <typename Component>
|
||||
struct without
|
||||
{
|
||||
static_assert(!std::is_reference_v<Component>, "a component cannot be a reference");
|
||||
|
||||
using component_type = std::remove_const_t<Component>;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ namespace psemek::ecs::detail
|
|||
auto & entry = entries.emplace_back();
|
||||
entry.table = table;
|
||||
|
||||
for (auto const & uuid : component_uuids)
|
||||
for (auto const & uuid : with_uuids)
|
||||
entry.columns.push_back(table->column(uuid));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ namespace psemek::ecs::detail
|
|||
|
||||
table::table(std::vector<std::unique_ptr<detail::column>> columns)
|
||||
{
|
||||
component_hasher hasher;
|
||||
component_hasher<true> hasher;
|
||||
for (auto & column : columns)
|
||||
{
|
||||
auto uuid = column->uuid();
|
||||
|
|
|
|||
|
|
@ -110,6 +110,59 @@ test_case(ecs_apply_components_2)
|
|||
expect_equal(sum, expected_sum);
|
||||
}
|
||||
|
||||
test_case(ecs_apply_without)
|
||||
{
|
||||
container container;
|
||||
|
||||
int const count = 1024;
|
||||
|
||||
int call_count = 0;
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
container.create();
|
||||
|
||||
call_count = 0;
|
||||
container.apply<without<component_2>>([&]{ ++call_count; });
|
||||
expect_equal(count, call_count);
|
||||
|
||||
call_count = 0;
|
||||
container.apply<component_1, without<component_2>>([&](component_1 const &){ ++call_count; });
|
||||
expect_equal(0, call_count);
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
container.create(component_1{i});
|
||||
|
||||
call_count = 0;
|
||||
container.apply<without<component_2>>([&]{ ++call_count; });
|
||||
expect_equal(count * 2, call_count);
|
||||
|
||||
call_count = 0;
|
||||
container.apply<component_1, without<component_2>>([&](component_1 const &){ ++call_count; });
|
||||
expect_equal(count, call_count);
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
container.create(component_2{i});
|
||||
|
||||
call_count = 0;
|
||||
container.apply<without<component_2>>([&]{ ++call_count; });
|
||||
expect_equal(count * 2, call_count);
|
||||
|
||||
call_count = 0;
|
||||
container.apply<component_1, without<component_2>>([&](component_1 const &){ ++call_count; });
|
||||
expect_equal(count, call_count);
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
container.create(component_1{i}, component_2{i});
|
||||
|
||||
call_count = 0;
|
||||
container.apply<without<component_2>>([&]{ ++call_count; });
|
||||
expect_equal(count * 2, call_count);
|
||||
|
||||
call_count = 0;
|
||||
container.apply<component_1, without<component_2>>([&](component_1 const &){ ++call_count; });
|
||||
expect_equal(count, call_count);
|
||||
}
|
||||
|
||||
test_case(ecs_apply_batch_invoke)
|
||||
{
|
||||
container container;
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ test_case(ecs_cache_empty)
|
|||
auto cache = container.cache();
|
||||
|
||||
expect_different_ptr(cache.get(), nullptr);
|
||||
expect(cache->component_uuids.empty());
|
||||
expect(cache->with_uuids.empty());
|
||||
expect_equal(cache->entries.size(), 4);
|
||||
}
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ test_case(ecs_cache_components)
|
|||
auto cache = container.cache<component_1>();
|
||||
|
||||
expect_different_ptr(cache.get(), nullptr);
|
||||
expect_equal(cache->component_uuids.size(), 1);
|
||||
expect_equal(cache->with_uuids.size(), 1);
|
||||
expect_equal(cache->entries.size(), 2);
|
||||
}
|
||||
|
||||
|
|
@ -71,23 +71,23 @@ test_case(ecs_cache_update)
|
|||
auto cache = container.cache<component_1>();
|
||||
|
||||
expect_different_ptr(cache.get(), nullptr);
|
||||
expect_equal(cache->component_uuids.size(), 1);
|
||||
expect_equal(cache->with_uuids.size(), 1);
|
||||
expect_equal(cache->entries.size(), 0);
|
||||
|
||||
container.create();
|
||||
expect_equal(cache->component_uuids.size(), 1);
|
||||
expect_equal(cache->with_uuids.size(), 1);
|
||||
expect_equal(cache->entries.size(), 0);
|
||||
|
||||
container.create(component_1{10});
|
||||
expect_equal(cache->component_uuids.size(), 1);
|
||||
expect_equal(cache->with_uuids.size(), 1);
|
||||
expect_equal(cache->entries.size(), 1);
|
||||
|
||||
container.create(component_2{20});
|
||||
expect_equal(cache->component_uuids.size(), 1);
|
||||
expect_equal(cache->with_uuids.size(), 1);
|
||||
expect_equal(cache->entries.size(), 1);
|
||||
|
||||
container.create(component_1{100}, component_2{200});
|
||||
expect_equal(cache->component_uuids.size(), 1);
|
||||
expect_equal(cache->with_uuids.size(), 1);
|
||||
expect_equal(cache->entries.size(), 2);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue