summaryrefslogtreecommitdiffstats
path: root/src/server
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-11-01 17:49:08 +0100
committersalmonik <salmonik@tuta.io>2026-11-01 17:49:08 +0100
commit2fe2e8cdbc3bb520994ff0944188f2fa657c546a (patch)
tree10f29b4dd62c2b8127d20f0bffd4325b3ca0bfd2 /src/server
parentdocs: add the phased GUI plan (diff)
downloadzagan-bet-2fe2e8cdbc3bb520994ff0944188f2fa657c546a.tar.gz
zagan-bet-2fe2e8cdbc3bb520994ff0944188f2fa657c546a.zip
v4.0.0: require HELLO MAJOR.MINOR lockstep before auth
Diffstat (limited to '')
-rw-r--r--src/server/core.rs35
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;
6use crate::blackjack::BlackjackState; 6use crate::blackjack::BlackjackState;
7use crate::crash::CrashState; 7use crate::crash::CrashState;
8use crate::server::database; 8use crate::server::database;
9use crate::version::{download_url, hello_line, Version, VERSION};
9use crate::server::games::blackjack::{BlackjackGame, BlackjackHand}; 10use crate::server::games::blackjack::{BlackjackGame, BlackjackHand};
10use crate::server::games::crash::{Clock, CrashGame, CrashRound, RealClock}; 11use crate::server::games::crash::{Clock, CrashGame, CrashRound, RealClock};
11use crate::server::games::registry::GameRegistry; 12use 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)]
16pub struct ConnectionState { 17pub 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
37pub struct ServerCore { 44pub 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();