diff options
| author | salmonik <salmonik@tuta.io> | 2026-11-01 18:51:05 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-11-01 18:51:05 +0100 |
| commit | 8c93f69081f0012e9a97ffc59adb7129bc1593ee (patch) | |
| tree | 4dfe458ca2e76e4d9d2ad8f52c2684d5c83178c9 /src | |
| parent | v4.0.4: move the client transport and TOFU into the library (diff) | |
| download | zagan-bet-8c93f69081f0012e9a97ffc59adb7129bc1593ee.tar.gz zagan-bet-8c93f69081f0012e9a97ffc59adb7129bc1593ee.zip | |
v4.0.5: login and registration as core commands
Diffstat (limited to 'src')
| -rw-r--r-- | src/client/core.rs | 71 | ||||
| -rw-r--r-- | src/client/view.rs | 5 |
2 files changed, 74 insertions, 2 deletions
diff --git a/src/client/core.rs b/src/client/core.rs index 37ba916..98f109f 100644 --- a/src/client/core.rs +++ b/src/client/core.rs | |||
| @@ -66,7 +66,7 @@ pub enum Effect { | |||
| 66 | Quit, | 66 | Quit, |
| 67 | } | 67 | } |
| 68 | 68 | ||
| 69 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 69 | #[derive(Debug, Clone, PartialEq, Eq)] |
| 70 | enum Pending { | 70 | enum Pending { |
| 71 | Spin, | 71 | Spin, |
| 72 | RouletteSpin, | 72 | RouletteSpin, |
| @@ -77,6 +77,8 @@ enum Pending { | |||
| 77 | CrashCashout, | 77 | CrashCashout, |
| 78 | CrashForfeit, | 78 | CrashForfeit, |
| 79 | History, | 79 | History, |
| 80 | Login { user: String }, | ||
| 81 | Register, | ||
| 80 | Ping, | 82 | Ping, |
| 81 | Echo, | 83 | Echo, |
| 82 | Info, | 84 | Info, |
| @@ -105,6 +107,8 @@ pub struct ClientCore { | |||
| 105 | screen: Screen, | 107 | screen: Screen, |
| 106 | /// Last plays fetched with `history`. | 108 | /// Last plays fetched with `history`. |
| 107 | play_history: Vec<PlayRecord>, | 109 | play_history: Vec<PlayRecord>, |
| 110 | /// Why the last login or registration attempt failed. | ||
| 111 | login_error: Option<String>, | ||
| 108 | } | 112 | } |
| 109 | 113 | ||
| 110 | impl ClientCore { | 114 | impl ClientCore { |
| @@ -123,6 +127,7 @@ impl ClientCore { | |||
| 123 | crash_started: None, | 127 | crash_started: None, |
| 124 | screen: Screen::Lobby, | 128 | screen: Screen::Lobby, |
| 125 | play_history: Vec::new(), | 129 | play_history: Vec::new(), |
| 130 | login_error: None, | ||
| 126 | } | 131 | } |
| 127 | } | 132 | } |
| 128 | 133 | ||
| @@ -137,6 +142,9 @@ impl ClientCore { | |||
| 137 | } | 142 | } |
| 138 | 143 | ||
| 139 | pub fn set_logged_in(&mut self, user: Option<String>) { | 144 | pub fn set_logged_in(&mut self, user: Option<String>) { |
| 145 | if user.is_some() { | ||
| 146 | self.login_error = None; | ||
| 147 | } | ||
| 140 | self.logged_in = user; | 148 | self.logged_in = user; |
| 141 | } | 149 | } |
| 142 | 150 | ||
| @@ -312,6 +320,25 @@ impl ClientCore { | |||
| 312 | } | 320 | } |
| 313 | Err(message) => print_line(message), | 321 | Err(message) => print_line(message), |
| 314 | }, | 322 | }, |
| 323 | Some(Pending::Login { user }) => match auth_result(reply) { | ||
| 324 | Ok(()) => { | ||
| 325 | self.logged_in = Some(user.clone()); | ||
| 326 | self.login_error = None; | ||
| 327 | self.screen = Screen::Lobby; | ||
| 328 | print_line(format!("Logged in as {user}")) | ||
| 329 | } | ||
| 330 | Err(message) => { | ||
| 331 | self.login_error = Some(message.clone()); | ||
| 332 | print_line(format!("Authentication failed: {message}")) | ||
| 333 | } | ||
| 334 | }, | ||
| 335 | Some(Pending::Register) => match register_result(reply) { | ||
| 336 | Ok(()) => print_line("Account created. Log in to play."), | ||
| 337 | Err(message) => { | ||
| 338 | self.login_error = Some(message.clone()); | ||
| 339 | print_line(format!("Registration failed: {message}")) | ||
| 340 | } | ||
| 341 | }, | ||
| 315 | Some(Pending::Ping) => { | 342 | Some(Pending::Ping) => { |
| 316 | if reply.trim() == "PONG" { | 343 | if reply.trim() == "PONG" { |
| 317 | print_line("Pong") | 344 | print_line("Pong") |
| @@ -404,7 +431,23 @@ impl ClientCore { | |||
| 404 | let game = self.current_game_type().map(str::to_string); | 431 | let game = self.current_game_type().map(str::to_string); |
| 405 | 432 | ||
| 406 | match command { | 433 | match command { |
| 407 | Command::Play { game, bet } => self.play_game(&game, bet), | 434 | Command::Login { user, credential } => { |
| 435 | if !self.connected { | ||
| 436 | return print_line("Not connected. Use 'connect <ip>:<port>' first."); | ||
| 437 | } | ||
| 438 | self.login_error = None; | ||
| 439 | self.pending = Some(Pending::Login { user: user.clone() }); | ||
| 440 | vec![Effect::SendLine(format!("AUTH {user} {credential}"))] | ||
| 441 | } | ||
| 442 | Command::Register { user, credential } => { | ||
| 443 | if !self.connected { | ||
| 444 | return print_line("Not connected. Use 'connect <ip>:<port>' first."); | ||
| 445 | } | ||
| 446 | self.login_error = None; | ||
| 447 | self.pending = Some(Pending::Register); | ||
| 448 | vec![Effect::SendLine(format!("REGISTER {user} {credential}"))] | ||
| 449 | } | ||
| 450 | Command::Play { game, bet } => self.play_game(&game, bet), | ||
| 408 | Command::History { limit } => self.load_history(limit), | 451 | Command::History { limit } => self.load_history(limit), |
| 409 | Command::ShowLobby => { | 452 | Command::ShowLobby => { |
| 410 | self.screen = Screen::Lobby; | 453 | self.screen = Screen::Lobby; |
| @@ -412,6 +455,7 @@ impl ClientCore { | |||
| 412 | } | 455 | } |
| 413 | Command::Logout => { | 456 | Command::Logout => { |
| 414 | self.logged_in = None; | 457 | self.logged_in = None; |
| 458 | self.login_error = None; | ||
| 415 | self.screen = Screen::Lobby; | 459 | self.screen = Screen::Lobby; |
| 416 | print_line("Logged out") | 460 | print_line("Logged out") |
| 417 | } | 461 | } |
| @@ -1070,6 +1114,7 @@ impl ClientCore { | |||
| 1070 | .into_iter() | 1114 | .into_iter() |
| 1071 | .map(|(game, name)| (game.to_string(), name.to_string())) | 1115 | .map(|(game, name)| (game.to_string(), name.to_string())) |
| 1072 | .collect(), | 1116 | .collect(), |
| 1117 | login_error: self.login_error.clone(), | ||
| 1073 | }), | 1118 | }), |
| 1074 | }) | 1119 | }) |
| 1075 | } | 1120 | } |
| @@ -1086,6 +1131,28 @@ impl ClientCore { | |||
| 1086 | } | 1131 | } |
| 1087 | } | 1132 | } |
| 1088 | 1133 | ||
| 1134 | fn auth_result(reply: &str) -> Result<(), String> { | ||
| 1135 | let reply = reply.trim(); | ||
| 1136 | if reply.starts_with("OK") { | ||
| 1137 | return Ok(()); | ||
| 1138 | } | ||
| 1139 | Err(reply | ||
| 1140 | .strip_prefix("ERROR") | ||
| 1141 | .map(|message| message.trim().to_string()) | ||
| 1142 | .unwrap_or_else(|| reply.to_string())) | ||
| 1143 | } | ||
| 1144 | |||
| 1145 | fn register_result(reply: &str) -> Result<(), String> { | ||
| 1146 | let reply = reply.trim(); | ||
| 1147 | if reply.starts_with("REGISTERED") { | ||
| 1148 | return Ok(()); | ||
| 1149 | } | ||
| 1150 | Err(reply | ||
| 1151 | .strip_prefix("ERROR") | ||
| 1152 | .map(|message| message.trim().to_string()) | ||
| 1153 | .unwrap_or_else(|| reply.to_string())) | ||
| 1154 | } | ||
| 1155 | |||
| 1089 | fn print_line(message: impl Into<String>) -> Vec<Effect> { | 1156 | fn print_line(message: impl Into<String>) -> Vec<Effect> { |
| 1090 | vec![Effect::Print(vec![message.into()])] | 1157 | vec![Effect::Print(vec![message.into()])] |
| 1091 | } | 1158 | } |
diff --git a/src/client/view.rs b/src/client/view.rs index 903403c..9443726 100644 --- a/src/client/view.rs +++ b/src/client/view.rs | |||
| @@ -248,6 +248,8 @@ pub struct LobbyView { | |||
| 248 | pub user: Option<String>, | 248 | pub user: Option<String>, |
| 249 | pub balance: Option<i64>, | 249 | pub balance: Option<i64>, |
| 250 | pub games: Vec<(String, String)>, | 250 | pub games: Vec<(String, String)>, |
| 251 | /// Why the last login or registration attempt failed. | ||
| 252 | pub login_error: Option<String>, | ||
| 251 | } | 253 | } |
| 252 | 254 | ||
| 253 | /// The play log, already parsed. | 255 | /// The play log, already parsed. |
| @@ -270,6 +272,9 @@ pub enum View { | |||
| 270 | /// Semantic input. Frontends turn keys and clicks into these. | 272 | /// Semantic input. Frontends turn keys and clicks into these. |
| 271 | #[derive(Debug, Clone, PartialEq, Eq)] | 273 | #[derive(Debug, Clone, PartialEq, Eq)] |
| 272 | pub enum Command { | 274 | pub enum Command { |
| 275 | /// The credential is already hashed by the frontend. | ||
| 276 | Login { user: String, credential: String }, | ||
| 277 | Register { user: String, credential: String }, | ||
| 273 | Play { game: String, bet: Option<u64> }, | 278 | Play { game: String, bet: Option<u64> }, |
| 274 | History { limit: u32 }, | 279 | History { limit: u32 }, |
| 275 | ShowLobby, | 280 | ShowLobby, |
