mirror of
https://github.com/zhenyan121/Cubed.git
synced 2026-08-08 17:57:02 +08:00
refactor(server_world): replace future-based chunk generation with queue
Remove `std::future` per-chunk tracking and poll_finished_chunks(). Instead, generation tasks push completed chunks to a concurrent queue consumed during update. This eliminates wait_all_chunk_tasks() and simplifies synchronization.
This commit is contained in:
@@ -11,7 +11,6 @@
|
|||||||
#include "world/block_change.pb.h"
|
#include "world/block_change.pb.h"
|
||||||
|
|
||||||
#include <absl/container/flat_hash_set.h>
|
#include <absl/container/flat_hash_set.h>
|
||||||
#include <future>
|
|
||||||
#include <shared_mutex>
|
#include <shared_mutex>
|
||||||
#include <tbb/concurrent_hash_map.h>
|
#include <tbb/concurrent_hash_map.h>
|
||||||
#include <tbb/concurrent_queue.h>
|
#include <tbb/concurrent_queue.h>
|
||||||
@@ -101,10 +100,6 @@ private:
|
|||||||
ChunkPos pos;
|
ChunkPos pos;
|
||||||
};
|
};
|
||||||
struct PendingChunk {
|
struct PendingChunk {
|
||||||
std::unique_ptr<ServerChunk> chunk;
|
|
||||||
std::future<void> future;
|
|
||||||
};
|
|
||||||
struct FinishedChunk {
|
|
||||||
ChunkPos pos;
|
ChunkPos pos;
|
||||||
std::unique_ptr<ServerChunk> chunk;
|
std::unique_ptr<ServerChunk> chunk;
|
||||||
};
|
};
|
||||||
@@ -112,8 +107,7 @@ private:
|
|||||||
using ChunkHashMap =
|
using ChunkHashMap =
|
||||||
tbb::concurrent_hash_map<ChunkPos, ChunkEntity, ChunkPos::TBBHash>;
|
tbb::concurrent_hash_map<ChunkPos, ChunkEntity, ChunkPos::TBBHash>;
|
||||||
using PlayerHashMap = std::unordered_map<std::string, ServerPlayer>;
|
using PlayerHashMap = std::unordered_map<std::string, ServerPlayer>;
|
||||||
using PendingChunkHashMap =
|
using NewChunkVector = std::vector<PendingChunk>;
|
||||||
std::unordered_map<ChunkPos, PendingChunk, ChunkPos::Hash>;
|
|
||||||
using ChunkPosSet = absl::flat_hash_set<ChunkPos, ChunkPos::Hash>;
|
using ChunkPosSet = absl::flat_hash_set<ChunkPos, ChunkPos::Hash>;
|
||||||
using PlayerUUIDMap = tbb::concurrent_hash_map<std::string, std::string>;
|
using PlayerUUIDMap = tbb::concurrent_hash_map<std::string, std::string>;
|
||||||
|
|
||||||
@@ -125,8 +119,7 @@ private:
|
|||||||
// key = uuid
|
// key = uuid
|
||||||
PlayerHashMap m_players;
|
PlayerHashMap m_players;
|
||||||
ChunkHashMap m_chunks;
|
ChunkHashMap m_chunks;
|
||||||
PendingChunkHashMap m_new_chunks;
|
NewChunkVector m_new_chunks;
|
||||||
std::vector<FinishedChunk> m_new_finished_chunk;
|
|
||||||
|
|
||||||
CaveCarver m_cave_carcer;
|
CaveCarver m_cave_carcer;
|
||||||
RiverWorm m_river_worm;
|
RiverWorm m_river_worm;
|
||||||
@@ -167,6 +160,7 @@ private:
|
|||||||
|
|
||||||
tbb::concurrent_unordered_map<std::string, Timer> m_timers;
|
tbb::concurrent_unordered_map<std::string, Timer> m_timers;
|
||||||
tbb::concurrent_queue<PendingRequest> m_waiting_chunk_requests;
|
tbb::concurrent_queue<PendingRequest> m_waiting_chunk_requests;
|
||||||
|
tbb::concurrent_queue<std::unique_ptr<ServerChunk>> m_finished_queue;
|
||||||
|
|
||||||
void init_chunks();
|
void init_chunks();
|
||||||
|
|
||||||
@@ -177,8 +171,7 @@ private:
|
|||||||
void sync_and_collect_missing_chunks(std::vector<ChunkPos>&,
|
void sync_and_collect_missing_chunks(std::vector<ChunkPos>&,
|
||||||
const ChunkPosSet&);
|
const ChunkPosSet&);
|
||||||
void submit_new_chunks(const std::string& uuid);
|
void submit_new_chunks(const std::string& uuid);
|
||||||
void poll_finished_chunks();
|
// void wait_all_chunk_tasks();
|
||||||
void wait_all_chunk_tasks();
|
|
||||||
|
|
||||||
void update_ref_count(const ChunkPosSet& old, const ChunkPosSet& now);
|
void update_ref_count(const ChunkPosSet& old, const ChunkPosSet& now);
|
||||||
|
|
||||||
|
|||||||
@@ -28,28 +28,13 @@ void ServerWorld::stop() {
|
|||||||
send_server_stop();
|
send_server_stop();
|
||||||
stop_gen_thread();
|
stop_gen_thread();
|
||||||
stop_server_thread();
|
stop_server_thread();
|
||||||
wait_all_chunk_tasks();
|
// wait_all_chunk_tasks();
|
||||||
stop_thread_pool();
|
stop_thread_pool();
|
||||||
|
|
||||||
|
m_finished_queue.clear();
|
||||||
m_chunks.clear();
|
m_chunks.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerWorld::wait_all_chunk_tasks() {
|
|
||||||
std::lock_guard lock(m_new_chunk_mutex);
|
|
||||||
for (auto& [pos, task] : m_new_chunks) {
|
|
||||||
if (task.future.valid()) {
|
|
||||||
try {
|
|
||||||
task.future.get();
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
Logger::error("Chunk generation failed: {}", e.what());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Logger::error("Chunk {} {} not started gen task", pos.x, pos.z);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ServerWorld::update_ref_count(const ChunkPosSet& old,
|
void ServerWorld::update_ref_count(const ChunkPosSet& old,
|
||||||
const ChunkPosSet& now) {
|
const ChunkPosSet& now) {
|
||||||
|
|
||||||
@@ -265,7 +250,7 @@ void ServerWorld::gen_chunks_internal(const std::string& uuid) {
|
|||||||
// Create new chunk
|
// Create new chunk
|
||||||
std::lock_guard lock(m_new_chunk_mutex);
|
std::lock_guard lock(m_new_chunk_mutex);
|
||||||
for (auto& pos : need_gen_chunks_pos) {
|
for (auto& pos : need_gen_chunks_pos) {
|
||||||
m_new_chunks.emplace(
|
m_new_chunks.emplace_back(
|
||||||
pos, std::make_unique<ServerChunk>(ServerChunk(*this, pos)));
|
pos, std::make_unique<ServerChunk>(ServerChunk(*this, pos)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -321,25 +306,24 @@ void ServerWorld::submit_new_chunks(const std::string& uuid) {
|
|||||||
switch (m_chunk_load_style) {
|
switch (m_chunk_load_style) {
|
||||||
case RANDOM:
|
case RANDOM:
|
||||||
// Enqueue directly in random order
|
// Enqueue directly in random order
|
||||||
for (auto& [pos, task] : m_new_chunks) {
|
for (auto& task : m_new_chunks) {
|
||||||
if (!task.future.valid()) {
|
|
||||||
task.future =
|
pool_ptr->enqueue([&task, this]() {
|
||||||
pool_ptr->enqueue([&task]() { task.chunk->gen_chunk(); });
|
std::unique_ptr<ServerChunk> chunk{std::move(task.chunk)};
|
||||||
}
|
chunk->gen_chunk();
|
||||||
|
m_finished_queue.push(std::move(chunk));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CENTER: {
|
case CENTER: {
|
||||||
std::vector<std::pair<ChunkPos, PendingChunk*>> tasks;
|
std::vector<std::pair<ChunkPos, PendingChunk*>> tasks;
|
||||||
for (auto& [pos, task] : m_new_chunks) {
|
for (auto& task : m_new_chunks) {
|
||||||
if (!task.future.valid()) {
|
|
||||||
tasks.emplace_back(pos, &task);
|
tasks.emplace_back(task.pos, &task);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
glm::vec3 player_pos = get_player_pos(uuid);
|
glm::vec3 player_pos = get_player_pos(uuid);
|
||||||
|
ChunkPos player_chunk_pos = get_chunk_pos(player_pos.x, player_pos.z);
|
||||||
auto dist2 = [player_pos](ChunkPos chunk_pos) {
|
auto dist2 = [player_chunk_pos](ChunkPos chunk_pos) {
|
||||||
ChunkPos player_chunk_pos =
|
|
||||||
get_chunk_pos(player_pos.x, player_pos.z);
|
|
||||||
float dx = player_chunk_pos.x - chunk_pos.x;
|
float dx = player_chunk_pos.x - chunk_pos.x;
|
||||||
float dz = player_chunk_pos.z - chunk_pos.z;
|
float dz = player_chunk_pos.z - chunk_pos.z;
|
||||||
return dx * dx + dz * dz;
|
return dx * dx + dz * dz;
|
||||||
@@ -350,39 +334,16 @@ void ServerWorld::submit_new_chunks(const std::string& uuid) {
|
|||||||
return dist2(a.first) < dist2(b.first);
|
return dist2(a.first) < dist2(b.first);
|
||||||
});
|
});
|
||||||
for (auto& [pos, task] : tasks) {
|
for (auto& [pos, task] : tasks) {
|
||||||
if (!task->future.valid()) {
|
|
||||||
task->future =
|
|
||||||
pool_ptr->enqueue([task]() { task->chunk->gen_chunk(); });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ServerWorld::poll_finished_chunks() {
|
pool_ptr->enqueue([this, chunk = std::move(task->chunk)]() mutable {
|
||||||
m_new_finished_chunk.clear();
|
chunk->gen_chunk();
|
||||||
std::lock_guard lock(m_new_chunk_mutex);
|
m_finished_queue.push(std::move(chunk));
|
||||||
std::erase_if(
|
|
||||||
m_new_chunks, [&](std::pair<const ChunkPos, PendingChunk>& pair) {
|
|
||||||
auto& pending = pair.second;
|
|
||||||
if (!pending.future.valid()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (pending.future.wait_for(0ms) != std::future_status::ready) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
pending.future.get();
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
Logger::error("Chunk generation failed: {}", e.what());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Spawn complete, move away
|
|
||||||
m_new_finished_chunk.emplace_back(pair.first,
|
|
||||||
std::move(pending.chunk));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_new_chunks.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerWorld::start_gen_thread() {
|
void ServerWorld::start_gen_thread() {
|
||||||
@@ -554,7 +515,6 @@ void ServerWorld::rebuild_world() {
|
|||||||
m_river_worm.reload(ChunkGenerator::seed());
|
m_river_worm.reload(ChunkGenerator::seed());
|
||||||
|
|
||||||
m_chunks.clear();
|
m_chunks.clear();
|
||||||
m_new_finished_chunk.clear();
|
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard lock(m_new_chunk_mutex);
|
std::lock_guard lock(m_new_chunk_mutex);
|
||||||
@@ -579,19 +539,24 @@ void ServerWorld::rebuild_world() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ServerWorld::update() {
|
void ServerWorld::update() {
|
||||||
poll_finished_chunks();
|
// poll_finished_chunks();
|
||||||
{
|
{
|
||||||
bool consumed = false;
|
bool consumed = false;
|
||||||
|
std::unique_ptr<ServerChunk> chunk;
|
||||||
for (auto& x : m_new_finished_chunk) {
|
while (m_finished_queue.try_pop(chunk)) {
|
||||||
|
if (!chunk) {
|
||||||
|
Logger::error("Finished Queue has nullptr Chunk");
|
||||||
|
return;
|
||||||
|
}
|
||||||
chunk_acc acc;
|
chunk_acc acc;
|
||||||
if (!m_chunks.find(acc, x.pos)) {
|
auto pos = chunk->get_chunk_pos();
|
||||||
|
if (!m_chunks.find(acc, pos)) {
|
||||||
Logger::error(
|
Logger::error(
|
||||||
"New Chunk {} {} not Find, don't move to m_chunks", x.pos.x,
|
"New Chunk {} {} not Find, don't move to m_chunks", pos.x,
|
||||||
x.pos.z);
|
pos.z);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
acc->second.chunk = std::move(x.chunk);
|
acc->second.chunk = std::move(chunk);
|
||||||
acc->second.state = ChunkState::READY;
|
acc->second.state = ChunkState::READY;
|
||||||
consumed = true;
|
consumed = true;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user