mirror of
https://github.com/zhenyan121/Cubed.git
synced 2026-08-08 17:57:02 +08:00
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:
@@ -5,6 +5,7 @@
|
||||
#include <entt/entt.hpp>
|
||||
#include <tbb/concurrent_hash_map.h>
|
||||
#include <tbb/concurrent_queue.h>
|
||||
#include <tbb/concurrent_vector.h>
|
||||
namespace Cubed {
|
||||
class ServerWorld;
|
||||
class Session;
|
||||
@@ -46,8 +47,9 @@ private:
|
||||
void send_all_entities(std::shared_ptr<Session>& session);
|
||||
void update_ai(entt::entity e);
|
||||
void update_move(entt::entity e);
|
||||
void update_send(entt::entity e,
|
||||
std::span<std::shared_ptr<Session>> sessions);
|
||||
void
|
||||
update_send(entt::entity e,
|
||||
tbb::concurrent_vector<std::shared_ptr<Session>>& sessions);
|
||||
template <typename... Args>
|
||||
EntityID create_entity_in_factory(Args&&... args) {
|
||||
auto entity = m_registry.create();
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <tbb/concurrent_hash_map.h>
|
||||
#include <tbb/concurrent_queue.h>
|
||||
#include <tbb/concurrent_unordered_map.h>
|
||||
#include <tbb/concurrent_vector.h>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -92,7 +93,7 @@ public:
|
||||
|
||||
int chunk_size() const;
|
||||
|
||||
std::vector<std::shared_ptr<Session>> get_all_session() const;
|
||||
tbb::concurrent_vector<std::shared_ptr<Session>> get_all_session() const;
|
||||
|
||||
uint32_t get_chunk_ref_count(const glm::vec3& pos) const;
|
||||
ServerEntityManager& entity_manager();
|
||||
@@ -114,7 +115,27 @@ private:
|
||||
struct ChunkEntity {
|
||||
ChunkState state;
|
||||
std::shared_ptr<ServerChunk> chunk;
|
||||
uint32_t ref_count = 0;
|
||||
std::atomic<uint32_t> ref_count = 0;
|
||||
ChunkEntity() = default;
|
||||
ChunkEntity(ChunkState s, std::shared_ptr<ServerChunk> c = {})
|
||||
: state(s), chunk(std::move(c)) {}
|
||||
|
||||
ChunkEntity& operator=(ChunkEntity&& o) noexcept {
|
||||
if (this == &o) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
state = std::exchange(o.state, ServerWorld::ChunkState::NONE);
|
||||
chunk = std::move(o.chunk);
|
||||
ref_count = o.ref_count.exchange(0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ChunkEntity(ChunkEntity&& o) noexcept
|
||||
: state(std::exchange(o.state, ServerWorld::ChunkState::NONE)),
|
||||
chunk(std::move(o.chunk)), ref_count(o.ref_count.exchange(0)) {}
|
||||
ChunkEntity(const ChunkEntity&) = delete;
|
||||
ChunkEntity& operator=(const ChunkEntity&) = delete;
|
||||
};
|
||||
|
||||
enum class ChunkLoadStyle { RANDOM, CENTER };
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user