feat(gameplay): add task ID to chunk requests to discard stale responses

This commit is contained in:
2026-06-27 14:05:49 +08:00
parent 65429b22d6
commit 1d4aa48230
9 changed files with 58 additions and 16 deletions

View File

@@ -52,6 +52,7 @@ public:
void receive_player_logout(const LogoutRsp& rsp);
int rendering_distance() const;
void rendering_distance(int rendering_distance);
int get_chunk_task_id() const;
void start_client_thread(std::string_view uuid);
void stop_client_thread();
@@ -108,6 +109,7 @@ private:
std::atomic<TickType> m_day_tick{6000};
std::atomic<bool> m_requesting_chunk{false};
std::atomic<bool> m_is_rebuilding{false};
std::atomic<int> m_chunk_task_id{0};
std::shared_ptr<NetworkClient> m_client;
ChunkLoadStyle m_chunk_load_style{ChunkLoadStyle::CENTER};

View File

@@ -22,6 +22,8 @@ public:
void update_pos(float x, float y, float z);
void update_sync_gametick(TickType gametick);
bool is_disconnect(TickType current_gametick) const;
int task_id() const;
void task_id(int id);
private:
static constexpr TickType TIMEOUT = 200;
@@ -32,5 +34,6 @@ private:
ChunkPos m_last_chunk_pos{0, 0};
std::shared_ptr<Session> m_session;
std::atomic<TickType> m_last_gametick{0};
std::atomic<int> m_chunk_task_id{0};
};
} // namespace Cubed

View File

@@ -72,7 +72,7 @@ public:
std::shared_ptr<Session> session);
glm::vec3 get_player_pos(const std::string& uuid) const;
void handle_chunk_req(const std::string& uuid, ChunkPos pos);
void handle_chunk_req(int task_id, const std::string& uuid, ChunkPos pos);
void handle_block_change(const BlockChangeReq& req);
template <typename Fn>
void register_timer(std::string_view id, TickType threshold, Fn&& f) {

View File

@@ -573,6 +573,7 @@ void DevPanel::show_client_world_table_bar() {
if (ImGui::Button("Spawn Point")) {
m_player->set_player_pos({0.0f, 255.0f, 0.0f});
}
ImGui::Text("Chunk Task Id %d", m_app.client_world().get_chunk_task_id());
}
void DevPanel::show_player_tab_item() {

View File

@@ -416,8 +416,10 @@ void ClientWorld::request_chunk() {
}
auto uuid = m_player.get_uuid();
Arena arena;
++m_chunk_task_id;
auto* req = Arena::Create<ChunkDataReq>(&arena);
for (const auto& pos : need_send_pos) {
req->set_task_id(m_chunk_task_id.load());
req->set_uuid(uuid);
auto* p = req->mutable_pos();
p->set_x(pos.x);
@@ -429,6 +431,9 @@ void ClientWorld::request_chunk() {
}
void ClientWorld::receive_chunk(ChunkDataRsp data) {
if (data.task_id() < m_chunk_task_id) {
return;
}
{
std::lock_guard lock(m_chunks_mutex);
@@ -573,6 +578,9 @@ void ClientWorld::rendering_distance(int rendering_distance) {
m_rendering_distance.load());
request_chunk();
}
int ClientWorld::get_chunk_task_id() const { return m_chunk_task_id.load(); }
const std::vector<ChunkRenderSnapshot>& ClientWorld::render_snapshots() const {
return m_render_snapshots;
};

View File

@@ -31,4 +31,7 @@ bool ServerPlayer::is_disconnect(TickType current_gametick) const {
return false;
}
int ServerPlayer::task_id() const { return m_chunk_task_id.load(); }
void ServerPlayer::task_id(int id) { m_chunk_task_id = id; }
} // namespace Cubed

View File

@@ -524,10 +524,32 @@ glm::vec3 ServerWorld::get_player_pos(const std::string& uuid) const {
return it->second.get_pos();
}
void ServerWorld::handle_chunk_req(const std::string& uuid, ChunkPos pos) {
void ServerWorld::handle_chunk_req(int task_id, const std::string& uuid,
ChunkPos pos) {
{
std::shared_lock lock(m_player_mutex);
auto it = m_players.find(uuid);
if (it == m_players.end()) {
return;
}
if (it->second.task_id() < task_id) {
// task_id is an atomic variable, can be operated on directly
it->second.task_id(task_id);
}
}
auto pool = m_gen_thread_pool.load();
pool->enqueue([uuid, pos, this]() {
pool->enqueue([task_id, uuid, pos, this]() {
{
std::shared_lock lock(m_player_mutex);
auto it = m_players.find(uuid);
if (it == m_players.end()) {
return;
}
if (task_id < it->second.task_id()) {
// Old chunk requests are simply discarded
return;
}
}
Arena arean;
ChunkDataRsp* rsp = Arena::Create<ChunkDataRsp>(&arean);
auto* rsq_pos = rsp->mutable_pos();
@@ -578,7 +600,7 @@ void ServerWorld::handle_chunk_req(const std::string& uuid, ChunkPos pos) {
Logger::error("Player {} session not exist", uuid);
return;
}
rsp->set_task_id(task_id);
s->send(make_packet(*rsp));
});
}

View File

@@ -77,7 +77,8 @@ asio::awaitable<void> Session::read_loop() {
// Logger::info("Session: Receive Chunk Data req");
if (decode_packet(*req, body_data, header)) {
m_server_world.handle_chunk_req(
req->uuid(), ChunkPos(req->pos().x(), req->pos().z()));
req->task_id(), req->uuid(),
ChunkPos(req->pos().x(), req->pos().z()));
}
}
if (cmd_id == to_num(PacketEnum::BLOCK_CHANGE_REQ)) {

View File

@@ -3,19 +3,21 @@ syntax = "proto3";
import "common/chunk_pos.proto";
message ChunkDataReq {
string uuid = 1;
ChunkPosNet pos = 2;
int32 task_id = 1;
string uuid = 2;
ChunkPosNet pos = 3;
}
message ChunkDataRsp {
ChunkPosNet pos = 1;
uint32 chunk_seed = 2;
int32 biome_type = 3;
repeated uint32 chunk_blocks = 4 [packed=true];
repeated uint32 neighbor_blocks_1 = 5 [packed=true];
repeated uint32 neighbor_blocks_2 = 6 [packed=true];
repeated uint32 neighbor_blocks_3 = 7 [packed=true];
repeated uint32 neighbor_blocks_4 = 8 [packed=true];
int32 task_id = 1;
ChunkPosNet pos = 2;
uint32 chunk_seed = 3;
int32 biome_type = 4;
repeated uint32 chunk_blocks = 5 [packed=true];
repeated uint32 neighbor_blocks_1 = 6 [packed=true];
repeated uint32 neighbor_blocks_2 = 7 [packed=true];
repeated uint32 neighbor_blocks_3 = 8 [packed=true];
repeated uint32 neighbor_blocks_4 = 9 [packed=true];
}
message S2C_ClearAllChunks {