mirror of
https://github.com/zhenyan121/Cubed.git
synced 2026-08-09 02:07:04 +08:00
feat(networking): add priority and sequence ordering to packet send queues
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
#include "Cubed/gameplay/packet.hpp"
|
#include "Cubed/gameplay/packet.hpp"
|
||||||
|
|
||||||
#include <asio.hpp>
|
#include <asio.hpp>
|
||||||
|
#include <queue>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
namespace Cubed {
|
namespace Cubed {
|
||||||
@@ -14,28 +15,50 @@ public:
|
|||||||
~NetworkClient();
|
~NetworkClient();
|
||||||
void close();
|
void close();
|
||||||
void stop();
|
void stop();
|
||||||
void send(Packet packet);
|
void send(Packet packet, int priority = 10);
|
||||||
void start(std::string ip, int port = 25530);
|
void start(std::string ip, int port = 25530);
|
||||||
bool is_connected() const;
|
bool is_connected() const;
|
||||||
bool is_connect_error() const;
|
bool is_connect_error() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct Task {
|
||||||
|
int priority = 10;
|
||||||
|
std::uint64_t sequence = 0;
|
||||||
|
Packet packet;
|
||||||
|
Task(int p, std::uint64_t seq, Packet pac)
|
||||||
|
: priority(p), sequence(seq), packet(std::move(pac)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TaskCompare {
|
||||||
|
bool operator()(const Task& a, const Task& b) const {
|
||||||
|
|
||||||
|
if (a.priority != b.priority) {
|
||||||
|
return a.priority > b.priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
return a.sequence > b.sequence;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
asio::io_context m_io;
|
asio::io_context m_io;
|
||||||
|
|
||||||
std::thread m_net_thread;
|
std::thread m_net_thread;
|
||||||
static constexpr uint32_t MAX_PACKET_SIZE = 4 * 1024 * 1024;
|
static constexpr uint32_t MAX_PACKET_SIZE = 4 * 1024 * 1024;
|
||||||
tcp::socket m_socket;
|
tcp::socket m_socket;
|
||||||
std::vector<char> m_read_buffer;
|
std::vector<char> m_read_buffer;
|
||||||
std::deque<Packet> m_write_queue;
|
|
||||||
asio::strand<asio::io_context::executor_type> m_strand;
|
|
||||||
asio::awaitable<void> connect(std::string ip, int port);
|
|
||||||
asio::awaitable<void> read_loop();
|
|
||||||
|
|
||||||
|
std::priority_queue<Task, std::vector<Task>, TaskCompare> m_write_queue;
|
||||||
|
|
||||||
|
asio::strand<asio::io_context::executor_type> m_strand;
|
||||||
std::atomic<bool> m_closed{false};
|
std::atomic<bool> m_closed{false};
|
||||||
std::atomic<bool> m_connected{false};
|
std::atomic<bool> m_connected{false};
|
||||||
std::atomic<bool> m_connect_error{false};
|
std::atomic<bool> m_connect_error{false};
|
||||||
// ClientWorld is managed by App
|
// ClientWorld is managed by App
|
||||||
ClientWorld& m_world;
|
ClientWorld& m_world;
|
||||||
|
std::atomic_uint64_t m_sequence{0};
|
||||||
|
|
||||||
|
asio::awaitable<void> connect(std::string ip, int port);
|
||||||
|
asio::awaitable<void> read_loop();
|
||||||
|
|
||||||
void do_write();
|
void do_write();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
#include "Cubed/gameplay/packet.hpp"
|
#include "Cubed/gameplay/packet.hpp"
|
||||||
|
|
||||||
#include <asio.hpp>
|
#include <asio.hpp>
|
||||||
#include <deque>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <queue>
|
||||||
#include <string>
|
#include <string>
|
||||||
namespace Cubed {
|
namespace Cubed {
|
||||||
|
|
||||||
@@ -17,20 +17,44 @@ public:
|
|||||||
asio::io_context& io);
|
asio::io_context& io);
|
||||||
~Session();
|
~Session();
|
||||||
void start();
|
void start();
|
||||||
void send(Packet packet);
|
void send(Packet packet, int priority = 10);
|
||||||
|
|
||||||
void close();
|
void close();
|
||||||
const std::string& uuid() const;
|
const std::string& uuid() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct Task {
|
||||||
|
int priority = 10;
|
||||||
|
std::uint64_t sequence = 0;
|
||||||
|
Packet packet;
|
||||||
|
Task(int p, std::uint64_t seq, Packet pac)
|
||||||
|
: priority(p), sequence(seq), packet(std::move(pac)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TaskCompare {
|
||||||
|
bool operator()(const Task& a, const Task& b) const {
|
||||||
|
|
||||||
|
if (a.priority != b.priority) {
|
||||||
|
return a.priority > b.priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
return a.sequence > b.sequence;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static constexpr uint32_t MAX_PACKET_SIZE = 4 * 1024 * 1024;
|
static constexpr uint32_t MAX_PACKET_SIZE = 4 * 1024 * 1024;
|
||||||
tcp::socket m_socket;
|
tcp::socket m_socket;
|
||||||
std::vector<char> m_read_buffer;
|
std::vector<char> m_read_buffer;
|
||||||
std::deque<Packet> m_write_queue;
|
std::priority_queue<Task, std::vector<Task>, TaskCompare> m_write_queue;
|
||||||
asio::strand<asio::io_context::executor_type> m_strand;
|
asio::strand<asio::io_context::executor_type> m_strand;
|
||||||
std::string m_uuid;
|
std::string m_uuid;
|
||||||
ServerWorld& m_server_world;
|
ServerWorld& m_server_world;
|
||||||
asio::awaitable<void> read_loop();
|
|
||||||
std::atomic<bool> m_closed{false};
|
std::atomic<bool> m_closed{false};
|
||||||
|
|
||||||
|
std::atomic_uint64_t m_sequence{0};
|
||||||
|
|
||||||
|
asio::awaitable<void> read_loop();
|
||||||
|
|
||||||
void do_write();
|
void do_write();
|
||||||
};
|
};
|
||||||
} // namespace Cubed
|
} // namespace Cubed
|
||||||
|
|||||||
@@ -270,7 +270,7 @@ void ClientWorld::report_block_change(const glm::ivec3& pos,
|
|||||||
p->set_x(pos.x);
|
p->set_x(pos.x);
|
||||||
p->set_y(pos.y);
|
p->set_y(pos.y);
|
||||||
p->set_z(pos.z);
|
p->set_z(pos.z);
|
||||||
m_client->send(make_packet(*req));
|
m_client->send(make_packet(*req), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientWorld::receive_block_change(const BlockChangeRsp& rsp) {
|
void ClientWorld::receive_block_change(const BlockChangeRsp& rsp) {
|
||||||
@@ -336,7 +336,7 @@ void ClientWorld::init(std::string_view player_name,
|
|||||||
start_thread_pool();
|
start_thread_pool();
|
||||||
// request login
|
// request login
|
||||||
Logger::info("Send Login Request");
|
Logger::info("Send Login Request");
|
||||||
m_client->send(make_packet(req));
|
m_client->send(make_packet(req), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientWorld::start_client_thread(std::string_view uuid) {
|
void ClientWorld::start_client_thread(std::string_view uuid) {
|
||||||
@@ -427,7 +427,7 @@ void ClientWorld::report_player_pos() {
|
|||||||
v3->set_x(player_pos.x);
|
v3->set_x(player_pos.x);
|
||||||
v3->set_y(player_pos.y);
|
v3->set_y(player_pos.y);
|
||||||
v3->set_z(player_pos.z);
|
v3->set_z(player_pos.z);
|
||||||
m_client->send(make_packet(*pos));
|
m_client->send(make_packet(*pos), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientWorld::update_chunk(const ChunkPosSet& old, const ChunkPosSet& now) {
|
void ClientWorld::update_chunk(const ChunkPosSet& old, const ChunkPosSet& now) {
|
||||||
|
|||||||
@@ -142,14 +142,15 @@ asio::awaitable<void> NetworkClient::read_loop() {
|
|||||||
co_return;
|
co_return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkClient::send(std::shared_ptr<std::vector<uint8_t>> packet) {
|
void NetworkClient::send(Packet packet, int priority) {
|
||||||
if (m_closed.load()) {
|
if (m_closed.load()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
asio::post(m_strand, [self = shared_from_this(),
|
asio::post(m_strand, [self = shared_from_this(), packet = std::move(packet),
|
||||||
packet = std::move(packet)]() mutable {
|
priority]() mutable {
|
||||||
bool idle = self->m_write_queue.empty();
|
bool idle = self->m_write_queue.empty();
|
||||||
self->m_write_queue.emplace_back(std::move(packet));
|
self->m_write_queue.emplace(priority, self->m_sequence++,
|
||||||
|
std::move(packet));
|
||||||
if (idle) {
|
if (idle) {
|
||||||
self->do_write();
|
self->do_write();
|
||||||
}
|
}
|
||||||
@@ -162,15 +163,16 @@ void NetworkClient::do_write() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto self = shared_from_this();
|
auto self = shared_from_this();
|
||||||
|
auto packet = std::move(m_write_queue.top().packet);
|
||||||
asio::async_write(
|
asio::async_write(
|
||||||
m_socket, asio::buffer(*(m_write_queue.front())),
|
m_socket, asio::buffer(*packet),
|
||||||
asio::bind_executor(m_strand, [self](std::error_code ec, size_t) {
|
asio::bind_executor(m_strand, [self](std::error_code ec, size_t) {
|
||||||
if (ec) {
|
if (ec) {
|
||||||
Logger::warn("Write Ec {}", ec.message());
|
Logger::warn("Write Ec {}", ec.message());
|
||||||
self->close();
|
self->close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self->m_write_queue.pop_front();
|
self->m_write_queue.pop();
|
||||||
if (!self->m_write_queue.empty()) {
|
if (!self->m_write_queue.empty()) {
|
||||||
self->do_write();
|
self->do_write();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ void ServerWorld::send_time() {
|
|||||||
rsp->set_game_tick(m_game_ticks);
|
rsp->set_game_tick(m_game_ticks);
|
||||||
|
|
||||||
for (auto& [uuid, player] : m_players) {
|
for (auto& [uuid, player] : m_players) {
|
||||||
player.get_session()->send(make_packet(*rsp));
|
player.get_session()->send(make_packet(*rsp), 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -573,7 +573,7 @@ void ServerWorld::sync_player_pos(const std::string& uuid, float x, float y,
|
|||||||
pos->set_x(x);
|
pos->set_x(x);
|
||||||
pos->set_y(y);
|
pos->set_y(y);
|
||||||
pos->set_z(z);
|
pos->set_z(z);
|
||||||
session->send(make_packet(*rsp));
|
session->send(make_packet(*rsp), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -597,7 +597,7 @@ void ServerWorld::handle_player_login(const std::string& name,
|
|||||||
if (!sucess) {
|
if (!sucess) {
|
||||||
auto* rsp = Arena::Create<LoginRsp>(&arena);
|
auto* rsp = Arena::Create<LoginRsp>(&arena);
|
||||||
rsp->set_success(false);
|
rsp->set_success(false);
|
||||||
session->send(make_packet(*rsp));
|
session->send(make_packet(*rsp), 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -621,7 +621,7 @@ void ServerWorld::handle_player_login(const std::string& name,
|
|||||||
auto* rsp = Arena::Create<LoginRsp>(&arena);
|
auto* rsp = Arena::Create<LoginRsp>(&arena);
|
||||||
rsp->set_success(true);
|
rsp->set_success(true);
|
||||||
rsp->set_uuid(uuid);
|
rsp->set_uuid(uuid);
|
||||||
session->send(make_packet(*rsp));
|
session->send(make_packet(*rsp), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerWorld::handle_player_exit(const std::string& uuid) {
|
void ServerWorld::handle_player_exit(const std::string& uuid) {
|
||||||
@@ -649,7 +649,7 @@ void ServerWorld::handle_player_exit(const std::string& uuid) {
|
|||||||
auto* rsp = Arena::Create<LogoutRsp>(&arena);
|
auto* rsp = Arena::Create<LogoutRsp>(&arena);
|
||||||
rsp->set_uuid(uuid);
|
rsp->set_uuid(uuid);
|
||||||
rsp->set_server_stop(false);
|
rsp->set_server_stop(false);
|
||||||
exit_session->send(make_packet(*rsp));
|
exit_session->send(make_packet(*rsp), 0);
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Session>> sessions;
|
std::vector<std::shared_ptr<Session>> sessions;
|
||||||
{
|
{
|
||||||
@@ -661,7 +661,7 @@ void ServerWorld::handle_player_exit(const std::string& uuid) {
|
|||||||
|
|
||||||
for (auto& s : sessions) {
|
for (auto& s : sessions) {
|
||||||
if (s) {
|
if (s) {
|
||||||
s->send(make_packet(*rsp));
|
s->send(make_packet(*rsp), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -723,7 +723,7 @@ void ServerWorld::handle_block_change(const BlockChangeReq& req) {
|
|||||||
|
|
||||||
for (auto& x : sessions) {
|
for (auto& x : sessions) {
|
||||||
if (x) {
|
if (x) {
|
||||||
x->send(make_packet(*rsp));
|
x->send(make_packet(*rsp), 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -783,7 +783,7 @@ void ServerWorld::send_server_stop() {
|
|||||||
rsp->set_server_stop(true);
|
rsp->set_server_stop(true);
|
||||||
std::shared_lock lock(m_player_mutex);
|
std::shared_lock lock(m_player_mutex);
|
||||||
for (auto& [uuid, player] : m_players) {
|
for (auto& [uuid, player] : m_players) {
|
||||||
player.get_session()->send(make_packet(*rsp));
|
player.get_session()->send(make_packet(*rsp), 0);
|
||||||
}
|
}
|
||||||
Logger::info("Send Server Mesaage Success");
|
Logger::info("Send Server Mesaage Success");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,11 +21,12 @@ void Session::start() {
|
|||||||
asio::detached);
|
asio::detached);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::send(std::shared_ptr<std::vector<uint8_t>> packet) {
|
void Session::send(std::shared_ptr<std::vector<uint8_t>> packet, int priority) {
|
||||||
asio::post(m_strand, [self = shared_from_this(),
|
asio::post(m_strand, [self = shared_from_this(), packet = std::move(packet),
|
||||||
packet = std::move(packet)]() mutable {
|
priority]() mutable {
|
||||||
bool idle = self->m_write_queue.empty();
|
bool idle = self->m_write_queue.empty();
|
||||||
self->m_write_queue.emplace_back(std::move(packet));
|
self->m_write_queue.emplace(priority, self->m_sequence++,
|
||||||
|
std::move(packet));
|
||||||
if (idle) {
|
if (idle) {
|
||||||
self->do_write();
|
self->do_write();
|
||||||
}
|
}
|
||||||
@@ -118,15 +119,16 @@ asio::awaitable<void> Session::read_loop() {
|
|||||||
void Session::do_write() {
|
void Session::do_write() {
|
||||||
|
|
||||||
auto self = shared_from_this();
|
auto self = shared_from_this();
|
||||||
|
auto packet = std::move(m_write_queue.top().packet);
|
||||||
asio::async_write(
|
asio::async_write(
|
||||||
m_socket, asio::buffer(*(m_write_queue.front())),
|
m_socket, asio::buffer(*packet),
|
||||||
asio::bind_executor(m_strand, [self](std::error_code ec, size_t) {
|
asio::bind_executor(m_strand, [self](std::error_code ec, size_t) {
|
||||||
if (ec) {
|
if (ec) {
|
||||||
Logger::warn("Write Ec {}", ec.message());
|
Logger::warn("Write Ec {}", ec.message());
|
||||||
self->close();
|
self->close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self->m_write_queue.pop_front();
|
self->m_write_queue.pop();
|
||||||
if (!self->m_write_queue.empty()) {
|
if (!self->m_write_queue.empty()) {
|
||||||
self->do_write();
|
self->do_write();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user