diff options
| author | salmonik <salmonik@tuta.io> | 2026-11-01 17:49:08 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-11-01 17:49:08 +0100 |
| commit | 2fe2e8cdbc3bb520994ff0944188f2fa657c546a (patch) | |
| tree | 10f29b4dd62c2b8127d20f0bffd4325b3ca0bfd2 /src/server/core.rs | |
| parent | docs: add the phased GUI plan (diff) | |
| download | zagan-bet-2fe2e8cdbc3bb520994ff0944188f2fa657c546a.tar.gz zagan-bet-2fe2e8cdbc3bb520994ff0944188f2fa657c546a.zip | |
v4.0.0: require HELLO MAJOR.MINOR lockstep before auth
Diffstat (limited to 'src/server/core.rs')
| -rw-r--r-- | src/server/core.rs | 35 |
1 files changed, 35 insertions, 0 deletions
diff --git a/src/server/core.rs b/src/server/core.rs index 89264f3..3cd6469 100644 --- a/src/server/core.rs +++ b/src/server/core.rs | |||
| @@ -6,6 +6,7 @@ use std::sync::Arc; | |||
| 6 | use crate::blackjack::BlackjackState; | 6 | use crate::blackjack::BlackjackState; |
| 7 | use crate::crash::CrashState; | 7 | use crate::crash::CrashState; |
| 8 | use crate::server::database; | 8 | use crate::server::database; |
| 9 | use crate::version::{download_url, hello_line, Version, VERSION}; | ||
| 9 | use crate::server::games::blackjack::{BlackjackGame, BlackjackHand}; | 10 | use crate::server::games::blackjack::{BlackjackGame, BlackjackHand}; |
| 10 | use crate::server::games::crash::{Clock, CrashGame, CrashRound, RealClock}; | 11 | use crate::server::games::crash::{Clock, CrashGame, CrashRound, RealClock}; |
| 11 | use crate::server::games::registry::GameRegistry; | 12 | use crate::server::games::registry::GameRegistry; |
| @@ -15,6 +16,7 @@ use crate::server::games::traits::{GameError, GamePhase, GameState}; | |||
| 15 | #[derive(Debug, Default)] | 16 | #[derive(Debug, Default)] |
| 16 | pub struct ConnectionState { | 17 | pub struct ConnectionState { |
| 17 | user: Option<String>, | 18 | user: Option<String>, |
| 19 | greeted: bool, | ||
| 18 | } | 20 | } |
| 19 | 21 | ||
| 20 | /// Settle reservations left behind by a previous process. | 22 | /// Settle reservations left behind by a previous process. |
| @@ -32,6 +34,11 @@ impl ConnectionState { | |||
| 32 | pub fn user(&self) -> Option<&str> { | 34 | pub fn user(&self) -> Option<&str> { |
| 33 | self.user.as_deref() | 35 | self.user.as_deref() |
| 34 | } | 36 | } |
| 37 | |||
| 38 | /// True once the client has completed the version handshake. | ||
| 39 | pub fn greeted(&self) -> bool { | ||
| 40 | self.greeted | ||
| 41 | } | ||
| 35 | } | 42 | } |
| 36 | 43 | ||
| 37 | pub struct ServerCore { | 44 | pub struct ServerCore { |
| @@ -91,7 +98,14 @@ impl ServerCore { | |||
| 91 | 98 | ||
| 92 | pub fn handle_message(&mut self, state: &mut ConnectionState, msg: &str) -> String { | 99 | pub fn handle_message(&mut self, state: &mut ConnectionState, msg: &str) -> String { |
| 93 | let parts: Vec<&str> = msg.splitn(5, ' ').collect(); | 100 | let parts: Vec<&str> = msg.splitn(5, ' ').collect(); |
| 101 | |||
| 102 | // Nothing but the handshake is accepted on a fresh connection. | ||
| 103 | if parts[0] != "HELLO" && !state.greeted { | ||
| 104 | return "ERROR HELLO required".to_string(); | ||
| 105 | } | ||
| 106 | |||
| 94 | match parts[0] { | 107 | match parts[0] { |
| 108 | "HELLO" => self.handle_hello(state, &parts), | ||
| 95 | "GAME_LIST" => { | 109 | "GAME_LIST" => { |
| 96 | let mut games = self.registry.list_games(); | 110 | let mut games = self.registry.list_games(); |
| 97 | games.sort(); | 111 | games.sort(); |
| @@ -193,6 +207,27 @@ impl ServerCore { | |||
| 193 | } | 207 | } |
| 194 | } | 208 | } |
| 195 | 209 | ||
| 210 | fn handle_hello(&mut self, state: &mut ConnectionState, parts: &[&str]) -> String { | ||
| 211 | if parts.len() != 2 { | ||
| 212 | return "ERROR Invalid HELLO format. Usage: HELLO <version>".to_string(); | ||
| 213 | } | ||
| 214 | |||
| 215 | let (Some(client), Some(server)) = (Version::parse(parts[1]), Version::parse(VERSION)) else { | ||
| 216 | return "ERROR Invalid version".to_string(); | ||
| 217 | }; | ||
| 218 | |||
| 219 | if !client.compatible_with(server) { | ||
| 220 | return format!( | ||
| 221 | "ERROR Version mismatch: client {}, server {VERSION}. Get {VERSION} at {}", | ||
| 222 | parts[1], | ||
| 223 | download_url() | ||
| 224 | ); | ||
| 225 | } | ||
| 226 | |||
| 227 | state.greeted = true; | ||
| 228 | hello_line(VERSION, VERSION, &download_url()) | ||
| 229 | } | ||
| 230 | |||
| 196 | fn handle_register(&mut self, username: &str, credential: &str) -> String { | 231 | fn handle_register(&mut self, username: &str, credential: &str) -> String { |
| 197 | if database::get_hashed_password(&self.conn, username).is_some() { | 232 | if database::get_hashed_password(&self.conn, username).is_some() { |
| 198 | return "ERROR User already exists".to_string(); | 233 | return "ERROR User already exists".to_string(); |
