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

View File

@ -26,9 +26,11 @@ private:
void start_accept();
void handle_accept(boost::system::error_code ec);
void start_read();
void handle_connection();
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_client_identification(bmp::ClientPacket& packet);