feat(gameplay): add run mode based thread pool config

Introduce RunMode enum and thread pool sizing helpers for client,
server, and hybrid modes. Add compute pool to server world and pass
mode through server/client initialization.
This commit is contained in:
2026-08-05 19:49:06 +08:00
parent 8ff789c576
commit 2aa9d07e43
9 changed files with 202 additions and 37 deletions

View File

@@ -32,7 +32,7 @@ public:
ClientWorld(AudioEngine& auido, Config& config, WorldScene& scene);
~ClientWorld();
void init(std::string_view player_name,
std::shared_ptr<NetworkClient> client);
std::shared_ptr<NetworkClient> client, RunMode mode);
void update(float dt);
bool handle_event(const Event& e);
const std::optional<LookBlock>& get_look_block_pos() const;
@@ -132,6 +132,7 @@ private:
static constexpr int WORLD_EXIT_TIMEOUT = 200;
static constexpr int MAX_UPLOAD_CHUNK_SUM = 16;
std::atomic<RunMode> m_runmode = RunMode::HYBRID;
ClientEntityManager m_entity_manager;
ClientPlayerManager m_player_manager;
ChunkHashMap m_chunks;

View File

@@ -14,8 +14,7 @@ public:
void stop();
// Run in another thread after initialization is complete
void start_server(int port);
void start_server();
void start_server(int port, RunMode mode);
int port() const;
ServerWorld& server_world();

View File

@@ -34,7 +34,7 @@ public:
~ServerWorld();
void stop();
void handle_player_exit(const std::string& uuid);
void init_world();
void init_world(RunMode mode);
void need_gen(std::string uuid);
void update();
void hot_reload();
@@ -96,7 +96,7 @@ public:
uint32_t get_chunk_ref_count(const glm::vec3& pos) const;
ServerEntityManager& entity_manager();
std::shared_ptr<ThreadPool> get_compute_pool();
int get_block(const glm::ivec3& block_pos) const override;
bool is_solid(const glm::ivec3& block_pos) const override;
bool can_pass_block(const glm::ivec3& block_pos) const override;
@@ -142,6 +142,7 @@ private:
using uuid_cacc = PlayerUUIDMap::const_accessor;
Config& m_config;
std::atomic<RunMode> m_runmode{RunMode::HYBRID};
ServerEntityManager m_entity_manager;
// key = uuid
PlayerHashMap m_players;
@@ -163,8 +164,9 @@ private:
std::atomic<bool> m_init{false};
std::atomic<bool> m_stopped{false};
std::atomic<int> m_rendering_distance{24};
std::atomic<int> m_gen_pool_threads{0};
std::atomic<int> m_net_pool_threads{0};
std::atomic<int> m_gen_threads{0};
std::atomic<int> m_net_threads{0};
std::atomic<int> m_compute_threads{0};
std::atomic<int> m_max_threads{1};
std::atomic<size_t> m_player_sum{0};
std::atomic<TickType> m_game_ticks{0};
@@ -180,6 +182,7 @@ private:
std::atomic<std::shared_ptr<PriorityThreadPool>> m_gen_thread_pool;
std::atomic<std::shared_ptr<ThreadPool>> m_net_thread_pool;
std::atomic<std::shared_ptr<ThreadPool>> m_compute_thread_pool;
std::atomic<ChunkLoadStyle> m_chunk_load_style{ChunkLoadStyle::CENTER};

View File

@@ -26,4 +26,7 @@ public:
glm::vec3{0.5f, 0.5f, 0.5f}};
}
};
enum class RunMode { CLIENT_ONLY, SERVER_ONLY, HYBRID };
} // namespace Cubed

View File

@@ -0,0 +1,130 @@
#pragma once
#include "Cubed/gameplay/world.hpp"
#include "Cubed/tools/cubed_assert.hpp"
#include <algorithm>
#include <thread>
namespace Cubed {
namespace Tools {
constexpr size_t SERVER_RESERVED_THREADS = 3; // tick + netio + gen scheduler
constexpr size_t CLIENT_RESERVED_THREADS =
3; // main/render + netio + system reserved
constexpr size_t safe_sub(size_t a, size_t b) { return a > b ? a - b : 0; }
inline size_t get_hardware_threads() {
auto hc = std::thread::hardware_concurrency();
return hc == 0 ? 4 : static_cast<size_t>(hc);
}
inline size_t get_server_available_threads() {
return std::max<size_t>(
1, safe_sub(get_hardware_threads(), SERVER_RESERVED_THREADS));
}
inline size_t get_client_available_threads() {
return std::max<size_t>(
1, safe_sub(get_hardware_threads(), CLIENT_RESERVED_THREADS));
}
inline size_t get_client_threads(RunMode mode) {
switch (mode) {
case RunMode::SERVER_ONLY:
ASSERT_MSG(false, "Server Only don't need client pool");
return 1;
case RunMode::CLIENT_ONLY: {
auto available = get_client_available_threads();
return std::clamp<size_t>(available, 1, 16);
}
case RunMode::HYBRID: {
auto available = get_client_available_threads();
return std::clamp<size_t>(available / 2, 1, 4);
}
}
return 1;
}
inline size_t get_server_net_pool_threads(RunMode mode) {
switch (mode) {
case RunMode::SERVER_ONLY: {
auto available = get_server_available_threads();
return std::clamp<size_t>(available / 4, 1,
std::min<size_t>(4, available));
}
case RunMode::CLIENT_ONLY:
ASSERT_MSG(false, "Client Only don't need net pool");
return 1;
case RunMode::HYBRID: {
auto available = get_server_available_threads();
return std::clamp<size_t>(available / 8, 1,
std::min<size_t>(4, available));
}
}
return 1;
}
inline size_t get_server_compute_treads(RunMode mode) {
switch (mode) {
case RunMode::HYBRID:
case RunMode::SERVER_ONLY: {
auto available = get_server_available_threads();
return std::clamp<size_t>(available / 4, 1,
std::min<size_t>(4, available));
}
case RunMode::CLIENT_ONLY:
ASSERT_MSG(false, "Client Only don't need update pool");
return 1;
}
return 1;
}
inline size_t get_server_gen_threads(RunMode mode) {
switch (mode) {
case RunMode::SERVER_ONLY: {
auto available = get_server_available_threads();
auto net_pool = get_server_net_pool_threads(mode);
auto update_pool = get_server_compute_treads(mode);
size_t remain = available;
remain -= std::min(remain, net_pool);
remain -= std::min(remain, update_pool);
return std::max<size_t>(1, remain);
}
case RunMode::CLIENT_ONLY:
ASSERT_MSG(false, "Client Only don't need gen pool");
return 1;
case RunMode::HYBRID: {
auto available = get_server_available_threads();
auto net_pool = get_server_net_pool_threads(mode);
auto update_pool = get_server_compute_treads(mode);
auto client_pool = get_client_threads(mode);
size_t remain = available;
remain -= std::min(remain, net_pool);
remain -= std::min(remain, update_pool);
remain -= std::min(remain, client_pool);
return std::max<size_t>(1, remain);
}
}
return 1;
}
} // namespace Tools
} // namespace Cubed