fix not reading continuously and other async bugs

This commit is contained in:
Lion Kortlepel 2024-03-10 13:52:13 +01:00
parent 29b4fa9b81
commit 4e304f798e
No known key found for this signature in database
GPG Key ID: 4322FF2B4C71259B
2 changed files with 77 additions and 61 deletions

View File

@ -5,6 +5,7 @@
#include "Identity.h" #include "Identity.h"
#include "Launcher.h" #include "Launcher.h"
#include <boost/asio/socket_base.hpp>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
@ -51,23 +52,27 @@ ClientNetwork::~ClientNetwork() {
spdlog::debug("Client network destroyed"); spdlog::debug("Client network destroyed");
} }
void ClientNetwork::start_read() {
client_tcp_read([this](auto&& packet) {
handle_packet(packet);
start_read();
});
}
void ClientNetwork::handle_connection() { void ClientNetwork::handle_connection() {
// immediately send launcher info (first step of client identification) // immediately send launcher info (first step of client identification)
m_client_state = bmp::ClientState::ClientIdentification; m_client_state = bmp::ClientState::ClientIdentification;
bmp::ClientPacket info_packet {}; client_tcp_write(bmp::ClientPacket {
info_packet.purpose = bmp::ClientPurpose::LauncherInfo; .purpose = bmp::ClientPurpose::LauncherInfo,
info_packet.raw_data = json_to_vec( .raw_data = json_to_vec(
{ {
{ "implementation", "Official BeamMP Launcher" }, { "implementation", "Official BeamMP Launcher" },
{ "version", { PRJ_VERSION_MAJOR, PRJ_VERSION_MINOR, PRJ_VERSION_PATCH } }, { "version", { PRJ_VERSION_MAJOR, PRJ_VERSION_MINOR, PRJ_VERSION_PATCH } },
{ "mod_cache_path", "/idk sorry/" }, // TODO: mod_cache_path in LauncherInfo { "mod_cache_path", "/idk sorry/" }, // TODO: mod_cache_path in LauncherInfo
}); }) });
client_tcp_write(info_packet);
client_tcp_read([this](auto&& packet) { start_read();
handle_packet(packet);
});
// packet read and respond loop // packet read and respond loop
/* /*
@ -159,10 +164,9 @@ void ClientNetwork::handle_client_identification(bmp::ClientPacket& packet) {
} }
void ClientNetwork::start_login() { void ClientNetwork::start_login() {
bmp::ClientPacket state_change { client_tcp_write(bmp::ClientPacket {
.purpose = bmp::ClientPurpose::StateChangeLogin, .purpose = bmp::ClientPurpose::StateChangeLogin,
}; });
client_tcp_write(state_change);
m_client_state = bmp::ClientState::Login; m_client_state = bmp::ClientState::Login;
@ -172,7 +176,7 @@ void ClientNetwork::start_login() {
// we just send the login result and continue to the next state. // we just send the login result and continue to the next state.
if (login.has_value()) { if (login.has_value()) {
*m_identity = login.value(); *m_identity = login.value();
bmp::ClientPacket login_result { client_tcp_write(bmp::ClientPacket {
.purpose = bmp::ClientPurpose::LoginResult, .purpose = bmp::ClientPurpose::LoginResult,
.raw_data = json_to_vec({ .raw_data = json_to_vec({
{ "success", true }, { "success", true },
@ -180,7 +184,7 @@ void ClientNetwork::start_login() {
{ "username", m_identity->Username }, { "username", m_identity->Username },
{ "role", m_identity->Role }, { "role", m_identity->Role },
}), }),
}; });
// move on to quick join right away // move on to quick join right away
start_quick_join(); start_quick_join();
return; // done return; // done
@ -193,22 +197,25 @@ void ClientNetwork::start_login() {
// first packet in login // first packet in login
// TODO: send LoginResult if already logged in. // TODO: send LoginResult if already logged in.
bmp::ClientPacket ask_for_creds { client_tcp_write(bmp::ClientPacket {
.purpose = bmp::ClientPurpose::AskForCredentials, .purpose = bmp::ClientPurpose::AskForCredentials,
}; });
client_tcp_write(ask_for_creds);
// wait for response, so return // wait for response, so return
} }
void ClientNetwork::disconnect(const std::string& reason) { void ClientNetwork::disconnect(const std::string& reason) {
spdlog::debug("Disconnecting game: {}", reason); spdlog::debug("Disconnecting game: {}", reason);
bmp::ClientPacket error { client_tcp_write(bmp::ClientPacket {
.purpose = bmp::ClientPurpose::Error, .purpose = bmp::ClientPurpose::Error,
.raw_data = json_to_vec({ "message", reason }) .raw_data = json_to_vec({ "message", reason }) },
}; [this](auto ec) {
client_tcp_write(error); // ignore any error and just shut down
m_game_socket.close(); // we pass `ec` to both of these and ignore their errors as well
// because we dont care if they fail
m_game_socket.shutdown(boost::asio::socket_base::shutdown_both, ec);
m_game_socket.close(ec);
start_accept(); start_accept();
});
} }
void ClientNetwork::handle_login(bmp::ClientPacket& packet) { void ClientNetwork::handle_login(bmp::ClientPacket& packet) {
@ -223,22 +230,21 @@ void ClientNetwork::handle_login(bmp::ClientPacket& packet) {
// login! // login!
auto result = ident::login(username, password, remember); auto result = ident::login(username, password, remember);
if (result.has_error()) { if (result.has_error()) {
bmp::ClientPacket login_fail { ;
client_tcp_write(bmp::ClientPacket {
.purpose = bmp::ClientPurpose::LoginResult, .purpose = bmp::ClientPurpose::LoginResult,
.raw_data = json_to_vec({ .raw_data = json_to_vec({
{ "success", false }, { "success", false },
{ "message", result.error() }, { "message", result.error() },
}), }),
}; });
client_tcp_write(login_fail); client_tcp_write(bmp::ClientPacket {
bmp::ClientPacket retry {
.purpose = bmp::ClientPurpose::AskForCredentials, .purpose = bmp::ClientPurpose::AskForCredentials,
}; });
client_tcp_write(retry);
return; return;
} }
*m_identity = result.value(); *m_identity = result.value();
bmp::ClientPacket login_result { client_tcp_write(bmp::ClientPacket {
.purpose = bmp::ClientPurpose::LoginResult, .purpose = bmp::ClientPurpose::LoginResult,
.raw_data = json_to_vec({ .raw_data = json_to_vec({
{ "success", true }, { "success", true },
@ -246,8 +252,7 @@ void ClientNetwork::handle_login(bmp::ClientPacket& packet) {
{ "username", m_identity->Username }, { "username", m_identity->Username },
{ "role", m_identity->Role }, { "role", m_identity->Role },
}), }),
}; });
client_tcp_write(login_result);
start_quick_join(); start_quick_join();
} catch (const std::exception& e) { } catch (const std::exception& e) {
@ -274,18 +279,16 @@ void ClientNetwork::handle_browsing(bmp::ClientPacket& packet) {
case bmp::ClientPurpose::ServerListRequest: { case bmp::ClientPurpose::ServerListRequest: {
auto list = load_server_list(); auto list = load_server_list();
if (list.has_value()) { if (list.has_value()) {
bmp::ClientPacket response { client_tcp_write(bmp::ClientPacket {
.purpose = bmp::ClientPurpose::ServerListResponse, .purpose = bmp::ClientPurpose::ServerListResponse,
.raw_data = list.value(), .raw_data = list.value(),
}; });
client_tcp_write(response);
} else { } else {
spdlog::error("Failed to load server list: {}", list.error()); spdlog::error("Failed to load server list: {}", list.error());
bmp::ClientPacket err { client_tcp_write(bmp::ClientPacket {
.purpose = bmp::ClientPurpose::Error, .purpose = bmp::ClientPurpose::Error,
.raw_data = json_to_vec({ "message", list.error() }), .raw_data = json_to_vec({ "message", list.error() }),
}; });
client_tcp_write(err);
} }
} break; } break;
case bmp::ClientPurpose::Logout: { case bmp::ClientPurpose::Logout: {
@ -369,6 +372,11 @@ void ClientNetwork::client_tcp_read(std::function<void(bmp::ClientPacket&&)> han
m_tmp_packet.raw_data.resize(hdr.data_size); m_tmp_packet.raw_data.resize(hdr.data_size);
m_tmp_packet.purpose = hdr.purpose; m_tmp_packet.purpose = hdr.purpose;
m_tmp_packet.flags = hdr.flags; m_tmp_packet.flags = hdr.flags;
spdlog::debug("Got header: purpose: 0x{:x}, flags: 0x{:x}, pid: {}, vid: {}, size: {}",
uint16_t(m_tmp_packet.purpose),
uint8_t(m_tmp_packet.flags),
m_tmp_packet.pid, m_tmp_packet.vid,
m_tmp_packet.get_readable_data().size());
boost::asio::async_read(m_game_socket, buffer(m_tmp_packet.raw_data), boost::asio::async_read(m_game_socket, buffer(m_tmp_packet.raw_data),
bind_executor(m_strand, [handler, this](auto ec, auto) { bind_executor(m_strand, [handler, this](auto ec, auto) {
if (ec) { if (ec) {
@ -382,21 +390,29 @@ void ClientNetwork::client_tcp_read(std::function<void(bmp::ClientPacket&&)> han
})); }));
} }
void ClientNetwork::client_tcp_write(bmp::ClientPacket& packet) { void ClientNetwork::client_tcp_write(bmp::ClientPacket&& packet, std::function<void(boost::system::error_code)> handler) {
auto header = packet.finalize(); auto header = packet.finalize();
// copy packet
auto owned_packet = std::make_shared<bmp::ClientPacket>(std::move(packet));
// serialize header // serialize header
auto data = std::make_shared<std::vector<uint8_t>>(bmp::ClientHeader::SERIALIZED_SIZE + packet.raw_data.size()); auto header_data = std::make_shared<std::vector<uint8_t>>(bmp::ClientHeader::SERIALIZED_SIZE);
auto offset = header.serialize_to(*data); header.serialize_to(*header_data);
// copy packet data (yes i know ugh) to the `data` in order to send it in one go std::array<const_buffer, 2> buffers = {
std::copy(packet.raw_data.begin(), packet.raw_data.end(), data->begin() + long(offset)); buffer(*header_data),
boost::asio::async_write(m_game_socket, buffer(*data), buffer(owned_packet->raw_data)
[this, packet, data](auto ec, auto) { };
if (ec) { boost::asio::async_write(m_game_socket, buffers,
spdlog::error("Failed to write a packet: {}", ec.message()); [this, header_data, owned_packet, handler](auto ec, auto size) {
disconnect("Failed to send data to game"); spdlog::debug("Wrote {} bytes for 0x{:x}", size, int(owned_packet->purpose));
if (handler) {
handler(ec);
} else { } else {
// ok! sent all data if (ec) {
spdlog::debug("Sent packet: 0x{:x}", int(packet.purpose)); disconnect(fmt::format("Failed to send packet to game: {}", ec.message()));
} else {
// ok!
spdlog::debug("Sent packet of type 0x{:x}", int(owned_packet->purpose));
}
} }
}); });
} }
@ -409,10 +425,9 @@ nlohmann::json ClientNetwork::vec_to_json(const std::vector<uint8_t>& vec) {
return json::parse(std::string(vec.begin(), vec.end())); return json::parse(std::string(vec.begin(), vec.end()));
} }
void ClientNetwork::start_quick_join() { void ClientNetwork::start_quick_join() {
bmp::ClientPacket change_to_quickjoin { client_tcp_write(bmp::ClientPacket {
.purpose = bmp::ClientPurpose::StateChangeQuickJoin, .purpose = bmp::ClientPurpose::StateChangeQuickJoin,
}; });
client_tcp_write(change_to_quickjoin);
m_client_state = bmp::ClientState::QuickJoin; m_client_state = bmp::ClientState::QuickJoin;
// TODO: Implement DoJoin, etc // TODO: Implement DoJoin, etc
@ -421,10 +436,9 @@ void ClientNetwork::start_quick_join() {
} }
void ClientNetwork::start_browsing() { void ClientNetwork::start_browsing() {
bmp::ClientPacket change_to_browsing { client_tcp_write(bmp::ClientPacket {
.purpose = bmp::ClientPurpose::StateChangeBrowsing, .purpose = bmp::ClientPurpose::StateChangeBrowsing,
}; });
client_tcp_write(change_to_browsing);
m_client_state = bmp::ClientState::Browsing; m_client_state = bmp::ClientState::Browsing;
} }

View File

@ -26,9 +26,11 @@ private:
void start_accept(); void start_accept();
void handle_accept(boost::system::error_code ec); void handle_accept(boost::system::error_code ec);
void start_read();
void handle_connection(); void handle_connection();
void client_tcp_read(std::function<void(bmp::ClientPacket&&)> handler); void client_tcp_read(std::function<void(bmp::ClientPacket&&)> handler);
void client_tcp_write(bmp::ClientPacket& packet); void client_tcp_write(bmp::ClientPacket&& packet, std::function<void(boost::system::error_code)> handler = nullptr);
void handle_packet(bmp::ClientPacket& packet); void handle_packet(bmp::ClientPacket& packet);
void handle_client_identification(bmp::ClientPacket& packet); void handle_client_identification(bmp::ClientPacket& packet);