From 630d5f1cfa44bcef82737a3ca7c8dc8392551bf6 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Mon, 22 Jan 2024 16:56:07 +0100 Subject: [PATCH] implement session sync --- include/Network.h | 31 +++++++--------- src/Network.cpp | 94 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 17 deletions(-) diff --git a/include/Network.h b/include/Network.h index e6cada7..6d2e49f 100644 --- a/include/Network.h +++ b/include/Network.h @@ -120,6 +120,8 @@ struct Vehicle { m_status_data = raw_packet; } + const std::vector& get_raw_status() const { return m_status_data; } + private: std::recursive_mutex m_mtx; @@ -163,14 +165,10 @@ public: std::unordered_map get_vehicles_owned_by(ClientID id); - std::optional get_vehicle(VehicleID id) { - auto vehicles = m_vehicles.synchronize(); - if (vehicles->contains(id)) { - return vehicles->at(id); - } else { - return std::nullopt; - } - } + std::optional get_vehicle(VehicleID id); + + /// Builds the SessionSetup.PlayersInfo json which contains all player info and all vehicles. + nlohmann::json build_players_info(); size_t authenticated_client_count() const; @@ -199,11 +197,16 @@ private: void udp_read_main(); void tcp_listen_main(); + /// Handles all packets which are allowed during the Identification state. void handle_identification(ClientID id, const bmp::Packet& packet, std::shared_ptr& client); - + /// Handles all packets which are allowed during the Authentication state. void handle_authentication(ClientID id, const bmp::Packet& packet, std::shared_ptr& client); - + /// Handles all packets which are allowed during the ModDownload state. void handle_mod_download(ClientID id, const bmp::Packet& packet, std::shared_ptr& client); + /// Handles all packets which are allowed during the SessionSetup state. + void handle_session_setup(ClientID id, const bmp::Packet& packet, std::shared_ptr& client); + /// Handles all packets which are allowed during the Playing state. + void handle_playing(ClientID id, const bmp::Packet& packet, std::shared_ptr& client); /// On failure, throws an exception with the error for the client. static void authenticate_user(const std::string& public_key, std::shared_ptr& client); @@ -216,13 +219,7 @@ private: Sync> m_client_magics {}; Sync> m_udp_endpoints {}; - ClientID new_client_id() { - static Sync s_id { 0 }; - auto id = s_id.synchronize(); - ClientID new_id = *id; - *id += 1; - return new_id; - } + ClientID new_client_id(); thread_pool m_threadpool { std::thread::hardware_concurrency() }; Sync m_shutdown { false }; diff --git a/src/Network.cpp b/src/Network.cpp index 6651560..7f49006 100644 --- a/src/Network.cpp +++ b/src/Network.cpp @@ -607,13 +607,41 @@ void Network::handle_packet(ClientID id, const bmp::Packet& packet) { handle_mod_download(id, packet, client); break; case bmp::State::SessionSetup: + handle_session_setup(id, packet, client); break; case bmp::State::Playing: + handle_playing(id, packet, client); break; case bmp::State::Leaving: break; } } + +void Network::handle_playing(ClientID id, const bmp::Packet& packet, std::shared_ptr& client) { + switch (packet.purpose) { + + default: + beammp_errorf("Got 0x{:x} in state {}. This is not allowed. Disconnecting the client", uint16_t(packet.purpose), int(client->state.get())); + disconnect(id, "invalid purpose in current state"); + } +} + +void Network::handle_session_setup(ClientID id, const bmp::Packet& packet, std::shared_ptr& client) { + switch (packet.purpose) { + case bmp::Purpose::SessionReady: { + beammp_infof("Client {} is synced", id); + bmp::Packet state_change { + .purpose = bmp::Purpose::StateChangePlaying, + }; + client->tcp_write(state_change); + break; + } + default: + beammp_errorf("Got 0x{:x} in state {}. This is not allowed. Disconnecting the client", uint16_t(packet.purpose), int(client->state.get())); + disconnect(id, "invalid purpose in current state"); + } +} + void Network::handle_identification(ClientID id, const bmp::Packet& packet, std::shared_ptr& client) { switch (packet.purpose) { case bmp::ProtocolVersion: { @@ -733,6 +761,25 @@ void Network::handle_mod_download(ClientID id, const bmp::Packet& packet, std::s .purpose = bmp::Purpose::StateChangeSessionSetup, }; client->tcp_write(state_change); + + beammp_infof("Client {} starting session sync.", id); + beammp_debugf("Syncing {} client(s) and {} vehicle(s) to client {}", m_clients->size(), m_vehicles->size(), id); + // immediately start with the player+vehicle info + bmp::Packet players_info { + .purpose = bmp::Purpose::PlayersVehiclesInfo, + .raw_data = {}, + }; + try { + auto players_info_json = build_players_info(); + beammp_tracef("Players and vehicles info: {}", players_info_json.dump(4)); + auto serialized = players_info_json.dump(); + players_info.raw_data = std::vector(serialized.begin(), serialized.end()); + } catch (const std::exception& e) { + beammp_errorf("Failed to construct players and vehicles info for session setup: {}. This is not recoverable, kicking client.", e.what()); + disconnect(id, "Internal server error: Session setup failed to construct players and vehicles info object"); + return; + } + client->tcp_write(players_info); break; } default: @@ -928,3 +975,50 @@ size_t Network::clients_in_state_count(bmp::State state) const { } size_t Network::vehicle_count() const { return m_vehicles->size(); } +nlohmann::json Network::build_players_info() { + auto all = boost::synchronize(m_clients, m_vehicles); + auto& clients = std::get<0>(all); + auto& vehicles = std::get<1>(all); + nlohmann::json info = nlohmann::json::array(); + for (const auto& [id, client] : *clients) { + auto obj = nlohmann::json({ + { "name", client->name.get() }, + { "id", client->id }, + { "role", client->role }, + }); + obj["vehicles"] = nlohmann::json::array(); + // get all vehicles owned by this client + // cant use the helper function since that locks as well, that's not clean + for (const auto& [vid, vehicle] : *vehicles) { + if (vehicle->owner == id) { + // vehicle owned by client + auto data = vehicle->data.synchronize(); + auto status = vehicle->get_raw_status(); + obj["vehicles"] = nlohmann::json::object({ + { "id", vid }, + { "data", nlohmann::json::parse(data->begin(), data->end()) }, + { "status", nlohmann::json::parse(status.begin(), status.end()) }, + }); + } + } + info.push_back(std::move(obj)); + } + return info; +} + +std::optional Network::get_vehicle(VehicleID id) { + auto vehicles = m_vehicles.synchronize(); + if (vehicles->contains(id)) { + return vehicles->at(id); + } else { + return std::nullopt; + } +} + +ClientID Network::new_client_id() { + static Sync s_id { 0 }; + auto id = s_id.synchronize(); + ClientID new_id = *id; + *id += 1; + return new_id; +}