summaryrefslogtreecommitdiffstats
path: root/src/bin/server/handler.rs
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-08-24 14:48:12 +0200
committersalmonik <salmonik@tuta.io>2026-08-24 14:48:12 +0200
commit5d6b5049c2a34a3f8abe725d4a871ba84a0dd714 (patch)
tree57cb03bf59b3358a96742f455d38c21f4f645396 /src/bin/server/handler.rs
parentstable and working, both platforms (diff)
downloadzagan-bet-5d6b5049c2a34a3f8abe725d4a871ba84a0dd714.tar.gz
zagan-bet-5d6b5049c2a34a3f8abe725d4a871ba84a0dd714.zip
TLS transport and games framework on client and server
Diffstat (limited to 'src/bin/server/handler.rs')
-rw-r--r--src/bin/server/handler.rs177
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 @@
1use crate::database;
2use crate::games::registry::get_game_registry;
3use crate::games::traits::GameError;
4use crate::tls::TlsStream;
5use once_cell::sync::Lazy;
6use rusqlite::Connection;
1use std::io::{BufRead, BufReader, Write}; 7use std::io::{BufRead, BufReader, Write};
2use std::net::TcpStream; 8use std::net::TcpStream;
3use std::sync::Mutex; 9use std::sync::Mutex;
4use once_cell::sync::Lazy;
5use rusqlite::Connection;
6use crate::database;
7 10
8static DB_CONN: Lazy<Mutex<Connection>> = Lazy::new(|| Mutex::new(database::init_db())); 11static DB_CONN: Lazy<Mutex<Connection>> = Lazy::new(|| Mutex::new(database::init_db()));
9 12
13#[allow(dead_code)]
10pub fn handle_client(stream: TcpStream) { 14pub 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
27fn process_message(msg: &str, user: &mut String) -> String { 31fn 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
127fn 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
212pub 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}