mirror of
https://github.com/BeamMP/BeamMP-Launcher.git
synced 2025-07-02 16:06:35 +00:00
add Result, move game socket to class
This commit is contained in:
parent
e4eb9a6e38
commit
fe9b2ad0f4
@ -14,20 +14,20 @@ void ClientNetwork::run() {
|
||||
boost::system::error_code ec;
|
||||
listener.open(listen_ep.protocol(), ec);
|
||||
if (ec) {
|
||||
spdlog::error("Failed to open socket: {}", ec.message());
|
||||
spdlog::error("Failed to open m_game_socket: {}", ec.message());
|
||||
return;
|
||||
}
|
||||
socket_base::linger linger_opt {};
|
||||
ip::tcp::socket::linger linger_opt {};
|
||||
linger_opt.enabled(false);
|
||||
listener.set_option(linger_opt, ec);
|
||||
if (ec) {
|
||||
spdlog::error("Failed to set up listening socket to not linger / reuse address. "
|
||||
"This may cause the socket to refuse to bind(). spdlog::error: {}",
|
||||
spdlog::error("Failed to set up listening m_game_socket to not linger / reuse address. "
|
||||
"This may cause the m_game_socket to refuse to bind(). spdlog::error: {}",
|
||||
ec.message());
|
||||
}
|
||||
|
||||
ip::tcp::acceptor acceptor(m_io, listen_ep);
|
||||
acceptor.listen(socket_base::max_listen_connections, ec);
|
||||
acceptor.listen(ip::tcp::socket::max_listen_connections, ec);
|
||||
if (ec) {
|
||||
spdlog::error("listen() failed, which is needed for the launcher to operate. "
|
||||
"Shutting down. spdlog::error: {}",
|
||||
@ -63,6 +63,7 @@ ClientNetwork::~ClientNetwork() {
|
||||
}
|
||||
|
||||
void ClientNetwork::handle_connection(ip::tcp::socket&& socket) {
|
||||
m_game_socket = std::move(socket);
|
||||
// immediately send launcher info (first step of client identification)
|
||||
m_client_state = bmp::ClientState::ClientIdentification;
|
||||
|
||||
@ -74,16 +75,16 @@ void ClientNetwork::handle_connection(ip::tcp::socket&& socket) {
|
||||
{ "version", { PRJ_VERSION_MAJOR, PRJ_VERSION_MINOR, PRJ_VERSION_PATCH } },
|
||||
{ "mod_cache_path", "/idk sorry/" }, // TODO: mod_cache_path in LauncherInfo
|
||||
});
|
||||
client_tcp_write(socket, info_packet);
|
||||
client_tcp_write(info_packet);
|
||||
|
||||
// packet read and respond loop
|
||||
while (!*m_shutdown) {
|
||||
auto packet = client_tcp_read(socket);
|
||||
handle_packet(socket, packet);
|
||||
auto packet = client_tcp_read();
|
||||
handle_packet(packet);
|
||||
}
|
||||
}
|
||||
|
||||
void ClientNetwork::handle_packet(ip::tcp::socket& socket, bmp::ClientPacket& packet) {
|
||||
void ClientNetwork::handle_packet(bmp::ClientPacket& packet) {
|
||||
spdlog::trace("Got client packet: purpose: 0x{:x}, flags: 0x{:x}, pid: {}, vid: {}, size: {}",
|
||||
uint16_t(packet.purpose),
|
||||
uint8_t(packet.flags),
|
||||
@ -93,39 +94,39 @@ void ClientNetwork::handle_packet(ip::tcp::socket& socket, bmp::ClientPacket& pa
|
||||
|
||||
switch (m_client_state) {
|
||||
case bmp::ClientIdentification:
|
||||
handle_client_identification(socket, packet);
|
||||
handle_client_identification(packet);
|
||||
break;
|
||||
case bmp::Login:
|
||||
handle_login(socket, packet);
|
||||
handle_login(packet);
|
||||
break;
|
||||
case bmp::QuickJoin:
|
||||
handle_quick_join(socket, packet);
|
||||
handle_quick_join(packet);
|
||||
break;
|
||||
case bmp::Browsing:
|
||||
handle_browsing(socket, packet);
|
||||
handle_browsing(packet);
|
||||
break;
|
||||
case bmp::ServerIdentification:
|
||||
handle_server_identification(socket, packet);
|
||||
handle_server_identification(packet);
|
||||
break;
|
||||
case bmp::ServerAuthentication:
|
||||
handle_server_authentication(socket, packet);
|
||||
handle_server_authentication(packet);
|
||||
break;
|
||||
case bmp::ServerModDownload:
|
||||
handle_server_mod_download(socket, packet);
|
||||
handle_server_mod_download(packet);
|
||||
break;
|
||||
case bmp::ServerSessionSetup:
|
||||
handle_server_session_setup(socket, packet);
|
||||
handle_server_session_setup(packet);
|
||||
break;
|
||||
case bmp::ServerPlaying:
|
||||
handle_server_playing(socket, packet);
|
||||
handle_server_playing(packet);
|
||||
break;
|
||||
case bmp::ServerLeaving:
|
||||
handle_server_leaving(socket, packet);
|
||||
handle_server_leaving(packet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ClientNetwork::handle_client_identification(ip::tcp::socket& socket, bmp::ClientPacket& packet) {
|
||||
void ClientNetwork::handle_client_identification(bmp::ClientPacket& packet) {
|
||||
switch (packet.purpose) {
|
||||
case bmp::ClientPurpose::GameInfo: {
|
||||
try {
|
||||
@ -135,7 +136,7 @@ void ClientNetwork::handle_client_identification(ip::tcp::socket& socket, bmp::C
|
||||
std::vector<int> game_version = game_info.at("game_version");
|
||||
std::vector<int> protocol_version = game_info.at("protocol_version");
|
||||
if (protocol_version.at(0) != 1) {
|
||||
disconnect(socket, fmt::format("Incompatible protocol version, expected v{}", "1.x.x"));
|
||||
disconnect(fmt::format("Incompatible protocol version, expected v{}", "1.x.x"));
|
||||
return;
|
||||
}
|
||||
m_mod_version = Version { uint8_t(mod_version.at(0)), uint8_t(mod_version.at(1)), uint8_t(mod_version.at(2)) };
|
||||
@ -148,37 +149,57 @@ void ClientNetwork::handle_client_identification(ip::tcp::socket& socket, bmp::C
|
||||
protocol_version.at(1), protocol_version.at(2));
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::error("Failed to read json for purpose 0x{:x}: {}", uint16_t(packet.purpose), e.what());
|
||||
disconnect(socket, fmt::format("Invalid json in purpose 0x{:x}, see launcher logs for more info", uint16_t(packet.purpose)));
|
||||
disconnect(fmt::format("Invalid json in purpose 0x{:x}, see launcher logs for more info", uint16_t(packet.purpose)));
|
||||
}
|
||||
bmp::ClientPacket state_change {
|
||||
.purpose = bmp::ClientPurpose::StateChangeLogin,
|
||||
};
|
||||
client_tcp_write(socket, state_change);
|
||||
client_tcp_write(state_change);
|
||||
|
||||
// first packet in login
|
||||
// TODO: send LoginResult if already logged in.
|
||||
bmp::ClientPacket ask_for_creds {
|
||||
.purpose = bmp::ClientPurpose::AskForCredentials,
|
||||
};
|
||||
client_tcp_write(socket, ask_for_creds);
|
||||
start_login();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
disconnect(socket, fmt::format("Invalid packet purpose in state 0x{:x}: 0x{:x}", uint16_t(m_client_state), uint16_t(packet.purpose)));
|
||||
disconnect(fmt::format("Invalid packet purpose in state 0x{:x}: 0x{:x}", uint16_t(m_client_state), uint16_t(packet.purpose)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ClientNetwork::disconnect(ip::tcp::socket& socket, const std::string& reason) {
|
||||
void ClientNetwork::start_login() {
|
||||
|
||||
if (ident::is_login_cached()) {
|
||||
auto login = ident::login_cached();
|
||||
if (login.has_value()) {
|
||||
bmp::ClientPacket login_result {
|
||||
.purpose = bmp::ClientPurpose::LoginResult,
|
||||
.raw_data = json_to_vec({
|
||||
{ "success", true },
|
||||
{ "message", login.value().Message },
|
||||
{ "username", login.value().Username },
|
||||
{ "role", login.value().Role },
|
||||
}),
|
||||
};
|
||||
client_tcp_write(login_result);
|
||||
}
|
||||
}
|
||||
// first packet in login
|
||||
// TODO: send LoginResult if already logged in.
|
||||
bmp::ClientPacket ask_for_creds {
|
||||
.purpose = bmp::ClientPurpose::AskForCredentials,
|
||||
};
|
||||
client_tcp_write(ask_for_creds);
|
||||
}
|
||||
|
||||
void ClientNetwork::disconnect(const std::string& reason) {
|
||||
bmp::ClientPacket error {
|
||||
.purpose = bmp::ClientPurpose::Error,
|
||||
.raw_data = json_to_vec({ "message", reason })
|
||||
};
|
||||
client_tcp_write(socket, error);
|
||||
socket.close();
|
||||
client_tcp_write(error);
|
||||
m_game_socket.close();
|
||||
}
|
||||
|
||||
void ClientNetwork::handle_login(ip::tcp::socket& socket, bmp::ClientPacket& packet) {
|
||||
void ClientNetwork::handle_login(bmp::ClientPacket& packet) {
|
||||
switch (packet.purpose) {
|
||||
case bmp::ClientPurpose::Credentials:
|
||||
try {
|
||||
@ -191,62 +212,62 @@ void ClientNetwork::handle_login(ip::tcp::socket& socket, bmp::ClientPacket& pac
|
||||
// CONTINUE HERE
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::error("Failed to read json for purpose 0x{:x}: {}", uint16_t(packet.purpose), e.what());
|
||||
disconnect(socket, fmt::format("Invalid json in purpose 0x{:x}, see launcher logs for more info", uint16_t(packet.purpose)));
|
||||
disconnect(fmt::format("Invalid json in purpose 0x{:x}, see launcher logs for more info", uint16_t(packet.purpose)));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
disconnect(socket, fmt::format("Invalid packet purpose in state 0x{:x}: 0x{:x}", uint16_t(m_client_state), uint16_t(packet.purpose)));
|
||||
disconnect(fmt::format("Invalid packet purpose in state 0x{:x}: 0x{:x}", uint16_t(m_client_state), uint16_t(packet.purpose)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ClientNetwork::handle_quick_join(ip::tcp::socket& socket, bmp::ClientPacket& packet) {
|
||||
void ClientNetwork::handle_quick_join(bmp::ClientPacket& packet) {
|
||||
}
|
||||
|
||||
void ClientNetwork::handle_browsing(ip::tcp::socket& socket, bmp::ClientPacket& packet) {
|
||||
void ClientNetwork::handle_browsing(bmp::ClientPacket& packet) {
|
||||
}
|
||||
|
||||
void ClientNetwork::handle_server_identification(ip::tcp::socket& socket, bmp::ClientPacket& packet) {
|
||||
void ClientNetwork::handle_server_identification(bmp::ClientPacket& packet) {
|
||||
}
|
||||
|
||||
void ClientNetwork::handle_server_authentication(ip::tcp::socket& socket, bmp::ClientPacket& packet) {
|
||||
void ClientNetwork::handle_server_authentication(bmp::ClientPacket& packet) {
|
||||
}
|
||||
|
||||
void ClientNetwork::handle_server_mod_download(ip::tcp::socket& socket, bmp::ClientPacket& packet) {
|
||||
void ClientNetwork::handle_server_mod_download(bmp::ClientPacket& packet) {
|
||||
}
|
||||
|
||||
void ClientNetwork::handle_server_session_setup(ip::tcp::socket& socket, bmp::ClientPacket& packet) {
|
||||
void ClientNetwork::handle_server_session_setup(bmp::ClientPacket& packet) {
|
||||
}
|
||||
|
||||
void ClientNetwork::handle_server_playing(ip::tcp::socket& socket, bmp::ClientPacket& packet) {
|
||||
void ClientNetwork::handle_server_playing(bmp::ClientPacket& packet) {
|
||||
}
|
||||
|
||||
void ClientNetwork::handle_server_leaving(ip::tcp::socket& socket, bmp::ClientPacket& packet) {
|
||||
void ClientNetwork::handle_server_leaving(bmp::ClientPacket& packet) {
|
||||
}
|
||||
|
||||
bmp::ClientPacket ClientNetwork::client_tcp_read(ip::tcp::socket& socket) {
|
||||
bmp::ClientPacket ClientNetwork::client_tcp_read() {
|
||||
bmp::ClientPacket packet {};
|
||||
std::vector<uint8_t> header_buffer(bmp::ClientHeader::SERIALIZED_SIZE);
|
||||
read(socket, buffer(header_buffer));
|
||||
read(m_game_socket, buffer(header_buffer));
|
||||
bmp::ClientHeader hdr {};
|
||||
hdr.deserialize_from(header_buffer);
|
||||
// vector eaten up by now, recv again
|
||||
packet.raw_data.resize(hdr.data_size);
|
||||
read(socket, buffer(packet.raw_data));
|
||||
read(m_game_socket, buffer(packet.raw_data));
|
||||
packet.purpose = hdr.purpose;
|
||||
packet.flags = hdr.flags;
|
||||
return packet;
|
||||
}
|
||||
|
||||
void ClientNetwork::client_tcp_write(ip::tcp::socket& socket, bmp::ClientPacket& packet) {
|
||||
void ClientNetwork::client_tcp_write(bmp::ClientPacket& packet) {
|
||||
// finalize the packet (compress etc) and produce header
|
||||
auto header = packet.finalize();
|
||||
// serialize header
|
||||
std::vector<uint8_t> header_data(bmp::ClientHeader::SERIALIZED_SIZE);
|
||||
header.serialize_to(header_data);
|
||||
// write header and packet data
|
||||
write(socket, buffer(header_data));
|
||||
write(socket, buffer(packet.raw_data));
|
||||
write(m_game_socket, buffer(header_data));
|
||||
write(m_game_socket, buffer(packet.raw_data));
|
||||
}
|
||||
std::vector<uint8_t> ClientNetwork::json_to_vec(const nlohmann::json& value) {
|
||||
auto str = value.dump();
|
||||
|
@ -21,22 +21,23 @@ public:
|
||||
|
||||
private:
|
||||
void handle_connection(ip::tcp::socket&& socket);
|
||||
bmp::ClientPacket client_tcp_read(ip::tcp::socket& socket);
|
||||
void client_tcp_write(ip::tcp::socket& socket, bmp::ClientPacket& packet);
|
||||
bmp::ClientPacket client_tcp_read();
|
||||
void client_tcp_write(bmp::ClientPacket& packet);
|
||||
|
||||
void handle_packet(ip::tcp::socket& socket, bmp::ClientPacket& packet);
|
||||
void handle_client_identification(ip::tcp::socket& socket, bmp::ClientPacket& packet);
|
||||
void handle_login(ip::tcp::socket& socket, bmp::ClientPacket& packet);
|
||||
void handle_quick_join(ip::tcp::socket& socket, bmp::ClientPacket& packet);
|
||||
void handle_browsing(ip::tcp::socket& socket, bmp::ClientPacket& packet);
|
||||
void handle_server_identification(ip::tcp::socket& socket, bmp::ClientPacket& packet);
|
||||
void handle_server_authentication(ip::tcp::socket& socket, bmp::ClientPacket& packet);
|
||||
void handle_server_mod_download(ip::tcp::socket& socket, bmp::ClientPacket& packet);
|
||||
void handle_server_session_setup(ip::tcp::socket& socket, bmp::ClientPacket& packet);
|
||||
void handle_server_playing(ip::tcp::socket& socket, bmp::ClientPacket& packet);
|
||||
void handle_server_leaving(ip::tcp::socket& socket, bmp::ClientPacket& packet);
|
||||
void handle_packet(bmp::ClientPacket& packet);
|
||||
void handle_client_identification(bmp::ClientPacket& packet);
|
||||
void handle_login(bmp::ClientPacket& packet);
|
||||
void handle_quick_join(bmp::ClientPacket& packet);
|
||||
void handle_browsing(bmp::ClientPacket& packet);
|
||||
void handle_server_identification(bmp::ClientPacket& packet);
|
||||
void handle_server_authentication(bmp::ClientPacket& packet);
|
||||
void handle_server_mod_download(bmp::ClientPacket& packet);
|
||||
void handle_server_session_setup(bmp::ClientPacket& packet);
|
||||
void handle_server_playing(bmp::ClientPacket& packet);
|
||||
void handle_server_leaving(bmp::ClientPacket& packet);
|
||||
|
||||
void disconnect(ip::tcp::socket& socket, const std::string& reason);
|
||||
void disconnect(const std::string& reason);
|
||||
void start_login();
|
||||
|
||||
static std::vector<uint8_t> json_to_vec(const nlohmann::json& json);
|
||||
static nlohmann::json vec_to_json(const std::vector<uint8_t>& vec);
|
||||
@ -44,8 +45,11 @@ private:
|
||||
Version m_mod_version;
|
||||
Version m_game_version;
|
||||
|
||||
ident::Identity m_identity {};
|
||||
|
||||
uint16_t m_listen_port {};
|
||||
io_context m_io {};
|
||||
ip::tcp::socket m_game_socket { m_io };
|
||||
Sync<bool> m_shutdown { false };
|
||||
bmp::ClientState m_client_state;
|
||||
};
|
||||
|
11
src/Result.h
Normal file
11
src/Result.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/outcome.hpp>
|
||||
#include <boost/outcome/success_failure.hpp>
|
||||
|
||||
// attribute because [[nodiscard]] isn't possible on using decls - if you wanna write a standards proposal,
|
||||
// this is one to write.
|
||||
template<typename T, typename EC>
|
||||
using Result __attribute__((warn_unused_result)) = boost::outcome_v2::result<T, EC>;
|
||||
|
||||
namespace outcome = boost::outcome_v2;
|
Loading…
x
Reference in New Issue
Block a user