use std::collections::HashMap; use std::sync::Arc; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::{TcpListener, TcpStream}; use tokio::sync::RwLock; // Use Tokio's async-aware lock // Type alias for our shared thread-safe state type Db = Arc>>; #[tokio::main] async fn main() -> Result<(), Box> { // Bind to all interfaces on port 5901 let listener = TcpListener::bind("0.0.0.0:5901").await?; let db: Db = Arc::new(RwLock::new(HashMap::new())); println!("🚀 Escape Room Server active on port 5901"); println!("Waiting for teams to connect..."); loop { let (socket, _) = listener.accept().await?; let db_clone = Arc::clone(&db); // Spawn a new async task for every connection tokio::spawn(async move { if let Err(e) = handle_connection(socket, db_clone).await { eprintln!("[SOCKET ERROR] {}", e); } }); } } async fn handle_connection(mut socket: TcpStream, db: Db) -> std::io::Result<()> { let mut buf = [0; 1024]; let n = socket.read(&mut buf).await?; if n == 0 { return Ok(()); } let request = String::from_utf8_lossy(&buf[..n]); let parts: Vec<&str> = request.trim().split_whitespace().collect(); if parts.is_empty() { return Ok(()); } match parts[0].to_uppercase().as_str() { "PUT" => { if parts.len() == 3 { let team_id = parts[1].to_string(); let val_str = parts[2]; // Validate if the value is an integer if val_str.parse::().is_ok() { let mut data = db.write().await; data.insert(team_id.clone(), val_str.to_string()); socket.write_all(b"ACK\n").await?; println!("[LOG] Team {} submitted solution: {}", team_id, val_str); } else { // Send error but don't stop the server socket.write_all(b"ERR_NOT_AN_INT\n").await?; println!("[WARN] Team {} tried to send non-int: {}", team_id, val_str); } } } "GET" => { if parts.len() == 2 { let target_id = parts[1]; let data = db.read().await; let response = data .get(target_id) .cloned() .unwrap_or_else(|| "-1".to_string()); socket.write_all(response.as_bytes()).await?; } } "ADMIN" => { if parts.len() == 4 && parts[1].to_uppercase() == "SET" { let team_id = parts[2].to_string(); let val_str = parts[3]; if val_str.parse::().is_ok() { let mut data = db.write().await; data.insert(team_id.clone(), val_str.to_string()); socket.write_all(b"ADMIN_OK\n").await?; println!("[ADMIN] Manual set: Team {} -> {}", team_id, val_str); } else { socket.write_all(b"ADMIN_ERR_NOT_AN_INT\n").await?; println!("[ADMIN] Manual set failed: {} is not an int", val_str); } } } _ => { socket.write_all(b"ERR_INVALID_CMD\n").await?; } } Ok(()) }