2 Commits

Author SHA1 Message Date
298fe68552 refactor(client): extract AABB creation and add collision check on block place
Make ClientPlayer::get_aabb and ClientWorld::get_block_aabb static.
Change player size to static constexpr M_SIZE.
Use shared_mutex for concurrent reads of other players.
Check for collision with other players before placing a block.
2026-07-01 15:28:26 +08:00
647f980c66 feat(server-world): use priority thread pool for chunk generation
Introduce PriorityThreadPool to process chunk generation tasks with priorities based on their distance from the player. Closer chunks receive higher priority, improving responsiveness.
2026-07-01 15:13:33 +08:00
6 changed files with 69 additions and 43 deletions

View File

@@ -23,7 +23,7 @@ public:
const ChunkPosSet& get_chunk_pos_set() const;
ChunkPosSet& get_chunk_pos_set();
AABB get_aabb(const glm::vec3& pos) const;
static AABB get_aabb(const glm::vec3& pos);
const glm::vec3& get_front() const;
const Gait& get_gait() const;
const std::optional<LookBlock>& get_look_block_pos() const;
@@ -97,7 +97,7 @@ private:
glm::vec3 m_front{0, 0, -1};
glm::vec3 m_right{0, 0, 0};
glm::vec3 m_size{0.6f, 1.8f, 0.6f};
static constexpr glm::vec3 M_SIZE{0.6f, 1.8f, 0.6f};
Gait m_gait = Gait::WALK;
MoveState m_move_state{};

View File

@@ -71,6 +71,7 @@ public:
void request_exit();
bool is_receive_exit();
int chunk_size() const;
static AABB get_block_aabb(const glm::ivec3& pos);
template <typename Fn>
void register_timer(std::string_view id, TickType threshold, Fn&& f) {
m_timers.emplace(std::piecewise_construct,
@@ -99,7 +100,7 @@ private:
std::mutex m_delete_vbo_mutex;
std::mutex m_delete_vao_mutex;
std::mutex m_other_players_mutex;
mutable std::shared_mutex m_other_players_mutex;
tbb::concurrent_queue<std::unique_ptr<ClientChunk>> m_pending_upload_queue;
tbb::concurrent_queue<ChunkPos> m_dirty_chunk_queue;

View File

@@ -6,6 +6,7 @@
#include "Cubed/gameplay/river_worm.hpp"
#include "Cubed/gameplay/server_chunk.hpp"
#include "Cubed/gameplay/server_player.hpp"
#include "Cubed/tools/priority_thread_pool.hpp"
#include "Cubed/tools/recent_queue.hpp"
#include "Cubed/tools/thread_pool.hpp"
#include "world/block_change.pb.h"
@@ -147,7 +148,7 @@ private:
RecentQueue<std::string> m_need_gen_queue;
std::atomic<std::shared_ptr<ThreadPool>> m_gen_thread_pool;
std::atomic<std::shared_ptr<PriorityThreadPool>> m_gen_thread_pool;
std::atomic<std::shared_ptr<ThreadPool>> m_net_thread_pool;
std::atomic<ChunkLoadStyle> m_chunk_load_style{ChunkLoadStyle::CENTER};
@@ -178,6 +179,9 @@ private:
int
change_pool_threads(std::atomic<std::shared_ptr<ThreadPool>>& thread_pool,
int threads);
int change_pool_threads(
std::atomic<std::shared_ptr<PriorityThreadPool>>& thread_pool,
int threads);
void send_server_stop();
};
} // namespace Cubed

View File

@@ -8,13 +8,13 @@ namespace Cubed {
ClientPlayer::ClientPlayer(ClientWorld& world) : m_world(world) {}
ClientPlayer::~ClientPlayer() {}
AABB ClientPlayer::get_aabb(const glm::vec3& pos) const {
float half_width = m_size.x / 2.0f;
float half_depth = m_size.z / 2.0f;
AABB ClientPlayer::get_aabb(const glm::vec3& pos) {
float half_width = M_SIZE.x / 2.0f;
float half_depth = M_SIZE.z / 2.0f;
glm::vec3 min{pos.x - half_width, pos.y, pos.z - half_depth};
glm::vec3 max{pos.x + half_width, pos.y + m_size.y, pos.z + half_depth};
glm::vec3 max{pos.x + half_width, pos.y + M_SIZE.y, pos.z + half_depth};
return AABB{min, max};
}
@@ -282,15 +282,7 @@ void ClientPlayer::update_lookup_block() {
if (Input::get_input_state().mouse_state.right) {
glm::ivec3 near_pos = m_look_block->pos + m_look_block->normal;
if (!m_world.is_solid(near_pos)) {
auto x = near_pos.x;
auto y = near_pos.y;
auto z = near_pos.z;
AABB block_box = {glm::vec3{static_cast<float>(x),
static_cast<float>(y),
static_cast<float>(z)},
glm::vec3{static_cast<float>(x + 1),
static_cast<float>(y + 1),
static_cast<float>(z + 1)}};
AABB block_box = ClientWorld::get_block_aabb(near_pos);
AABB player_box = get_aabb(get_player_pos());
if (!player_box.intersects(block_box)) {
m_world.report_block_change(near_pos, m_place_block);
@@ -409,13 +401,9 @@ void ClientPlayer::update_x_move(glm::vec3& player_pos) {
for (int x = minx; x <= maxx; ++x) {
for (int y = miny; y <= maxy; ++y) {
for (int z = minz; z <= maxz; ++z) {
if (!m_world.can_pass_block(glm::ivec3{x, y, z})) {
AABB block_box = {glm::vec3{static_cast<float>(x),
static_cast<float>(y),
static_cast<float>(z)},
glm::vec3{static_cast<float>(x + 1),
static_cast<float>(y + 1),
static_cast<float>(z + 1)}};
glm::ivec3 block_pos{x, y, z};
if (!m_world.can_pass_block(block_pos)) {
AABB block_box = ClientWorld::get_block_aabb(block_pos);
if (player_box.intersects(block_box)) {
m_gait = Gait::WALK;
player_pos.x -= move_distance.x;
@@ -443,13 +431,9 @@ void ClientPlayer::update_y_move(glm::vec3& player_pos) {
for (int x = minx; x <= maxx; ++x) {
for (int y = miny; y <= maxy; ++y) {
for (int z = minz; z <= maxz; ++z) {
if (!m_world.can_pass_block(glm::ivec3{x, y, z})) {
AABB block_box = {glm::vec3{static_cast<float>(x),
static_cast<float>(y),
static_cast<float>(z)},
glm::vec3{static_cast<float>(x + 1),
static_cast<float>(y + 1),
static_cast<float>(z + 1)}};
glm::ivec3 block_pos{x, y, z};
if (!m_world.can_pass_block(block_pos)) {
AABB block_box = ClientWorld::get_block_aabb(block_pos);
if (player_box.intersects(block_box)) {
player_pos.y -= move_distance.y;
m_y_speed = 0.0f;
@@ -481,13 +465,9 @@ void ClientPlayer::update_z_move(glm::vec3& player_pos) {
for (int x = minx; x <= maxx; ++x) {
for (int y = miny; y <= maxy; ++y) {
for (int z = minz; z <= maxz; ++z) {
if (!m_world.can_pass_block(glm::ivec3{x, y, z})) {
AABB block_box = {glm::vec3{static_cast<float>(x),
static_cast<float>(y),
static_cast<float>(z)},
glm::vec3{static_cast<float>(x + 1),
static_cast<float>(y + 1),
static_cast<float>(z + 1)}};
glm::ivec3 block_pos{x, y, z};
if (!m_world.can_pass_block(block_pos)) {
AABB block_box = ClientWorld::get_block_aabb(block_pos);
if (player_box.intersects(block_box)) {
m_gait = Gait::WALK;
player_pos.z -= move_distance.z;

View File

@@ -262,6 +262,18 @@ void ClientWorld::push_delete_vao(GLuint vao) {
void ClientWorld::report_block_change(const glm::ivec3& pos,
unsigned id) const {
{
AABB block_box = get_block_aabb(pos);
std::shared_lock lock(m_other_players_mutex);
for (auto& [uuid, player] : m_other_players) {
AABB box = ClientPlayer::get_aabb(player.target_pos);
if (box.intersects(block_box)) {
return;
}
}
}
Arena arena;
auto* req = Arena::Create<BlockChangeReq>(&arena);
req->set_uuid(m_player.get_uuid());
@@ -572,6 +584,16 @@ bool ClientWorld::is_receive_exit() { return m_receive_exit; }
int ClientWorld::chunk_size() const { return m_chunks.size(); }
AABB ClientWorld::get_block_aabb(const glm::ivec3& pos) {
auto x = pos.x;
auto y = pos.y;
auto z = pos.z;
return {glm::vec3{static_cast<float>(x), static_cast<float>(y),
static_cast<float>(z)},
glm::vec3{static_cast<float>(x + 1), static_cast<float>(y + 1),
static_cast<float>(z + 1)}};
}
void ClientWorld::request_exit() {
if (m_receive_exit) {
return;

View File

@@ -330,12 +330,17 @@ void ServerWorld::submit_new_chunks(const std::string& uuid,
[&dist2](const auto& a, const auto& b) {
return dist2(a.first) < dist2(b.first);
});
for (auto& [pos, task] : tasks) {
pool_ptr->enqueue([this, chunk = std::move(task->chunk)]() mutable {
chunk->gen_chunk();
m_finished_queue.push(std::move(chunk));
});
const int CHUNKS_PER_PRIORITY = m_gen_pool_threads;
for (size_t i = 0; i < tasks.size(); ++i) {
int priority = 10 + static_cast<int>(i / CHUNKS_PER_PRIORITY);
auto* task = tasks[i].second;
pool_ptr->enqueue(priority,
[this, chunk = std::move(task->chunk)]() mutable {
chunk->gen_chunk();
m_finished_queue.push(std::move(chunk));
});
}
} break;
}
@@ -777,6 +782,20 @@ int ServerWorld::change_pool_threads(
return used_thread;
}
int ServerWorld::change_pool_threads(
std::atomic<std::shared_ptr<PriorityThreadPool>>& thread_pool,
int threads) {
m_max_threads = std::thread::hardware_concurrency();
if (m_max_threads < 1) {
Logger::warn("Can't Get Max Support Threads, Set Max Threads to 4");
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<PriorityThreadPool>(used_thread));
return used_thread;
}
void ServerWorld::send_server_stop() {
Arena arena;
auto* rsp = Arena::Create<LogoutRsp>(&arena);