mirror of
https://github.com/BeamMP/BeamMP-Launcher.git
synced 2025-07-04 00:47:23 +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;
|
boost::system::error_code ec;
|
||||||
listener.open(listen_ep.protocol(), ec);
|
listener.open(listen_ep.protocol(), ec);
|
||||||
if (ec) {
|
if (ec) {
|
||||||
spdlog::error("Failed to open socket: {}", ec.message());
|
spdlog::error("Failed to open m_game_socket: {}", ec.message());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
socket_base::linger linger_opt {};
|
ip::tcp::socket::linger linger_opt {};
|
||||||
linger_opt.enabled(false);
|
linger_opt.enabled(false);
|
||||||
listener.set_option(linger_opt, ec);
|
listener.set_option(linger_opt, ec);
|
||||||
if (ec) {
|
if (ec) {
|
||||||
spdlog::error("Failed to set up listening socket to not linger / reuse address. "
|
spdlog::error("Failed to set up listening m_game_socket to not linger / reuse address. "
|
||||||
"This may cause the socket to refuse to bind(). spdlog::error: {}",
|
"This may cause the m_game_socket to refuse to bind(). spdlog::error: {}",
|
||||||
ec.message());
|
ec.message());
|
||||||
}
|
}
|
||||||
|
|
||||||
ip::tcp::acceptor acceptor(m_io, listen_ep);
|
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) {
|
if (ec) {
|
||||||
spdlog::error("listen() failed, which is needed for the launcher to operate. "
|
spdlog::error("listen() failed, which is needed for the launcher to operate. "
|
||||||
"Shutting down. spdlog::error: {}",
|
"Shutting down. spdlog::error: {}",
|
||||||
@ -63,6 +63,7 @@ ClientNetwork::~ClientNetwork() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ClientNetwork::handle_connection(ip::tcp::socket&& socket) {
|
void ClientNetwork::handle_connection(ip::tcp::socket&& socket) {
|
||||||
|
m_game_socket = std::move(socket);
|
||||||
// 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;
|
||||||
|
|
||||||
@ -74,16 +75,16 @@ void ClientNetwork::handle_connection(ip::tcp::socket&& socket) {
|
|||||||
{ "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(socket, info_packet);
|
client_tcp_write(info_packet);
|
||||||
|
|
||||||
// packet read and respond loop
|
// packet read and respond loop
|
||||||
while (!*m_shutdown) {
|
while (!*m_shutdown) {
|
||||||
auto packet = client_tcp_read(socket);
|
auto packet = client_tcp_read();
|
||||||
handle_packet(socket, packet);
|
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: {}",
|
spdlog::trace("Got client packet: purpose: 0x{:x}, flags: 0x{:x}, pid: {}, vid: {}, size: {}",
|
||||||
uint16_t(packet.purpose),
|
uint16_t(packet.purpose),
|
||||||
uint8_t(packet.flags),
|
uint8_t(packet.flags),
|
||||||
@ -93,39 +94,39 @@ void ClientNetwork::handle_packet(ip::tcp::socket& socket, bmp::ClientPacket& pa
|
|||||||
|
|
||||||
switch (m_client_state) {
|
switch (m_client_state) {
|
||||||
case bmp::ClientIdentification:
|
case bmp::ClientIdentification:
|
||||||
handle_client_identification(socket, packet);
|
handle_client_identification(packet);
|
||||||
break;
|
break;
|
||||||
case bmp::Login:
|
case bmp::Login:
|
||||||
handle_login(socket, packet);
|
handle_login(packet);
|
||||||
break;
|
break;
|
||||||
case bmp::QuickJoin:
|
case bmp::QuickJoin:
|
||||||
handle_quick_join(socket, packet);
|
handle_quick_join(packet);
|
||||||
break;
|
break;
|
||||||
case bmp::Browsing:
|
case bmp::Browsing:
|
||||||
handle_browsing(socket, packet);
|
handle_browsing(packet);
|
||||||
break;
|
break;
|
||||||
case bmp::ServerIdentification:
|
case bmp::ServerIdentification:
|
||||||
handle_server_identification(socket, packet);
|
handle_server_identification(packet);
|
||||||
break;
|
break;
|
||||||
case bmp::ServerAuthentication:
|
case bmp::ServerAuthentication:
|
||||||
handle_server_authentication(socket, packet);
|
handle_server_authentication(packet);
|
||||||
break;
|
break;
|
||||||
case bmp::ServerModDownload:
|
case bmp::ServerModDownload:
|
||||||
handle_server_mod_download(socket, packet);
|
handle_server_mod_download(packet);
|
||||||
break;
|
break;
|
||||||
case bmp::ServerSessionSetup:
|
case bmp::ServerSessionSetup:
|
||||||
handle_server_session_setup(socket, packet);
|
handle_server_session_setup(packet);
|
||||||
break;
|
break;
|
||||||
case bmp::ServerPlaying:
|
case bmp::ServerPlaying:
|
||||||
handle_server_playing(socket, packet);
|
handle_server_playing(packet);
|
||||||
break;
|
break;
|
||||||
case bmp::ServerLeaving:
|
case bmp::ServerLeaving:
|
||||||
handle_server_leaving(socket, packet);
|
handle_server_leaving(packet);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientNetwork::handle_client_identification(ip::tcp::socket& socket, bmp::ClientPacket& packet) {
|
void ClientNetwork::handle_client_identification(bmp::ClientPacket& packet) {
|
||||||
switch (packet.purpose) {
|
switch (packet.purpose) {
|
||||||
case bmp::ClientPurpose::GameInfo: {
|
case bmp::ClientPurpose::GameInfo: {
|
||||||
try {
|
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> game_version = game_info.at("game_version");
|
||||||
std::vector<int> protocol_version = game_info.at("protocol_version");
|
std::vector<int> protocol_version = game_info.at("protocol_version");
|
||||||
if (protocol_version.at(0) != 1) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
m_mod_version = Version { uint8_t(mod_version.at(0)), uint8_t(mod_version.at(1)), uint8_t(mod_version.at(2)) };
|
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));
|
protocol_version.at(1), protocol_version.at(2));
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
spdlog::error("Failed to read json for purpose 0x{:x}: {}", uint16_t(packet.purpose), e.what());
|
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 {
|
bmp::ClientPacket state_change {
|
||||||
.purpose = bmp::ClientPurpose::StateChangeLogin,
|
.purpose = bmp::ClientPurpose::StateChangeLogin,
|
||||||
};
|
};
|
||||||
client_tcp_write(socket, state_change);
|
client_tcp_write(state_change);
|
||||||
|
|
||||||
|
start_login();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
disconnect(fmt::format("Invalid packet purpose in state 0x{:x}: 0x{:x}", uint16_t(m_client_state), uint16_t(packet.purpose)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
// first packet in login
|
||||||
// TODO: send LoginResult if already logged in.
|
// TODO: send LoginResult if already logged in.
|
||||||
bmp::ClientPacket ask_for_creds {
|
bmp::ClientPacket ask_for_creds {
|
||||||
.purpose = bmp::ClientPurpose::AskForCredentials,
|
.purpose = bmp::ClientPurpose::AskForCredentials,
|
||||||
};
|
};
|
||||||
client_tcp_write(socket, ask_for_creds);
|
client_tcp_write(ask_for_creds);
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
disconnect(socket, 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::disconnect(const std::string& reason) {
|
||||||
bmp::ClientPacket error {
|
bmp::ClientPacket error {
|
||||||
.purpose = bmp::ClientPurpose::Error,
|
.purpose = bmp::ClientPurpose::Error,
|
||||||
.raw_data = json_to_vec({ "message", reason })
|
.raw_data = json_to_vec({ "message", reason })
|
||||||
};
|
};
|
||||||
client_tcp_write(socket, error);
|
client_tcp_write(error);
|
||||||
socket.close();
|
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) {
|
switch (packet.purpose) {
|
||||||
case bmp::ClientPurpose::Credentials:
|
case bmp::ClientPurpose::Credentials:
|
||||||
try {
|
try {
|
||||||
@ -191,62 +212,62 @@ void ClientNetwork::handle_login(ip::tcp::socket& socket, bmp::ClientPacket& pac
|
|||||||
// CONTINUE HERE
|
// CONTINUE HERE
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
spdlog::error("Failed to read json for purpose 0x{:x}: {}", uint16_t(packet.purpose), e.what());
|
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;
|
break;
|
||||||
default:
|
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;
|
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 {};
|
bmp::ClientPacket packet {};
|
||||||
std::vector<uint8_t> header_buffer(bmp::ClientHeader::SERIALIZED_SIZE);
|
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 {};
|
bmp::ClientHeader hdr {};
|
||||||
hdr.deserialize_from(header_buffer);
|
hdr.deserialize_from(header_buffer);
|
||||||
// vector eaten up by now, recv again
|
// vector eaten up by now, recv again
|
||||||
packet.raw_data.resize(hdr.data_size);
|
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.purpose = hdr.purpose;
|
||||||
packet.flags = hdr.flags;
|
packet.flags = hdr.flags;
|
||||||
return packet;
|
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
|
// finalize the packet (compress etc) and produce header
|
||||||
auto header = packet.finalize();
|
auto header = packet.finalize();
|
||||||
// serialize header
|
// serialize header
|
||||||
std::vector<uint8_t> header_data(bmp::ClientHeader::SERIALIZED_SIZE);
|
std::vector<uint8_t> header_data(bmp::ClientHeader::SERIALIZED_SIZE);
|
||||||
header.serialize_to(header_data);
|
header.serialize_to(header_data);
|
||||||
// write header and packet data
|
// write header and packet data
|
||||||
write(socket, buffer(header_data));
|
write(m_game_socket, buffer(header_data));
|
||||||
write(socket, buffer(packet.raw_data));
|
write(m_game_socket, buffer(packet.raw_data));
|
||||||
}
|
}
|
||||||
std::vector<uint8_t> ClientNetwork::json_to_vec(const nlohmann::json& value) {
|
std::vector<uint8_t> ClientNetwork::json_to_vec(const nlohmann::json& value) {
|
||||||
auto str = value.dump();
|
auto str = value.dump();
|
||||||
|
@ -21,22 +21,23 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void handle_connection(ip::tcp::socket&& socket);
|
void handle_connection(ip::tcp::socket&& socket);
|
||||||
bmp::ClientPacket client_tcp_read(ip::tcp::socket& socket);
|
bmp::ClientPacket client_tcp_read();
|
||||||
void client_tcp_write(ip::tcp::socket& socket, bmp::ClientPacket& packet);
|
void client_tcp_write(bmp::ClientPacket& packet);
|
||||||
|
|
||||||
void handle_packet(ip::tcp::socket& socket, bmp::ClientPacket& packet);
|
void handle_packet(bmp::ClientPacket& packet);
|
||||||
void handle_client_identification(ip::tcp::socket& socket, bmp::ClientPacket& packet);
|
void handle_client_identification(bmp::ClientPacket& packet);
|
||||||
void handle_login(ip::tcp::socket& socket, bmp::ClientPacket& packet);
|
void handle_login(bmp::ClientPacket& packet);
|
||||||
void handle_quick_join(ip::tcp::socket& socket, bmp::ClientPacket& packet);
|
void handle_quick_join(bmp::ClientPacket& packet);
|
||||||
void handle_browsing(ip::tcp::socket& socket, bmp::ClientPacket& packet);
|
void handle_browsing(bmp::ClientPacket& packet);
|
||||||
void handle_server_identification(ip::tcp::socket& socket, bmp::ClientPacket& packet);
|
void handle_server_identification(bmp::ClientPacket& packet);
|
||||||
void handle_server_authentication(ip::tcp::socket& socket, bmp::ClientPacket& packet);
|
void handle_server_authentication(bmp::ClientPacket& packet);
|
||||||
void handle_server_mod_download(ip::tcp::socket& socket, bmp::ClientPacket& packet);
|
void handle_server_mod_download(bmp::ClientPacket& packet);
|
||||||
void handle_server_session_setup(ip::tcp::socket& socket, bmp::ClientPacket& packet);
|
void handle_server_session_setup(bmp::ClientPacket& packet);
|
||||||
void handle_server_playing(ip::tcp::socket& socket, bmp::ClientPacket& packet);
|
void handle_server_playing(bmp::ClientPacket& packet);
|
||||||
void handle_server_leaving(ip::tcp::socket& socket, 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 std::vector<uint8_t> json_to_vec(const nlohmann::json& json);
|
||||||
static nlohmann::json vec_to_json(const std::vector<uint8_t>& vec);
|
static nlohmann::json vec_to_json(const std::vector<uint8_t>& vec);
|
||||||
@ -44,8 +45,11 @@ private:
|
|||||||
Version m_mod_version;
|
Version m_mod_version;
|
||||||
Version m_game_version;
|
Version m_game_version;
|
||||||
|
|
||||||
|
ident::Identity m_identity {};
|
||||||
|
|
||||||
uint16_t m_listen_port {};
|
uint16_t m_listen_port {};
|
||||||
io_context m_io {};
|
io_context m_io {};
|
||||||
|
ip::tcp::socket m_game_socket { m_io };
|
||||||
Sync<bool> m_shutdown { false };
|
Sync<bool> m_shutdown { false };
|
||||||
bmp::ClientState m_client_state;
|
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