diff --git a/include/Cubed/gameplay/client_world.hpp b/include/Cubed/gameplay/client_world.hpp index 6b63faa..3040b22 100644 --- a/include/Cubed/gameplay/client_world.hpp +++ b/include/Cubed/gameplay/client_world.hpp @@ -32,7 +32,7 @@ public: ClientWorld(AudioEngine& auido, Config& config, WorldScene& scene); ~ClientWorld(); void init(std::string_view player_name, - std::shared_ptr client); + std::shared_ptr client, RunMode mode); void update(float dt); bool handle_event(const Event& e); const std::optional& 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 m_runmode = RunMode::HYBRID; ClientEntityManager m_entity_manager; ClientPlayerManager m_player_manager; ChunkHashMap m_chunks; diff --git a/include/Cubed/gameplay/network_server.hpp b/include/Cubed/gameplay/network_server.hpp index f12fbd6..7abf197 100644 --- a/include/Cubed/gameplay/network_server.hpp +++ b/include/Cubed/gameplay/network_server.hpp @@ -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(); diff --git a/include/Cubed/gameplay/server_world.hpp b/include/Cubed/gameplay/server_world.hpp index b82dfe1..84b3263 100644 --- a/include/Cubed/gameplay/server_world.hpp +++ b/include/Cubed/gameplay/server_world.hpp @@ -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 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 m_runmode{RunMode::HYBRID}; ServerEntityManager m_entity_manager; // key = uuid PlayerHashMap m_players; @@ -163,8 +164,9 @@ private: std::atomic m_init{false}; std::atomic m_stopped{false}; std::atomic m_rendering_distance{24}; - std::atomic m_gen_pool_threads{0}; - std::atomic m_net_pool_threads{0}; + std::atomic m_gen_threads{0}; + std::atomic m_net_threads{0}; + std::atomic m_compute_threads{0}; std::atomic m_max_threads{1}; std::atomic m_player_sum{0}; std::atomic m_game_ticks{0}; @@ -180,6 +182,7 @@ private: std::atomic> m_gen_thread_pool; std::atomic> m_net_thread_pool; + std::atomic> m_compute_thread_pool; std::atomic m_chunk_load_style{ChunkLoadStyle::CENTER}; diff --git a/include/Cubed/gameplay/world.hpp b/include/Cubed/gameplay/world.hpp index 69ffd7c..710166a 100644 --- a/include/Cubed/gameplay/world.hpp +++ b/include/Cubed/gameplay/world.hpp @@ -26,4 +26,7 @@ public: glm::vec3{0.5f, 0.5f, 0.5f}}; } }; + +enum class RunMode { CLIENT_ONLY, SERVER_ONLY, HYBRID }; + } // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/tools/threas_utils.hpp b/include/Cubed/tools/threas_utils.hpp new file mode 100644 index 0000000..fd01fad --- /dev/null +++ b/include/Cubed/tools/threas_utils.hpp @@ -0,0 +1,130 @@ +#pragma once +#include "Cubed/gameplay/world.hpp" +#include "Cubed/tools/cubed_assert.hpp" + +#include +#include +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(hc); +} + +inline size_t get_server_available_threads() { + return std::max( + 1, safe_sub(get_hardware_threads(), SERVER_RESERVED_THREADS)); +} + +inline size_t get_client_available_threads() { + return std::max( + 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(available, 1, 16); + } + + case RunMode::HYBRID: { + auto available = get_client_available_threads(); + return std::clamp(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(available / 4, 1, + std::min(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(available / 8, 1, + std::min(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(available / 4, 1, + std::min(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(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(1, remain); + } + } + return 1; +} + +} // namespace Tools +} // namespace Cubed diff --git a/src/gameplay/client_world.cpp b/src/gameplay/client_world.cpp index 7fe4da9..7f1b645 100644 --- a/src/gameplay/client_world.cpp +++ b/src/gameplay/client_world.cpp @@ -6,6 +6,7 @@ #include "Cubed/gameplay/game_time.hpp" #include "Cubed/gameplay/packet.hpp" #include "Cubed/scene/world_scene.hpp" +#include "Cubed/tools/threas_utils.hpp" #include "Cubed/tools/time_tools.hpp" #include @@ -363,7 +364,8 @@ void ClientWorld::send_player_water_sound(bool underwater, } void ClientWorld::init(std::string_view player_name, - std::shared_ptr client) { + std::shared_ptr client, RunMode mode) { + m_runmode = mode; m_entity_manager.init(); m_player_manager.init(player_name); m_client = client; @@ -480,8 +482,8 @@ void ClientWorld::stop_client_thread() { m_game_running = false; } void ClientWorld::start_thread_pool() { - int max_threads = std::thread::hardware_concurrency(); - int threads = std::min(max_threads, 4); + auto threads = Tools::get_client_threads(m_runmode); + Logger::info("Client pool threads {}", threads); change_pool_threads(threads); } void ClientWorld::stop_thread_pool() { @@ -500,7 +502,6 @@ void ClientWorld::change_pool_threads(int threads) { m_max_threads = 1; } int used_thread = std::clamp(threads, 1, m_max_threads); - Logger::info("Create New Thread Pool Use {} Threads", used_thread); m_thread_pool.store(std::make_shared(used_thread)); } diff --git a/src/gameplay/network_server.cpp b/src/gameplay/network_server.cpp index d024470..26fdf35 100644 --- a/src/gameplay/network_server.cpp +++ b/src/gameplay/network_server.cpp @@ -92,14 +92,10 @@ void NetworkServer::net_run() { Logger::info("Server Started!"); } -void NetworkServer::start_server(int port) { +void NetworkServer::start_server(int port, RunMode mode) { m_port = port; m_config.set("port", m_port); - start_server(); -} - -void NetworkServer::start_server() { - m_world.init_world(); + m_world.init_world(mode); net_run(); m_started = true; } diff --git a/src/gameplay/server_world.cpp b/src/gameplay/server_world.cpp index f359603..90778a1 100644 --- a/src/gameplay/server_world.cpp +++ b/src/gameplay/server_world.cpp @@ -7,6 +7,7 @@ #include "Cubed/tools/log.hpp" #include "Cubed/tools/math_tools.hpp" #include "Cubed/tools/net_utils.hpp" +#include "Cubed/tools/threas_utils.hpp" #include "Cubed/tools/uuid.hpp" #include @@ -178,8 +179,8 @@ void ServerWorld::send_chunk(int task_id, const std::string& uuid, s->send(make_packet(*rsp)); } -void ServerWorld::init_world() { - +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]() { @@ -361,7 +362,7 @@ void ServerWorld::submit_new_chunks(const std::string& uuid, return dist2(a.first) < dist2(b.first); }); - const int CHUNKS_PER_PRIORITY = m_gen_pool_threads; + const int CHUNKS_PER_PRIORITY = m_gen_threads; for (size_t i = 0; i < tasks.size(); ++i) { int priority = 10 + static_cast(i / CHUNKS_PER_PRIORITY); @@ -411,20 +412,30 @@ void ServerWorld::start_server_thread() { } void ServerWorld::start_thread_pool() { - int max_thread = std::thread::hardware_concurrency(); - if (m_gen_pool_threads == 0) { - m_gen_pool_threads = change_pool_threads(m_gen_thread_pool, - max_thread - RESERVED_THREADS); + if (m_gen_threads == 0) { + auto gen_threads = Tools::get_server_gen_threads(m_runmode); + Logger::info("Server Gen pool threads {}", gen_threads); + m_gen_threads = change_pool_threads(m_gen_thread_pool, gen_threads); } else { - m_gen_pool_threads = - change_pool_threads(m_gen_thread_pool, m_gen_pool_threads); + m_gen_threads = change_pool_threads(m_gen_thread_pool, m_gen_threads); } - if (m_net_pool_threads == 0) { - m_net_pool_threads = change_pool_threads(m_net_thread_pool, 4); + if (m_net_threads == 0) { + auto net_threads = Tools::get_server_net_pool_threads(m_runmode); + Logger::info("Server Net pool threads {}", net_threads); + m_net_threads = change_pool_threads(m_net_thread_pool, net_threads); } else { - m_net_pool_threads = - change_pool_threads(m_net_thread_pool, m_net_pool_threads); + m_net_threads = change_pool_threads(m_net_thread_pool, m_net_threads); + } + + if (m_compute_threads == 0) { + auto compute_threads = Tools::get_server_compute_treads(m_runmode); + Logger::info("Server compute pool threads {}", compute_threads); + m_compute_threads = + change_pool_threads(m_compute_thread_pool, compute_threads); + } else { + m_compute_threads = + change_pool_threads(m_compute_thread_pool, m_compute_threads); } } @@ -459,6 +470,13 @@ void ServerWorld::stop_thread_pool() { } m_net_thread_pool.store(nullptr); Logger::info("Net Thread Pool Stopped"); + + auto c = m_compute_thread_pool.load(); + if (c) { + c->stop(); + } + m_compute_thread_pool.store(nullptr); + Logger::info("Compute Thread Pool Stopped"); } void ServerWorld::serever_run(std::stop_token stoken) { @@ -910,16 +928,16 @@ void ServerWorld::per_tick_time(int ms) { m_per_tick_time = ms; } bool ServerWorld::is_tick_running() const { return m_tick_running.load(); } void ServerWorld::tick_running(bool run) { m_tick_running = run; } -int ServerWorld::gen_pool_threads() const { return m_gen_pool_threads.load(); } +int ServerWorld::gen_pool_threads() const { return m_gen_threads.load(); } int ServerWorld::max_threads() const { return m_max_threads.load(); } void ServerWorld::change_pool_threads(ThreadPoolKind kind, int threads) { switch (kind) { case ThreadPoolKind::NET: - m_net_pool_threads = change_pool_threads(m_net_thread_pool, threads); + m_net_threads = change_pool_threads(m_net_thread_pool, threads); break; case ThreadPoolKind::GEN: - m_gen_pool_threads = change_pool_threads(m_gen_thread_pool, threads); + m_gen_threads = change_pool_threads(m_gen_thread_pool, threads); break; } } @@ -946,7 +964,6 @@ int ServerWorld::change_pool_threads( m_max_threads = 1; } int used_thread = std::clamp(threads, 1, m_max_threads.load()); - Logger::info("Create New Thread Pool Use {} Threads", used_thread); thread_pool.store(std::make_shared(used_thread)); return used_thread; } @@ -1115,4 +1132,7 @@ BlockType ServerWorld::get_block_tpye(const glm::ivec3& block_pos) const { int ServerWorld::get_per_tick_time() const { return m_per_tick_time; } ServerEntityManager& ServerWorld::entity_manager() { return m_entity_manager; } +std::shared_ptr ServerWorld::get_compute_pool() { + return m_compute_thread_pool.load(); +} } // namespace Cubed \ No newline at end of file diff --git a/src/scene/world_scene.cpp b/src/scene/world_scene.cpp index 84199a5..6ba1c82 100644 --- a/src/scene/world_scene.cpp +++ b/src/scene/world_scene.cpp @@ -129,10 +129,21 @@ void WorldScene::on_enter() { load_config(); m_error_ui.init(); m_client = std::make_shared(m_client_world); + RunMode mode = RunMode::HYBRID; + if (m_argument.direct_enter) { + if (m_argument.ip) { + mode = RunMode::CLIENT_ONLY; + } + } else { + if (!m_scene_manager.world_scene_param().host_game) { + mode = RunMode::CLIENT_ONLY; + } + } + if (m_argument.direct_enter) { if (!m_argument.ip) { ChunkGenerator::init(); - m_server.start_server(*m_argument.port); + m_server.start_server(*m_argument.port, mode); m_client->start("127.0.0.1", *m_argument.port); } else { m_client->start(*m_argument.ip, *m_argument.port); @@ -148,7 +159,7 @@ void WorldScene::on_enter() { } else { ChunkGenerator::init(); } - m_server.start_server(param.port); + m_server.start_server(param.port, mode); } m_client->start(param.ip, param.port); @@ -157,7 +168,8 @@ void WorldScene::on_enter() { // init will send packet try { - m_client_world.init(m_argument.player.value_or("Unknown"), m_client); + m_client_world.init(m_argument.player.value_or("Unknown"), m_client, + mode); Logger::info("World Init Success"); m_camera.camera_init(&m_client_world.get_player());