make server network async, implement connecting

This commit is contained in:
Lion Kortlepel 2024-03-10 14:40:54 +01:00
parent 4e304f798e
commit 5d56d529bc
No known key found for this signature in database
GPG Key ID: 4322FF2B4C71259B
3 changed files with 108 additions and 68 deletions

View File

@ -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)));

View File

@ -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: {
@ -194,7 +190,7 @@ void ServerNetwork::handle_identification(const bmp::Packet& packet) {
}; };
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) {
if (ec) {
spdlog::error("Failed to read from server: {}", ec.message());
} else {
bmp::Header hdr {}; bmp::Header hdr {};
hdr.deserialize_from(header_buffer); hdr.deserialize_from(m_tmp_header_buffer);
// vector eaten up by now, recv again // vector eaten up by now, recv again
packet.raw_data.resize(hdr.size); m_tmp_packet.raw_data.resize(hdr.size);
read(m_tcp_socket, buffer(packet.raw_data)); m_tmp_packet.purpose = hdr.purpose;
packet.purpose = hdr.purpose; m_tmp_packet.flags = hdr.flags;
packet.flags = hdr.flags; boost::asio::async_read(m_tcp_socket, buffer(m_tmp_packet.raw_data),
return packet; [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();
});
}

View File

@ -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 {};