#include #include #include namespace psemek::ecs { bool container::alive(handle entity) const { return entity_list_.get_entities()[entity.id].epoch == entity.epoch; } void container::destroy(handle entity) { #ifdef PSEMEK_DEBUG assert(!currently_changing_archetype_.contains(entity)); currently_changing_archetype_.insert(entity); #endif 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); #ifdef PSEMEK_DEBUG currently_changing_archetype_.erase(entity); #endif finalize_method(); --method_recursion_depth_; } void container::destroy_finally(handle entity) { finally([entity](container & world){ if (world.alive(entity)) world.destroy(entity); }); } accessor container::get(handle entity) { assert(alive(entity)); auto const data = entity_list_.get_entities()[entity.id]; return {data.table, data.row}; } std::string container::describe(handle entity) const { assert(alive(entity)); auto const data = entity_list_.get_entities()[entity.id]; return data.table->describe(data.row); } bool container::can_clone(handle entity) const { return entity_list_.get_entities()[entity.id].table->non_copyable_components().empty(); } handle container::clone(handle entity) { if (auto result = try_clone(entity)) return *result; auto const data = entity_list_.get_entities()[entity.id]; throw entity_not_cloneable(entity, data.table->non_copyable_components()); } std::optional 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; auto table = data.table; if (table->get_iteration_data()) table = table->get_delayed_table(); auto row = table->row_count(); auto id = entity_list_.create(table, row); handle handle{id, entity_list_.get_entities()[id].epoch}; table->copy_row(handle, data.table, data.row); table->trigger_constructors(*this, row); finalize_method(); --method_recursion_depth_; return handle; } detail::table * container::insert_table(std::vector> columns) { auto table = table_container_.insert(std::make_unique(std::move(columns))); query_cache_container_.apply([table](detail::query_cache & cache){ cache.add(table); }, [table](util::uuid const & uuid){ return table->column(uuid) != nullptr; }); return table; } void container::do_destroy(handle entity) { auto entities = entity_list_.get_entities(); auto & data = entities[entity.id]; auto & iteration_data = data.table->get_iteration_data(); if (!iteration_data || iteration_data->current_row < data.row) remove_row(*data.table, data.row, entities); else data.table->push_remove(data.row); } void container::remove_row(detail::table & table, std::uint32_t row, util::span entities) { // Swap with the last row in that table auto table_entity_handles = table.entity_handles(); auto last_row = table_entity_handles.size() - 1; if (row != last_row) { table.swap_rows(row, last_row); auto swap_handle = table_entity_handles[row]; entities[swap_handle.id].row = row; } table.pop_row(); } void container::finalize_iteration(detail::table & table) { auto remove_queue = table.grab_remove_queue(); std::sort(remove_queue.begin(), remove_queue.end()); auto entities = entity_list_.get_entities(); for (auto row : util::reversed(remove_queue)) remove_row(table, row, entities); std::uint32_t row = table.row_count(); for (auto const & handle : table.flush_delayed()) { auto & data = entities[handle.id]; data.table = &table; data.row = row++; } } 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(); } std::size_t container::cache_count() { return query_cache_container_.cache_count(); } std::size_t container::table_count() { return table_container_.table_count(); } statistics container::statistics() { struct statistics result; table_container_.apply([&](detail::table & table) { result.tables.push_back({table.describe(), table.row_count()}); }, {}, {}); return result; } }