chore: integrate multiplayer code (wip)
This commit is contained in:
parent
2838d59f30
commit
4f5e21becb
66 changed files with 6423 additions and 18 deletions
91
server/relay-server/src/lobby.rs
Normal file
91
server/relay-server/src/lobby.rs
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
//! This module handles game rooms where players connect and exchange messages.
|
||||
//! It provides:
|
||||
//! - [`Room`]: A game session with host-to-client broadcast channels
|
||||
//! - [`AppState`]: Global state holding all active rooms and game configurations
|
||||
//! - [`reload_config`]: Hot-reloading of game settings from `GameConfig.json`
|
||||
|
||||
use bytes::Bytes;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::SqlitePool;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use tokio::fs;
|
||||
use tokio::sync::{Mutex, RwLock};
|
||||
use tokio::sync::{broadcast, mpsc};
|
||||
|
||||
/// The game entry we have for one game.
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct GameEntry {
|
||||
/// The name of the game.
|
||||
pub name: String,
|
||||
/// The maximum amount of players (0 = no limit)
|
||||
pub max_players: u16,
|
||||
}
|
||||
|
||||
type EntryList = Vec<GameEntry>;
|
||||
|
||||
/// The description of the room, the players play in
|
||||
pub struct Room {
|
||||
/// The next id a client gets, this is consecutively counted.
|
||||
pub next_client_id: u16, // Needs Mutex
|
||||
/// The amount of players currently in the room.
|
||||
pub amount_of_players: u16, // Needs mutex.
|
||||
/// This is a status counter for rule variation in a game (like coop vs semi-coop).
|
||||
pub rule_variation: u16,
|
||||
/// The sender to send messages to the host.
|
||||
pub to_host_sender: mpsc::Sender<Bytes>, // Clone-able no Mutex!
|
||||
/// The broad case sender needed to subscribe for the clients.
|
||||
pub host_to_client_broadcaster: broadcast::Sender<Bytes>, // Clone-able -> no Mutex!
|
||||
/// Reconnect tokens keyed by player id. Used to authenticate reconnect attempts.
|
||||
pub player_tokens: HashMap<u16, u64>,
|
||||
/// Whether the host WebSocket is currently active. False during the grace period
|
||||
/// after host disconnect — the grace-period task will clean up the room if the
|
||||
/// host does not reconnect in time.
|
||||
pub host_connected: bool,
|
||||
/// IDs of non-host players whose WebSocket is currently active.
|
||||
/// Used to replay NEW_CLIENT / CLIENT_DISCONNECTS when the host reconnects.
|
||||
pub connected_players: Vec<u16>,
|
||||
/// Row id in `game_records` for this session. None when no authenticated player created the room.
|
||||
pub game_record_id: Option<i64>,
|
||||
/// Maps in-game player_id → database user_id. None means the player is anonymous.
|
||||
pub user_ids: HashMap<u16, Option<i64>>,
|
||||
}
|
||||
|
||||
/// The application state.
|
||||
pub struct AppState {
|
||||
/// The rooms we associate with several sessions.
|
||||
pub rooms: Mutex<HashMap<String, Room>>,
|
||||
/// Contains a mapping from game name to the maximum amount of players allowed.
|
||||
pub configs: RwLock<HashMap<String, u16>>,
|
||||
/// SQLite connection pool — shared across all request handlers.
|
||||
pub db: SqlitePool,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
pub fn new(db: SqlitePool) -> Self {
|
||||
Self {
|
||||
rooms: Mutex::new(HashMap::new()),
|
||||
configs: RwLock::new(HashMap::new()),
|
||||
db,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reloads the configuration file, that lists the games with the maximum number of players per room.
|
||||
pub async fn reload_config(state: &Arc<AppState>) -> Result<(), String> {
|
||||
let json_content = fs::read_to_string("GameConfig.json")
|
||||
.await
|
||||
.map_err(|e| format!("Failed to read file: {}", e))?;
|
||||
let raw_data: EntryList =
|
||||
serde_json::from_str(&json_content).map_err(|e| format!("Failed to parse JSON: {}", e))?;
|
||||
let new_configs: HashMap<String, u16> = raw_data
|
||||
.into_iter()
|
||||
.map(|entry| (entry.name, entry.max_players))
|
||||
.collect();
|
||||
|
||||
{
|
||||
let mut configs = state.configs.write().await;
|
||||
*configs = new_configs; // Replace all.
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue