perf(server): parallelize entity update loop

Use parallel_do to process entities concurrently via the compute
pool. Switch get_all_session to a thread-safe
tbb::concurrent_vector and make ChunkEntity's ref_count atomic to
avoid data races. Also remove the debug pig spawn from world init.
This commit is contained in:
2026-08-05 20:26:57 +08:00
parent 2aa9d07e43
commit b2187bf0ad
4 changed files with 57 additions and 20 deletions

View File

@@ -33,18 +33,31 @@ void ServerEntityManager::update() {
handle_task();
auto view = m_registry.view<BaseServerCreature>();
auto sessions = m_world.get_all_session();
for (auto e : view) {
const auto& c = view.get<BaseServerCreature>(e);
if (!m_world.get_chunk_ref_count(c.transform.position.value)) {
const auto& entity = m_registry.get<Entity>(e);
destory(entity.id);
continue;
}
update_ai(e);
update_move(e);
update_send(e, sessions);
auto pool = m_world.get_compute_pool();
if (!pool) {
return;
}
auto sessions = m_world.get_all_session();
std::vector<entt::entity> entities;
for (auto e : view) {
entities.push_back(e);
}
// parallel block touches disjoint entities only;
// structural registry changes stay on the server thread via m_tasks.
parallel_do(
*pool, entities.begin(), entities.end(), pool->thread_sum(),
[this, &sessions](entt::entity e) {
const auto& c = m_registry.get<BaseServerCreature>(e);
if (!m_world.get_chunk_ref_count(c.transform.position.value)) {
const auto& entity = m_registry.get<Entity>(e);
destory(entity.id);
return;
}
update_ai(e);
update_move(e);
update_send(e, sessions);
});
}
void ServerEntityManager::update_ai(entt::entity e) {
@@ -59,7 +72,8 @@ void ServerEntityManager::update_move(entt::entity e) {
}
void ServerEntityManager::update_send(
entt::entity e, std::span<std::shared_ptr<Session>> sessions) {
entt::entity e,
tbb::concurrent_vector<std::shared_ptr<Session>>& sessions) {
if (!m_registry.all_of<Entity, BaseServerCreature>(e)) {
return;

View File

@@ -182,7 +182,6 @@ void ServerWorld::send_chunk(int task_id, const std::string& uuid,
void ServerWorld::init_world(RunMode mode) {
m_runmode = mode;
m_entity_manager.init();
m_entity_manager.add_entity("cubed:pig", {0, 225, 0});
register_timer("player disconnect", 5, [this]() {
std::vector<std::string> disconnect;
{
@@ -319,7 +318,7 @@ void ServerWorld::sync_and_collect_missing_chunks(
chunk_acc acc;
if (m_chunks.insert(acc, pos)) {
need_gen_chunks_pos.push_back(pos);
acc->second = ChunkEntity{ChunkState::GENERATING, nullptr, 0};
acc->second = ChunkEntity{ChunkState::GENERATING};
}
}
}
@@ -1028,9 +1027,10 @@ void ServerWorld::set_chunk_load_style(int id) {
int ServerWorld::chunk_size() const { return m_chunks.size(); }
std::vector<std::shared_ptr<Session>> ServerWorld::get_all_session() const {
tbb::concurrent_vector<std::shared_ptr<Session>>
ServerWorld::get_all_session() const {
std::shared_lock lock(m_players_mutex);
std::vector<std::shared_ptr<Session>> sessions;
tbb::concurrent_vector<std::shared_ptr<Session>> sessions;
for (const auto& [_, player] : m_players) {
sessions.emplace_back(player.get_session());
}