diff options
Diffstat (limited to '')
| -rw-r--r-- | src/bin/server/handler.rs | 177 |
1 files changed, 171 insertions, 6 deletions
diff --git a/src/bin/server/handler.rs b/src/bin/server/handler.rs index 01fd9c9..867910e 100644 --- a/src/bin/server/handler.rs +++ b/src/bin/server/handler.rs | |||
| @@ -1,12 +1,16 @@ | |||
| 1 | use crate::database; | ||
| 2 | use crate::games::registry::get_game_registry; | ||
| 3 | use crate::games::traits::GameError; | ||
| 4 | use crate::tls::TlsStream; | ||
| 5 | use once_cell::sync::Lazy; | ||
| 6 | use rusqlite::Connection; | ||
| 1 | use std::io::{BufRead, BufReader, Write}; | 7 | use std::io::{BufRead, BufReader, Write}; |
| 2 | use std::net::TcpStream; | 8 | use std::net::TcpStream; |
| 3 | use std::sync::Mutex; | 9 | use std::sync::Mutex; |
| 4 | use once_cell::sync::Lazy; | ||
| 5 | use rusqlite::Connection; | ||
| 6 | use crate::database; | ||
| 7 | 10 | ||
| 8 | static DB_CONN: Lazy<Mutex<Connection>> = Lazy::new(|| Mutex::new(database::init_db())); | 11 | static DB_CONN: Lazy<Mutex<Connection>> = Lazy::new(|| Mutex::new(database::init_db())); |
| 9 | 12 | ||
| 13 | #[allow(dead_code)] | ||
| 10 | pub fn handle_client(stream: TcpStream) { | 14 | pub fn handle_client(stream: TcpStream) { |
| 11 | let client_addr = stream.peer_addr().unwrap().to_string(); | 15 | let client_addr = stream.peer_addr().unwrap().to_string(); |
| 12 | let mut user = "guest".to_string(); | 16 | let mut user = "guest".to_string(); |
| @@ -25,8 +29,49 @@ pub fn handle_client(stream: TcpStream) { | |||
| 25 | } | 29 | } |
| 26 | 30 | ||
| 27 | fn process_message(msg: &str, user: &mut String) -> String { | 31 | fn process_message(msg: &str, user: &mut String) -> String { |
| 28 | let parts: Vec<&str> = msg.splitn(3, ' ').collect(); | 32 | let parts: Vec<&str> = msg.splitn(5, ' ').collect(); |
| 29 | match parts[0] { | 33 | match parts[0] { |
| 34 | "GAME_LIST" => { | ||
| 35 | let registry = get_game_registry(); | ||
| 36 | let games: Vec<String> = registry.list_games(); | ||
| 37 | if games.is_empty() { | ||
| 38 | "ERROR No games available".to_string() | ||
| 39 | } else { | ||
| 40 | let game_info: Vec<String> = games | ||
| 41 | .iter() | ||
| 42 | .filter_map(|gt| { | ||
| 43 | registry | ||
| 44 | .get_game(gt) | ||
| 45 | .map(|g| format!("{}:{}", gt, g.get_name())) | ||
| 46 | }) | ||
| 47 | .collect(); | ||
| 48 | format!("GAME_LIST {}", game_info.join(",")) | ||
| 49 | } | ||
| 50 | } | ||
| 51 | "GAME_INFO" => { | ||
| 52 | if parts.len() != 2 { | ||
| 53 | "ERROR Invalid GAME_INFO format. Usage: GAME_INFO <game_type>".to_string() | ||
| 54 | } else { | ||
| 55 | let registry = get_game_registry(); | ||
| 56 | match registry.get_game(parts[1]) { | ||
| 57 | Some(game) => format!( | ||
| 58 | "GAME_INFO {} {} {}", | ||
| 59 | parts[1], | ||
| 60 | game.get_name(), | ||
| 61 | game.get_description() | ||
| 62 | ), | ||
| 63 | None => format!("ERROR Unknown game type: {}", parts[1]), | ||
| 64 | } | ||
| 65 | } | ||
| 66 | } | ||
| 67 | "PLAY_START" => { | ||
| 68 | if parts.len() != 3 { | ||
| 69 | "ERROR Invalid PLAY_START format. Usage: PLAY_START <game_type> <bet_amount>" | ||
| 70 | .to_string() | ||
| 71 | } else { | ||
| 72 | handle_play_start(user, parts[1], parts[2]) | ||
| 73 | } | ||
| 74 | } | ||
| 30 | "REGISTER" => { | 75 | "REGISTER" => { |
| 31 | if parts.len() != 3 { | 76 | if parts.len() != 3 { |
| 32 | "ERROR Invalid REGISTER format".to_string() | 77 | "ERROR Invalid REGISTER format".to_string() |
| @@ -65,13 +110,133 @@ fn process_message(msg: &str, user: &mut String) -> String { | |||
| 65 | "ERROR Not logged in".to_string() | 110 | "ERROR Not logged in".to_string() |
| 66 | } else { | 111 | } else { |
| 67 | let db = DB_CONN.lock().unwrap(); | 112 | let db = DB_CONN.lock().unwrap(); |
| 68 | if let (Some(id), Some(balance)) = (database::get_account_id(&db, user), database::get_balance(&db, user)) { | 113 | if let (Some(id), Some(balance)) = ( |
| 114 | database::get_account_id(&db, user), | ||
| 115 | database::get_balance(&db, user), | ||
| 116 | ) { | ||
| 69 | format!("INFO {} {} {}", user, id, balance) | 117 | format!("INFO {} {} {}", user, id, balance) |
| 70 | } else { | 118 | } else { |
| 71 | "ERROR Error retrieving user info".to_string() | 119 | "ERROR Error retrieving user info".to_string() |
| 72 | } | 120 | } |
| 73 | } | 121 | } |
| 74 | } | 122 | } |
| 75 | _ => msg.to_string(), // Echo back for compatibility | 123 | _ => msg.to_string(), |
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | fn handle_play_start(user: &str, game_type: &str, bet_amount_str: &str) -> String { | ||
| 128 | if user == "guest" { | ||
| 129 | return "ERROR Not logged in".to_string(); | ||
| 130 | } | ||
| 131 | |||
| 132 | let bet_amount: u64 = match bet_amount_str.parse() { | ||
| 133 | Ok(amount) => amount, | ||
| 134 | Err(_) => return "ERROR Invalid bet amount".to_string(), | ||
| 135 | }; | ||
| 136 | |||
| 137 | let db = DB_CONN.lock().unwrap(); | ||
| 138 | let current_balance = match database::get_balance(&db, user) { | ||
| 139 | Some(balance) => balance as u64, | ||
| 140 | None => return "ERROR Could not retrieve balance".to_string(), | ||
| 141 | }; | ||
| 142 | |||
| 143 | let registry = get_game_registry(); | ||
| 144 | let game = match registry.get_game(game_type) { | ||
| 145 | Some(game) => game, | ||
| 146 | None => return format!("ERROR Unknown game type: {}", game_type), | ||
| 147 | }; | ||
| 148 | |||
| 149 | if let Err(e) = game.validate_bet(bet_amount, current_balance) { | ||
| 150 | return match e { | ||
| 151 | GameError::InsufficientBalance => "ERROR Insufficient balance".to_string(), | ||
| 152 | GameError::InvalidBet => "ERROR Invalid bet amount".to_string(), | ||
| 153 | _ => "ERROR Bet validation failed".to_string(), | ||
| 154 | }; | ||
| 155 | } | ||
| 156 | |||
| 157 | let game_state = game.prepare_state(user, bet_amount); | ||
| 158 | |||
| 159 | drop(db); | ||
| 160 | let db = DB_CONN.lock().unwrap(); | ||
| 161 | if let Err(_) = db.execute( | ||
| 162 | "UPDATE users SET balance = balance - ?1 WHERE username = ?2", | ||
| 163 | (bet_amount as i64, user), | ||
| 164 | ) { | ||
| 165 | return "ERROR Failed to deduct bet".to_string(); | ||
| 166 | } | ||
| 167 | |||
| 168 | let new_state = match game.process_action(&game_state, "spin", &[]) { | ||
| 169 | Ok(state) => state, | ||
| 170 | Err(e) => { | ||
| 171 | let _ = db.execute( | ||
| 172 | "UPDATE users SET balance = balance + ?1 WHERE username = ?2", | ||
| 173 | (bet_amount as i64, user), | ||
| 174 | ); | ||
| 175 | return format!("ERROR Failed to spin: {:?}", e); | ||
| 176 | } | ||
| 177 | }; | ||
| 178 | |||
| 179 | let result = match game.calculate_result(&new_state) { | ||
| 180 | Ok(result) => result, | ||
| 181 | Err(e) => { | ||
| 182 | let _ = db.execute( | ||
| 183 | "UPDATE users SET balance = balance + ?1 WHERE username = ?2", | ||
| 184 | (bet_amount as i64, user), | ||
| 185 | ); | ||
| 186 | return format!("ERROR Failed to calculate result: {:?}", e); | ||
| 187 | } | ||
| 188 | }; | ||
| 189 | |||
| 190 | let total_return = result.bet_amount + result.winnings; | ||
| 191 | if let Err(_) = db.execute( | ||
| 192 | "UPDATE users SET balance = balance + ?1 WHERE username = ?2", | ||
| 193 | (total_return as i64, user), | ||
| 194 | ) { | ||
| 195 | return "ERROR Failed to update balance".to_string(); | ||
| 196 | } | ||
| 197 | |||
| 198 | let new_balance = match database::get_balance(&db, user) { | ||
| 199 | Some(balance) => balance, | ||
| 200 | None => return "ERROR Could not retrieve new balance".to_string(), | ||
| 201 | }; | ||
| 202 | |||
| 203 | let reels = new_state.data.get("reels").cloned().unwrap_or_default(); | ||
| 204 | let win_type = new_state.data.get("win_type").cloned().unwrap_or_default(); | ||
| 205 | |||
| 206 | format!( | ||
| 207 | "GAME_RESULT {} {} {} {} {} {}", | ||
| 208 | result.session_id, result.winnings, result.bet_amount, new_balance, reels, win_type | ||
| 209 | ) | ||
| 210 | } | ||
| 211 | |||
| 212 | pub fn handle_client_tls(mut stream: TlsStream) { | ||
| 213 | let client_addr = stream.peer_addr().unwrap().to_string(); | ||
| 214 | let mut user = "guest".to_string(); | ||
| 215 | println!("[{} @ {}] TLS Client connected", user, client_addr); | ||
| 216 | |||
| 217 | loop { | ||
| 218 | let mut line = String::new(); | ||
| 219 | match stream.read_line(&mut line) { | ||
| 220 | Ok(0) => break, | ||
| 221 | Ok(_) => {} | ||
| 222 | Err(e) => { | ||
| 223 | eprintln!("Read error: {}", e); | ||
| 224 | break; | ||
| 225 | } | ||
| 226 | } | ||
| 227 | |||
| 228 | let line = line.trim(); | ||
| 229 | if line.is_empty() { | ||
| 230 | continue; | ||
| 231 | } | ||
| 232 | |||
| 233 | println!("[{} @ {}] Received: {}", user, client_addr, line); | ||
| 234 | let response = process_message(line, &mut user); | ||
| 235 | if let Err(e) = stream.write_line(&response) { | ||
| 236 | eprintln!("Write error: {}", e); | ||
| 237 | break; | ||
| 238 | } | ||
| 239 | println!("[{} @ {}] Sent: {}", user, client_addr, response); | ||
| 76 | } | 240 | } |
| 241 | println!("[{} @ {}] TLS Client disconnected", user, client_addr); | ||
| 77 | } | 242 | } |
