mirror of
https://github.com/BeamMP/BeamMP-Launcher.git
synced 2025-07-04 00:47:23 +00:00
fix not reading continuously and other async bugs
This commit is contained in:
parent
29b4fa9b81
commit
4e304f798e
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user