199 lines
4.6 KiB
C++
199 lines
4.6 KiB
C++
#include <psemek/ecs/container.hpp>
|
|
#include <psemek/util/assert.hpp>
|
|
|
|
#include <algorithm>
|
|
|
|
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<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;
|
|
|
|
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<std::unique_ptr<detail::column>> columns)
|
|
{
|
|
auto table = table_container_.insert(std::make_unique<detail::table>(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<detail::entity_data> 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;
|
|
}
|
|
|
|
}
|