refactor: config (#30)

* refactor(config): replace singleton with dependency injection

* refactor(client_player): return ChunkPosSet by value in non-const getter

Copy the internal set under the mutex lock to avoid exposing a mutable reference that could be used unsafely after the lock is released. This fixes a potential data race when the caller holds the returned reference beyond the critical section. Also update the caller in `client_world` to remove the now-unnecessary `std::move`.

* fix(gameplay): prevent use-after-free in pending delete queues during destruction

* refactor(config): separate game and server configuration with explicit paths
This commit is contained in:
zhenyan121
2026-07-11 09:22:40 +08:00
committed by GitHub
parent 913809a5f0
commit 03259f323c
27 changed files with 285 additions and 368 deletions

View File

@@ -121,9 +121,8 @@ void ClientPlayer::change_mode(GameMode mode) {
}
}
void ClientPlayer::hot_reload() {
auto& config = Config::get();
m_sensitivity =
static_cast<float>(config.get<double>("player.mouse_sensitivity"));
auto& config = m_world.get_config();
m_sensitivity = config.get("player.mouse_sensitivity", 0.15f);
}
void ClientPlayer::set_player_pos(const glm::vec3& pos) { m_player_pos = pos; }
@@ -562,7 +561,7 @@ const ClientPlayer::ChunkPosSet& ClientPlayer::get_chunk_pos_set() const {
return m_player_chunk_pos_set;
}
ClientPlayer::ChunkPosSet& ClientPlayer::get_chunk_pos_set() {
ClientPlayer::ChunkPosSet ClientPlayer::get_chunk_pos_set() {
std::lock_guard lock(m_chunk_pos_mutex);
return m_player_chunk_pos_set;
}

View File

@@ -21,15 +21,26 @@ struct ChunkRenderData {
};
} // namespace
ClientWorld::ClientWorld(AudioEngine& auido)
: m_player(*this), m_audio(auido) {}
ClientWorld::ClientWorld(AudioEngine& auido, Config& config)
: m_player(*this), m_audio(auido), m_config(config) {}
ClientWorld::~ClientWorld() {
m_client->close();
stop_client_thread();
stop_thread_pool();
// Must first clean up and push the generated chunk data into
// m_pending_delete_vbo and m_pending_delete_vao; cannot delete them in the
// destructor, otherwise it will cause leaks and use-after-free.
m_dirty_chunk_queue.clear();
m_pending_upload_queue.clear();
m_chunks.clear();
if (m_is_pending_delete_queue_free.exchange(true)) {
return;
}
{
std::lock_guard lk(m_delete_vbo_mutex);
m_pending_delete_vbo.clear();
@@ -262,10 +273,18 @@ void ClientWorld::set_block(const glm::ivec3& block_pos, unsigned id) {
}
}
void ClientWorld::push_delete_vbo(std::unique_ptr<VertexBuffer>& vbo) {
if (m_is_pending_delete_queue_free) {
Logger::error("Push delete vbo Use After Free");
return;
}
std::lock_guard lk(m_delete_vbo_mutex);
m_pending_delete_vbo.push_back(std::move(vbo));
}
void ClientWorld::push_delete_vao(std::unique_ptr<VertexArray>& vao) {
if (m_is_pending_delete_queue_free) {
Logger::error("Push delete vao Use After Free");
return;
}
std::lock_guard lk(m_delete_vao_mutex);
m_pending_delete_vao.push_back(std::move(vao));
}
@@ -504,8 +523,7 @@ void ClientWorld::change_pool_threads(int threads) {
}
void ClientWorld::hot_reload() {
auto& config = Config::get();
int dist = config.get<int>("world.rendering_distance");
int dist = m_config.get<int>("world.rendering_distance", PRE_LOAD_DISTANCE);
Logger::info("Get Config Randering dist {}", dist);
m_rendering_distance = dist <= MAX_DISTANCE ? dist : MAX_DISTANCE;
request_chunk();
@@ -591,7 +609,7 @@ void ClientWorld::request_chunk() {
}
}
ChunkPosSet old = std::move(m_player.get_chunk_pos_set());
ChunkPosSet old = m_player.get_chunk_pos_set();
m_player.update_chunk_set(required_chunks);
ChunkPosVector need_send_pos;
@@ -699,6 +717,7 @@ AABB ClientWorld::get_block_aabb(const glm::ivec3& pos) {
}
AudioEngine& ClientWorld::get_audio() { return m_audio; }
Config& ClientWorld::get_config() { return m_config; }
void ClientWorld::request_exit() {
if (m_receive_exit) {

View File

@@ -4,7 +4,10 @@
using asio::ip::tcp;
namespace Cubed {
NetworkServer::NetworkServer(int port) : m_port(port) {}
NetworkServer::NetworkServer()
: m_config(ASSETS_PATH "server-config.toml"), m_world(m_config) {
m_port = m_config.get("port", 25530);
}
NetworkServer::~NetworkServer() { stop(); }
@@ -84,6 +87,11 @@ void NetworkServer::net_run() {
void NetworkServer::start_server(int port) {
m_port = port;
m_config.set("port", m_port);
start_server();
}
void NetworkServer::start_server() {
m_world.init_world();
net_run();
m_started = true;

View File

@@ -1,6 +1,5 @@
#include "Cubed/gameplay/server_world.hpp"
#include "Cubed/config.hpp"
#include "Cubed/gameplay/packet.hpp"
#include "Cubed/gameplay/session.hpp"
#include "Cubed/tools/cubed_assert.hpp"
@@ -14,7 +13,7 @@ using namespace std::chrono_literals;
using namespace google::protobuf;
namespace Cubed {
ServerWorld::ServerWorld() {}
ServerWorld::ServerWorld(Config& config) : m_config(config) {}
ServerWorld::~ServerWorld() { stop(); }
@@ -501,8 +500,7 @@ bool ServerWorld::set_block(const glm::ivec3& block_pos, unsigned id) {
}
void ServerWorld::hot_reload() {
auto& config = Config::get();
int dist = config.get<int>("world.rendering_distance");
int dist = m_config.get("server_distance", 24);
m_rendering_distance = dist <= MAX_DISTANCE ? dist : MAX_DISTANCE;
}