mirror of
https://github.com/BeamMP/BeamMP-Launcher.git
synced 2025-07-03 16:37:11 +00:00
make server network async, implement connecting
This commit is contained in:
parent
4e304f798e
commit
5d56d529bc
@ -73,18 +73,6 @@ void ClientNetwork::handle_connection() {
|
|||||||
}) });
|
}) });
|
||||||
|
|
||||||
start_read();
|
start_read();
|
||||||
|
|
||||||
// packet read and respond loop
|
|
||||||
/*
|
|
||||||
try {
|
|
||||||
auto packet = client_tcp_read();
|
|
||||||
handle_packet(packet);
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
spdlog::error("Unhandled exception in connection handler, connection closing.");
|
|
||||||
spdlog::debug("Exception: {}", e.what());
|
|
||||||
m_game_socket.close();
|
|
||||||
break;
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientNetwork::handle_packet(bmp::ClientPacket& packet) {
|
void ClientNetwork::handle_packet(bmp::ClientPacket& packet) {
|
||||||
@ -300,6 +288,16 @@ void ClientNetwork::handle_browsing(bmp::ClientPacket& packet) {
|
|||||||
std::string host = details.at("host");
|
std::string host = details.at("host");
|
||||||
uint16_t port = details.at("port");
|
uint16_t port = details.at("port");
|
||||||
spdlog::info("Game requesting to connect to server [{}]:{}", host, port);
|
spdlog::info("Game requesting to connect to server [{}]:{}", host, port);
|
||||||
|
auto connect_result = launcher.start_server_network(host, port);
|
||||||
|
if (connect_result.has_error()) {
|
||||||
|
spdlog::error("Failed to connect to server: {}", connect_result.error());
|
||||||
|
client_tcp_write(bmp::ClientPacket {
|
||||||
|
.purpose = bmp::ClientPurpose::ConnectError,
|
||||||
|
.raw_data = json_to_vec({ "message", connect_result.error() }),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
spdlog::info("Connected to server!");
|
||||||
|
}
|
||||||
} 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(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)));
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "ServerNetwork.h"
|
#include "ServerNetwork.h"
|
||||||
#include "ClientInfo.h"
|
#include "ClientInfo.h"
|
||||||
|
#include "ClientNetwork.h"
|
||||||
#include "ClientPacket.h"
|
#include "ClientPacket.h"
|
||||||
#include "ClientTransport.h"
|
#include "ClientTransport.h"
|
||||||
#include "Identity.h"
|
#include "Identity.h"
|
||||||
@ -9,7 +10,6 @@
|
|||||||
#include "ProtocolVersion.h"
|
#include "ProtocolVersion.h"
|
||||||
#include "ServerInfo.h"
|
#include "ServerInfo.h"
|
||||||
#include "Transport.h"
|
#include "Transport.h"
|
||||||
#include "ClientNetwork.h"
|
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
@ -23,7 +23,6 @@ ServerNetwork::ServerNetwork(Launcher& launcher, const ip::tcp::endpoint& ep)
|
|||||||
if (ec) {
|
if (ec) {
|
||||||
throw std::runtime_error(ec.message());
|
throw std::runtime_error(ec.message());
|
||||||
}
|
}
|
||||||
launcher.client_network->handle_server_packet(bmp::Packet {});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerNetwork::~ServerNetwork() {
|
ServerNetwork::~ServerNetwork() {
|
||||||
@ -47,21 +46,19 @@ void ServerNetwork::run() {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
version.serialize_to(version_packet.raw_data);
|
version.serialize_to(version_packet.raw_data);
|
||||||
tcp_write(version_packet);
|
tcp_write(std::move(version_packet));
|
||||||
// main tcp recv loop
|
|
||||||
while (true) {
|
start_read();
|
||||||
auto packet = tcp_read();
|
|
||||||
handle_packet(packet);
|
m_io.run();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerNetwork::handle_packet(const bmp::Packet& packet) {
|
void ServerNetwork::handle_packet(const bmp::Packet& packet) {
|
||||||
// handle ping immediately
|
// handle ping immediately
|
||||||
if (m_state > bmp::State::Identification && packet.purpose == bmp::Purpose::Ping) {
|
if (m_state > bmp::State::Identification && packet.purpose == bmp::Purpose::Ping) {
|
||||||
bmp::Packet pong {
|
tcp_write(bmp::Packet {
|
||||||
.purpose = bmp::Purpose::Ping,
|
.purpose = bmp::Purpose::Ping,
|
||||||
};
|
});
|
||||||
tcp_write(pong);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch (m_state) {
|
switch (m_state) {
|
||||||
@ -99,11 +96,10 @@ void ServerNetwork::handle_mod_download(const bmp::Packet& packet) {
|
|||||||
}
|
}
|
||||||
// TODO: implement mod download
|
// TODO: implement mod download
|
||||||
// for now we just pretend we're all good!
|
// for now we just pretend we're all good!
|
||||||
bmp::Packet ok {
|
tcp_write(bmp::Packet {
|
||||||
.purpose = bmp::Purpose::ModsSyncDone,
|
.purpose = bmp::Purpose::ModsSyncDone,
|
||||||
};
|
});
|
||||||
spdlog::info("Done syncing mods");
|
spdlog::info("Done syncing mods");
|
||||||
tcp_write(ok);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case bmp::Purpose::MapInfo: {
|
case bmp::Purpose::MapInfo: {
|
||||||
@ -179,22 +175,22 @@ void ServerNetwork::handle_identification(const bmp::Packet& packet) {
|
|||||||
struct bmp::ClientInfo ci {
|
struct bmp::ClientInfo ci {
|
||||||
.program_version = { .major = PRJ_VERSION_MAJOR, .minor = PRJ_VERSION_MINOR, .patch = PRJ_VERSION_PATCH },
|
.program_version = { .major = PRJ_VERSION_MAJOR, .minor = PRJ_VERSION_MINOR, .patch = PRJ_VERSION_PATCH },
|
||||||
.game_version = {
|
.game_version = {
|
||||||
.major = launcher.game_version->major,
|
.major = launcher.game_version->major,
|
||||||
.minor = launcher.game_version->minor,
|
.minor = launcher.game_version->minor,
|
||||||
.patch = launcher.game_version->patch,
|
.patch = launcher.game_version->patch,
|
||||||
},
|
},
|
||||||
.mod_version = {
|
.mod_version = {
|
||||||
.major = launcher.mod_version->major,
|
.major = launcher.mod_version->major,
|
||||||
.minor = launcher.mod_version->minor,
|
.minor = launcher.mod_version->minor,
|
||||||
.patch = launcher.mod_version->patch,
|
.patch = launcher.mod_version->patch,
|
||||||
},
|
},
|
||||||
.implementation = bmp::ImplementationInfo {
|
.implementation = bmp::ImplementationInfo {
|
||||||
.value = "Official BeamMP Launcher",
|
.value = "Official BeamMP Launcher",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
auto sz = ci.serialize_to(ci_packet.raw_data);
|
auto sz = ci.serialize_to(ci_packet.raw_data);
|
||||||
ci_packet.raw_data.resize(sz);
|
ci_packet.raw_data.resize(sz);
|
||||||
tcp_write(ci_packet);
|
tcp_write(std::move(ci_packet));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case bmp::Purpose::ProtocolVersionBad:
|
case bmp::Purpose::ProtocolVersionBad:
|
||||||
@ -215,11 +211,9 @@ void ServerNetwork::handle_identification(const bmp::Packet& packet) {
|
|||||||
spdlog::debug("Starting authentication");
|
spdlog::debug("Starting authentication");
|
||||||
m_state = bmp::State::Authentication;
|
m_state = bmp::State::Authentication;
|
||||||
auto ident = launcher.identity.synchronize();
|
auto ident = launcher.identity.synchronize();
|
||||||
bmp::Packet pubkey_packet {
|
tcp_write(bmp::Packet {
|
||||||
.purpose = bmp::Purpose::PlayerPublicKey,
|
.purpose = bmp::Purpose::PlayerPublicKey,
|
||||||
.raw_data = std::vector<uint8_t>(ident->PublicKey.begin(), ident->PublicKey.end())
|
.raw_data = std::vector<uint8_t>(ident->PublicKey.begin(), ident->PublicKey.end()) });
|
||||||
};
|
|
||||||
tcp_write(pubkey_packet);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -229,29 +223,58 @@ void ServerNetwork::handle_identification(const bmp::Packet& packet) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bmp::Packet ServerNetwork::tcp_read() {
|
void ServerNetwork::tcp_read(std::function<void(bmp::Packet&&)> handler) {
|
||||||
bmp::Packet packet {};
|
m_tmp_header_buffer.resize(bmp::Header::SERIALIZED_SIZE);
|
||||||
std::vector<uint8_t> header_buffer(bmp::Header::SERIALIZED_SIZE);
|
boost::asio::async_read(m_tcp_socket, buffer(m_tmp_header_buffer),
|
||||||
read(m_tcp_socket, buffer(header_buffer));
|
[this, handler](auto ec, auto) {
|
||||||
bmp::Header hdr {};
|
if (ec) {
|
||||||
hdr.deserialize_from(header_buffer);
|
spdlog::error("Failed to read from server: {}", ec.message());
|
||||||
// vector eaten up by now, recv again
|
} else {
|
||||||
packet.raw_data.resize(hdr.size);
|
bmp::Header hdr {};
|
||||||
read(m_tcp_socket, buffer(packet.raw_data));
|
hdr.deserialize_from(m_tmp_header_buffer);
|
||||||
packet.purpose = hdr.purpose;
|
// vector eaten up by now, recv again
|
||||||
packet.flags = hdr.flags;
|
m_tmp_packet.raw_data.resize(hdr.size);
|
||||||
return packet;
|
m_tmp_packet.purpose = hdr.purpose;
|
||||||
|
m_tmp_packet.flags = hdr.flags;
|
||||||
|
boost::asio::async_read(m_tcp_socket, buffer(m_tmp_packet.raw_data),
|
||||||
|
[handler, this](auto ec, auto) {
|
||||||
|
if (ec) {
|
||||||
|
spdlog::error("Failed to read from server: {}", ec.message());
|
||||||
|
} else {
|
||||||
|
// ok!
|
||||||
|
handler(std::move(m_tmp_packet));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerNetwork::tcp_write(bmp::Packet& packet) {
|
void ServerNetwork::tcp_write(bmp::Packet&& packet, std::function<void(boost::system::error_code)> handler) {
|
||||||
// finalize the packet (compress etc) and produce header
|
// finalize the packet (compress etc) and produce header
|
||||||
auto header = packet.finalize();
|
auto header = packet.finalize();
|
||||||
|
|
||||||
|
auto owned_packet = std::make_shared<bmp::Packet>(std::move(packet));
|
||||||
// serialize header
|
// serialize header
|
||||||
std::vector<uint8_t> header_data(bmp::Header::SERIALIZED_SIZE);
|
auto header_data = std::make_shared<std::vector<uint8_t>>(bmp::Header::SERIALIZED_SIZE);
|
||||||
header.serialize_to(header_data);
|
header.serialize_to(*header_data);
|
||||||
// write header and packet data
|
std::array<const_buffer, 2> buffers = {
|
||||||
write(m_tcp_socket, buffer(header_data));
|
buffer(*header_data),
|
||||||
write(m_tcp_socket, buffer(packet.raw_data));
|
buffer(owned_packet->raw_data)
|
||||||
|
};
|
||||||
|
boost::asio::async_write(m_tcp_socket, buffers,
|
||||||
|
[this, header_data, owned_packet, handler](auto ec, auto size) {
|
||||||
|
spdlog::debug("Wrote {} bytes for 0x{:x} to server", size, int(owned_packet->purpose));
|
||||||
|
if (handler) {
|
||||||
|
handler(ec);
|
||||||
|
} else {
|
||||||
|
if (ec) {
|
||||||
|
spdlog::error("Failed to send packet of type 0x{:x} to server", int(owned_packet->purpose));
|
||||||
|
} else {
|
||||||
|
// ok!
|
||||||
|
spdlog::debug("Sent packet of type 0x{:x} to server", int(owned_packet->purpose));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bmp::Packet ServerNetwork::udp_read(ip::udp::endpoint& out_ep) {
|
bmp::Packet ServerNetwork::udp_read(ip::udp::endpoint& out_ep) {
|
||||||
@ -268,20 +291,25 @@ bmp::Packet ServerNetwork::udp_read(ip::udp::endpoint& out_ep) {
|
|||||||
|
|
||||||
void ServerNetwork::udp_write(bmp::Packet& packet) {
|
void ServerNetwork::udp_write(bmp::Packet& packet) {
|
||||||
auto header = packet.finalize();
|
auto header = packet.finalize();
|
||||||
std::vector<uint8_t> data(header.size + bmp::Header::SERIALIZED_SIZE);
|
auto data = std::make_shared<std::vector<uint8_t>>(header.size + bmp::Header::SERIALIZED_SIZE);
|
||||||
auto offset = header.serialize_to(data);
|
auto offset = header.serialize_to(*data);
|
||||||
std::copy(packet.raw_data.begin(), packet.raw_data.end(), data.begin() + static_cast<long>(offset));
|
std::copy(packet.raw_data.begin(), packet.raw_data.end(), data->begin() + static_cast<long>(offset));
|
||||||
m_udp_socket.send_to(buffer(data), m_udp_ep, {});
|
m_udp_socket.async_send_to(buffer(*data), m_udp_ep, [data](auto ec, auto size) {
|
||||||
|
if (ec) {
|
||||||
|
spdlog::error("Failed to UDP write to server: {}", ec.message());
|
||||||
|
} else {
|
||||||
|
spdlog::info("Wrote {} bytes to server via UDP", size);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
void ServerNetwork::handle_session_setup(const bmp::Packet& packet) {
|
void ServerNetwork::handle_session_setup(const bmp::Packet& packet) {
|
||||||
switch (packet.purpose) {
|
switch (packet.purpose) {
|
||||||
case bmp::Purpose::PlayersVehiclesInfo: {
|
case bmp::Purpose::PlayersVehiclesInfo: {
|
||||||
spdlog::debug("Players and vehicles info: {} bytes ({} bytes on arrival)", packet.get_readable_data().size(), packet.raw_data.size());
|
spdlog::debug("Players and vehicles info: {} bytes ({} bytes on arrival)", packet.get_readable_data().size(), packet.raw_data.size());
|
||||||
// TODO: Send to game
|
// TODO: Send to game
|
||||||
bmp::Packet ready {
|
tcp_write(bmp::Packet {
|
||||||
.purpose = bmp::Purpose::SessionReady,
|
.purpose = bmp::Purpose::SessionReady,
|
||||||
};
|
});
|
||||||
tcp_write(ready);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case bmp::Purpose::StateChangePlaying: {
|
case bmp::Purpose::StateChangePlaying: {
|
||||||
@ -303,3 +331,11 @@ void ServerNetwork::handle_playing(const bmp::Packet& packet) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ServerNetwork::start_read() {
|
||||||
|
tcp_read([this](auto&& packet) {
|
||||||
|
spdlog::debug("Got packet 0x{:x} from server", int(packet.purpose));
|
||||||
|
handle_packet(packet);
|
||||||
|
start_read();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -19,10 +19,12 @@ public:
|
|||||||
void run();
|
void run();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void start_read();
|
||||||
|
|
||||||
/// Reads a single packet from the TCP stream. Blocks all other reads (not writes).
|
/// Reads a single packet from the TCP stream. Blocks all other reads (not writes).
|
||||||
bmp::Packet tcp_read();
|
void tcp_read(std::function<void(bmp::Packet&&)> handler);
|
||||||
/// Writes the packet to the TCP stream. Blocks all other writes.
|
/// Writes the packet to the TCP stream. Blocks all other writes.
|
||||||
void tcp_write(bmp::Packet& packet);
|
void tcp_write(bmp::Packet&& packet, std::function<void(boost::system::error_code)> handler = nullptr);
|
||||||
|
|
||||||
/// Reads a packet from the given UDP socket, returning the client's endpoint as an out-argument.
|
/// Reads a packet from the given UDP socket, returning the client's endpoint as an out-argument.
|
||||||
bmp::Packet udp_read(ip::udp::endpoint& out_ep);
|
bmp::Packet udp_read(ip::udp::endpoint& out_ep);
|
||||||
@ -40,6 +42,10 @@ private:
|
|||||||
ip::tcp::socket m_tcp_socket { m_io };
|
ip::tcp::socket m_tcp_socket { m_io };
|
||||||
ip::udp::socket m_udp_socket { m_io };
|
ip::udp::socket m_udp_socket { m_io };
|
||||||
|
|
||||||
|
// these two tmp fields are used for temporary reading into by read, don't use anywhere else please
|
||||||
|
bmp::Packet m_tmp_packet {};
|
||||||
|
std::vector<uint8_t> m_tmp_header_buffer {};
|
||||||
|
|
||||||
bmp::State m_state {};
|
bmp::State m_state {};
|
||||||
|
|
||||||
uint64_t m_udp_magic {};
|
uint64_t m_udp_magic {};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user