implement session sync

This commit is contained in:
Lion Kortlepel 2024-01-22 16:56:07 +01:00
parent bde689d31a
commit 630d5f1cfa
No known key found for this signature in database
GPG Key ID: 4322FF2B4C71259B
2 changed files with 108 additions and 17 deletions

View File

@ -120,6 +120,8 @@ struct Vehicle {
m_status_data = raw_packet;
}
const std::vector<uint8_t>& get_raw_status() const { return m_status_data; }
private:
std::recursive_mutex m_mtx;
@ -163,14 +165,10 @@ public:
std::unordered_map<VehicleID, Vehicle::Ptr> get_vehicles_owned_by(ClientID id);
std::optional<Vehicle::Ptr> get_vehicle(VehicleID id) {
auto vehicles = m_vehicles.synchronize();
if (vehicles->contains(id)) {
return vehicles->at(id);
} else {
return std::nullopt;
}
}
std::optional<Vehicle::Ptr> 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>& client);
/// Handles all packets which are allowed during the Authentication state.
void handle_authentication(ClientID id, const bmp::Packet& packet, std::shared_ptr<Client>& 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>& 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>& client);
/// Handles all packets which are allowed during the Playing state.
void handle_playing(ClientID id, const bmp::Packet& packet, std::shared_ptr<Client>& 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>& client);
@ -216,13 +219,7 @@ private:
Sync<std::unordered_map<uint64_t, ClientID>> m_client_magics {};
Sync<std::unordered_map<ip::udp::endpoint, ClientID>> m_udp_endpoints {};
ClientID new_client_id() {
static Sync<ClientID> 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<bool> m_shutdown { false };

View File

@ -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>& 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>& 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>& 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<uint8_t>(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<Vehicle::Ptr> 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<ClientID> s_id { 0 };
auto id = s_id.synchronize();
ClientID new_id = *id;
*id += 1;
return new_id;
}