81 constexpr auto operator=(const
world &) noexcept
89 auto operator=(
world &&other) noexcept
104 template <typename ...T>
105 constexpr auto
spawn(T &&...components) noexcept
107 auto entity{_entities.size() + _spawning.size()};
109 if (_despawned.size()) {
110 entity = *(_despawned.end() - 1);
115 if (
const auto pair{_entities.at(entity)}) {
116 pair->second =
false;
119 _spawning.insert(entity);
123 const auto command{[
this] (
const auto entity,
auto &&...components)
noexcept ->
void {
124 [
this] <std::size_t ...I> (
const auto entity, T &&...components,
const std::index_sequence<I...>)
noexcept ->
void {
125 const auto &ids{internal::sparse_storage::ids<T...>()};
126 (_components.at<T>(ids[I]).insert(entity, std::forward<T>(components)), ...);
127 }(entity, std::forward<T>(components)..., std::index_sequence_for<T...>{});
133 _commands.enqueue([cpt = std::pair{command, std::tuple{entity, std::forward<T>(components)...}}]
mutable noexcept ->
void {
134 std::apply(cpt.first, std::move(cpt.second));
138 command(entity, std::forward<T>(components)...);
152 template <
typename ...T>
154 constexpr auto insert(
const std::uint32_t entity, T &&...components)
noexcept
155 -> std::expected<void, std::tuple<T...>>
156 requires (
static_cast<bool>(
sizeof...(T)))
158 if (
const auto pair{_entities.at(entity)}; (!pair || pair->second) && !
spawning(entity)) {
159 return std::unexpected{std::tuple{std::forward<T>(components)...}};
162 const auto command{[
this] (
const auto entity,
auto &&...components)
noexcept ->
void {
163 [
this] <std::size_t ...I> (
const auto entity, T &&...components,
const std::index_sequence<I...>)
noexcept ->
void {
164 const auto &ids{internal::sparse_storage::ids<T...>()};
165 (_components.at<T>(ids[I]).insert(entity, std::forward<T>(components)), ...);
166 }(entity, std::forward<T>(components)..., std::index_sequence_for<T...>{});
168 cache = (*this)[entity];
170 if constexpr (1 <
sizeof...(T)) {
174 cache.insert(internal::sparse_storage::ids<T...>().front());
177 relocate(entity, _entities[entity].first);
181 _commands.enqueue([cpt = std::pair{command, std::tuple{entity, std::forward<T>(components)...}}]
mutable noexcept ->
void {
182 std::apply(cpt.first, std::move(cpt.second));
186 command(entity, std::forward<T>(components)...);
233 template <typename ...T>
235 constexpr auto
contains(const std::uint32_t entity) const noexcept
236 requires (static_cast<
bool>(sizeof...(T)))
238 if (
const auto pair{_entities.at(entity)}) {
239 return [] <std::size_t ...I> [[nodiscard]] (
const auto &
archetype,
const std::index_sequence<I...>)
noexcept ->
auto {
240 const auto &ids{internal::sparse_storage::ids<T...>()};
242 }(_archetypes[pair->first].first, std::index_sequence_for<T...>{});
245 return std::tuple{ext::expand_as_v<T, false>...};
254 auto at(std::uint32_t entity)
const noexcept
263 template <
typename ...T>
265 constexpr auto at(
const std::uint32_t entity)
const noexcept
266 requires (
static_cast<bool>(
sizeof...(T)))
268 return [
this] <std::size_t ...I> [[nodiscard]] (
const auto entity,
const std::index_sequence<I...>)
noexcept ->
auto {
269 const auto &ids{internal::sparse_storage::ids<T...>()};
270 return std::tuple{_components.at<T>(ids[I]).
at(entity)...};
271 }(entity, std::index_sequence_for<T...>{});
280 template <
typename ...T>
282 constexpr auto at(
const std::uint32_t entity)
noexcept
283 requires (
static_cast<bool>(
sizeof...(T)))
285 return [
this] <std::size_t ...I> [[nodiscard]] (
const auto entity,
const std::index_sequence<I...>)
noexcept ->
auto {
286 const auto &ids{internal::sparse_storage::ids<T...>()};
287 return std::tuple{_components.at<T>(ids[I]).
at(entity)...};
288 }(entity, std::index_sequence_for<T...>{});
308 template <
typename ...T>
310 constexpr auto operator[](
const std::uint32_t entity)
const noexcept
311 requires (
static_cast<bool>(
sizeof...(T)))
313 return [
this] <std::size_t ...I> [[nodiscard]] (
const auto entity,
const std::index_sequence<I...>)
noexcept ->
auto {
314 const auto &ids{internal::sparse_storage::ids<T...>()};
315 return std::tuple<
const T &...>{_components.operator[]<T>(ids[I])[entity]...};
316 }(entity, std::index_sequence_for<T...>{});
326 template <
typename ...T>
328 constexpr auto operator[](
const std::uint32_t entity)
noexcept
329 requires (
static_cast<bool>(
sizeof...(T)))
331 return [
this] <std::size_t ...I> [[nodiscard]] (
const auto entity,
const std::index_sequence<I...>)
noexcept ->
auto {
332 const auto &ids{internal::sparse_storage::ids<T...>()};
333 return std::tuple<T &...>{_components.operator[]<T>(ids[I])[entity]...};
334 }(entity, std::index_sequence_for<T...>{});
345 return internal::query{_archetypes | std::views::transform([] [[nodiscard]] (
const auto &pair)
noexcept ->
auto {
346 return pair.second | std::views::transform([&] [[nodiscard]] (
const auto entity)
noexcept ->
auto {
347 return std::tuple<std::uint32_t, const archetype &>{entity, pair.first};
349 }) | std::views::join, _execute};
357 template <
typename T>
362 return internal::query{_components.at<T>(internal::sparse_storage::ids<T>().front()).zip(), _execute};
370 template <
typename ...T>
373 requires (1 < sizeof...(T))
377 if (_queries.extend(
id + 1, std::numeric_limits<std::uint16_t>::max()) || _queries[
id] == std::numeric_limits<std::uint16_t>::max()) {
378 const auto &archetype{archetype::of<T...>()};
380 const auto pair{std::ranges::find_if(_indices, [&] [[nodiscard]] (const auto &pair) noexcept -> auto {
381 return pair.first == archetype;
384 if (pair == _indices.end()) {
385 _queries[id] = _indices.size();
386 _indices.push(
archetype, internal::stack_vector<std::uint16_t>{});
388 for (
const auto index : std::views::iota(std::uint16_t{}, _archetypes.size())) {
389 if (archetype <= _archetypes[index].first) {
390 (_indices.end() - 1)->second.push(index);
395 _queries[id] =
static_cast<std::uint16_t
>(pair - _indices.begin());
400 return internal::query{std::ranges::subrange{_indices[_queries[id]].second} | std::views::transform([
this] [[nodiscard]] (
const auto index)
noexcept ->
const auto & {
401 return _archetypes[index].second;
402 }) | std::views::join | std::views::transform([components = [
this] <std::size_t ...I> [[nodiscard]] (
const std::index_sequence<I...>)
noexcept ->
auto {
403 const auto &ids{internal::sparse_storage::ids<T...>()};
404 return std::tuple<internal::sparse_map<std::uint32_t, T> &...>{_components.operator[]<T>(ids[I])...};
405 }(std::index_sequence_for<T...>{})] [[nodiscard]] (
const auto entity)
noexcept ->
auto {
406 return [] <std::size_t ...I> [[nodiscard]] (
const auto entity,
const auto &components,
const std::index_sequence<I...>)
noexcept ->
auto {
407 return std::tuple<std::uint32_t, T &...>{entity, std::get<I>(components)[entity]...};
408 }(entity, components, std::index_sequence_for<T...>{});
418 template <
typename ...T>
419 constexpr void erase(
const std::uint32_t entity)
noexcept
420 requires (
static_cast<bool>(
sizeof...(T)))
422 if (
const auto pair{_entities.at(entity)}; (!pair || pair->second) && !
spawning(entity)) {
426 const auto command{[
this] (
const auto entity)
noexcept ->
void {
427 [
this] <std::size_t ...I> (
const auto entity,
const std::index_sequence<I...>)
noexcept ->
void {
428 const auto &ids{internal::sparse_storage::ids<T...>()};
429 (_components.at<T>(ids[I]).erase(entity), ...);
430 }(entity, std::index_sequence_for<T...>{});
432 cache = (*this)[entity];
434 if constexpr (1 <
sizeof...(T)) {
438 cache.erase(internal::sparse_storage::ids<T...>().front());
441 relocate(entity, _entities[entity].first);
445 _commands.enqueue([cpt = std::pair{command, entity}]
noexcept ->
void {
446 cpt.first(cpt.second);
469 internal::sparse_storage _components;
470 internal::sparse_set _spawning;
471 internal::stack_vector<std::uint32_t, std::uint32_t> _despawned;
472 internal::sparse_map<std::uint32_t, std::pair<std::uint16_t,
bool>> _entities;
473 internal::stack_vector<std::pair<
archetype, internal::sparse_set>, std::uint16_t> _archetypes;
474 internal::stack_vector<std::uint16_t, std::uint16_t> _queries;
475 internal::stack_vector<std::pair<
archetype, internal::stack_vector<std::uint16_t>>, std::uint16_t> _indices;
477 internal::command_queue _commands;
479 std::function<void ()> _execute{[
this]
noexcept ->
void {
486 template <
typename T>
487 constexpr void relocate(
const std::uint32_t entity, T arg)
noexcept
489 if constexpr (std::is_same_v<T, std::uint16_t>) {
490 _archetypes[arg].second.erase(entity);
493 const auto pair{std::ranges::find_if(_archetypes, [&] [[nodiscard]] (
const auto &pair)
noexcept ->
auto {
494 if constexpr (std::is_same_v<T, const archetype &>) {
495 return pair.first == arg;
497 else if constexpr (std::is_same_v<T, std::uint16_t>) {
498 return pair.first == cache;
502 if (pair == _archetypes.end()) {
503 if constexpr (std::is_same_v<T, const archetype &>) {
504 _entities.insert(entity, _archetypes.size(),
false);
505 _archetypes.push(arg, internal::sparse_set{});
507 else if constexpr (std::is_same_v<T, std::uint16_t>) {
508 _entities[entity].first = _archetypes.size();
509 _archetypes.push(cache, internal::sparse_set{});
512 (_archetypes.end() - 1)->second.insert(entity);
514 for (
auto &pair : _indices) {
515 if (pair.first <= (_archetypes.end() - 1)->first) {
516 pair.second.push(_archetypes.size() - 1);
521 if constexpr (std::is_same_v<T, const archetype &>) {
522 _entities.insert(entity, pair - _archetypes.begin(),
false);
524 else if constexpr (std::is_same_v<T, std::uint16_t>) {
525 _entities[entity].first =
static_cast<std::uint16_t
>(pair - _archetypes.begin());
528 pair->second.insert(entity);
static constexpr auto of() noexcept -> std::conditional_t< static_cast< bool >(sizeof...(T)), const archetype &, archetype >
Retrieves an archetype for a set of component types.
Definition archetype.hpp:51