fix: add thread safety for cave and river path mutexes

This commit is contained in:
2026-06-21 15:56:58 +08:00
parent 9a7fe1bfe9
commit 790f4a5aa4
8 changed files with 80 additions and 48 deletions

View File

@@ -17,11 +17,13 @@ public:
int cave_sum() const;
float& cave_probability();
std::shared_mutex& path_mutex();
private:
CaveHashMap m_paths;
unsigned m_seed = 0;
Random m_random;
float m_cave_probability = 0.035f;
std::shared_mutex m_path_mutex;
};
} // namespace Cubed

View File

@@ -12,6 +12,7 @@ class RiverWorm {
public:
RiverWorm();
~RiverWorm();
RiverHashMap& paths();
void init(unsigned world_seed);
void reload(unsigned world_seed);
@@ -21,12 +22,14 @@ public:
int river_sum() const;
float& river_probability();
std::shared_mutex& paths_mutex();
private:
RiverHashMap m_paths;
unsigned m_seed = 0;
Random m_random;
float m_probability = 0.01f;
std::shared_mutex m_paths_mutex;
};
}; // namespace Cubed

View File

@@ -104,6 +104,7 @@ private:
void submit_new_chunks();
void poll_finished_chunks();
void wait_all_chunk_tasks();
public:
World();

View File

@@ -48,7 +48,14 @@ public:
for (auto& w : m_workers) {
w.request_stop();
}
m_cv.notify_all();
for (auto& w : m_workers) {
if (w.joinable()) {
w.join();
}
}
}
template <typename F> auto enqueue(F&& f) {

View File

@@ -63,5 +63,5 @@ void CaveCarver::cleanup_finished_caves() {
int CaveCarver::cave_sum() const { return m_paths.size(); }
float& CaveCarver::cave_probability() { return m_cave_probability; }
std::shared_mutex& CaveCarver::path_mutex() { return m_path_mutex; }
} // namespace Cubed

View File

@@ -723,19 +723,23 @@ void ChunkGenerator::generate_cave() {
auto& paths = cave_carver.paths();
const auto& chunk_pos = m_chunk.chunk_pos();
auto& blocks = m_chunk.blocks();
{
std::shared_lock lock(cave_carver.path_mutex());
for (auto& [id, path] : paths) {
for (auto& [id, path] : paths) {
carve_worm(path.points(), chunk_pos, [&](int x, int y, int z) -> void {
int idx = Chunk::index(x, y, z);
if (blocks[idx] == 7)
return;
if (y < WORLD_SIZE_Y - 1 && blocks[Chunk::index(x, y + 1, z)] == 7)
return;
blocks[idx] = 0;
});
if (!m_chunk.is_temp_chunk()) {
path.clear_chunk(chunk_pos);
carve_worm(path.points(), chunk_pos,
[&](int x, int y, int z) -> void {
int idx = Chunk::index(x, y, z);
if (blocks[idx] == 7)
return;
if (y < WORLD_SIZE_Y - 1 &&
blocks[Chunk::index(x, y + 1, z)] == 7)
return;
blocks[idx] = 0;
});
if (!m_chunk.is_temp_chunk()) {
path.clear_chunk(chunk_pos);
}
}
}
}
@@ -746,29 +750,33 @@ void ChunkGenerator::generate_river() {
auto& paths = river_worm.paths();
const auto& chunk_pos = m_chunk.chunk_pos();
auto& blocks = m_chunk.blocks();
bool is_river = false;
for (auto& [id, path] : paths) {
if ((m_chunk.biome() == BiomeType::DESERT) ||
(m_chunk.biome() == BiomeType::OCEAN)) {
path.clear_chunk(chunk_pos);
continue;
}
carve_worm(path.points(), chunk_pos, [&](int x, int y, int z) -> void {
int idx = Chunk::index(x, y, z);
if (y > SEA_LEVEL) {
blocks[idx] = 0;
return;
{
std::shared_lock lock(river_worm.paths_mutex());
for (auto& [id, path] : paths) {
if ((m_chunk.biome() == BiomeType::DESERT) ||
(m_chunk.biome() == BiomeType::OCEAN)) {
if (!m_chunk.is_temp_chunk()) {
path.clear_chunk(chunk_pos);
}
continue;
}
is_river = true;
if (blocks[idx] == 0) {
return;
carve_worm(path.points(), chunk_pos,
[&](int x, int y, int z) -> void {
int idx = Chunk::index(x, y, z);
if (y > SEA_LEVEL) {
blocks[idx] = 0;
return;
}
is_river = true;
if (blocks[idx] == 0) {
return;
}
blocks[idx] = 7;
});
if (!m_chunk.is_temp_chunk()) {
path.clear_chunk(chunk_pos);
}
blocks[idx] = 7;
});
if (!m_chunk.is_temp_chunk()) {
path.clear_chunk(chunk_pos);
}
}

View File

@@ -4,7 +4,7 @@
namespace Cubed {
RiverWorm::RiverWorm() {}
RiverWorm::~RiverWorm() {}
RiverWorm::RiverHashMap& RiverWorm::paths() { return m_paths; }
void RiverWorm::init(unsigned world_seed) {
@@ -60,5 +60,6 @@ void RiverWorm::cleanup_finished_rivers() {
int RiverWorm::river_sum() const { return m_paths.size(); }
float& RiverWorm::river_probability() { return m_probability; }
std::shared_mutex& RiverWorm::paths_mutex() { return m_paths_mutex; }
} // namespace Cubed

View File

@@ -21,6 +21,9 @@ World::World() {}
World::~World() {
stop_gen_thread();
stop_server_thread();
wait_all_chunk_tasks();
m_gen_thread_pool.reset();
m_chunks.clear();
{
std::lock_guard lk(m_delete_vbo_mutex);
@@ -38,6 +41,12 @@ World::~World() {
}
}
void World::wait_all_chunk_tasks() {
for (auto& [pos, task] : new_chunks) {
task.future.get();
}
}
bool World::can_move(const AABB& player_box) const { return true; }
const std::optional<LookBlock>&
@@ -158,21 +167,26 @@ void World::gen_chunks_internal() {
new_chunks.emplace(pos, Chunk(*this, pos));
}
auto t1 = system_clock::now();
parallel_do(*m_gen_thread_pool, temp_neighbor.begin(), temp_neighbor.end(),
m_gen_thread_pool->thread_sum(),
[this](std::pair<ChunkPos, Chunk>& new_chunk) {
auto& [pos, chunk] = new_chunk;
chunk.gen_phase_one();
m_cave_carcer.try_to_add_path(pos, chunk.seed());
m_river_worm.try_to_add_path(pos, chunk.seed());
});
{
std::scoped_lock lock{m_cave_carcer.path_mutex(),
m_river_worm.paths_mutex()};
parallel_do(*m_gen_thread_pool, temp_neighbor.begin(),
temp_neighbor.end(), m_gen_thread_pool->thread_sum(),
[this](std::pair<ChunkPos, Chunk>& new_chunk) {
auto& [pos, chunk] = new_chunk;
chunk.gen_phase_one();
m_cave_carcer.try_to_add_path(pos, chunk.seed());
m_river_worm.try_to_add_path(pos, chunk.seed());
});
m_cave_carcer.cleanup_finished_caves();
m_river_worm.cleanup_finished_rivers();
}
auto t2 = system_clock::now();
Logger::info("Temp Neighbor Add Path Consum {}",
duration_cast<milliseconds>(t2 - t1));
m_chunk_gen_fraction = 0.9f;
m_cave_carcer.cleanup_finished_caves();
m_river_worm.cleanup_finished_rivers();
m_chunk_gen_fraction = 1.0f;
submit_new_chunks();
m_chunk_gen_finished = true;
@@ -209,10 +223,6 @@ void World::compute_required_chunks(ChunkPosSet& required_chunks,
for (int dz = -radius; dz <= radius; ++dz) {
if (dx * dx + dz * dz <= r2) {
ChunkPos pos{chunk_x + dx, chunk_z + dz};
auto it = required_chunks.find(pos);
if (it != required_chunks.end()) {
continue;
}
temp_neighbor.emplace_back(pos, Chunk(*this, pos));
}
}