diff --git a/src/main.rs b/src/main.rs index e7a11a9..004ee89 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,103 @@ -fn main() { - println!("Hello, world!"); +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?; + + // Connection closed + 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() { + // Teams submit their solution: PUT [team_id] [value] + "PUT" => { + if parts.len() == 3 { + let team_id = parts[1].to_string(); + let val = parts[2].to_string(); + + let mut data = db.write().await; // Async lock + data.insert(team_id.clone(), val.clone()); + + socket.write_all(b"ACK\n").await?; + println!("[LOG] Team {} submitted solution: {}", team_id, val); + } + } + + // Teams request previous solution: GET [prev_team_id] + "GET" => { + if parts.len() == 2 { + let target_id = parts[1]; + let data = db.read().await; // Async lock + + let response = data + .get(target_id) + .cloned() + .unwrap_or_else(|| "-1".to_string()); + + if response == "-1" { + println!("[WAIT] Team requested {}, but no data yet.", target_id); + } + + socket.write_all(response.as_bytes()).await?; + } + } + + // Admin override: ADMIN SET [team_id] [value] + "ADMIN" => { + if parts.len() == 4 && parts[1].to_uppercase() == "SET" { + let team_id = parts[2].to_string(); + let val = parts[3].to_string(); + + let mut data = db.write().await; + data.insert(team_id.clone(), val.clone()); + + socket.write_all(b"ADMIN_OK\n").await?; + println!("[ADMIN] Manual set: Team {} -> {}", team_id, val); + } + } + + _ => { + socket.write_all(b"ERR_INVALID_CMD\n").await?; + } + } + + Ok(()) +} +