summaryrefslogtreecommitdiffstats
path: root/src/bin/server/handler.rs
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-11-08 03:29:35 +0100
committersalmonik <salmonik@tuta.io>2026-11-08 03:29:35 +0100
commit058432130628d00764663ecf819a9adc6793768d (patch)
treed3f49f111fbfaa48647cca2600adcf9017af2d30 /src/bin/server/handler.rs
parentweb: serve latest.php through PHP and refuse stray PHP files (diff)
downloadzagan-bet-058432130628d00764663ecf819a9adc6793768d.tar.gz
zagan-bet-058432130628d00764663ecf819a9adc6793768d.zip
v5.0.0: protocol v5 and the browser frontend
Protocol v5 replaces newline lines with bounded [u32 len][text] frames and REQ/RES/EVT envelopes. Requests carry correlation ids and the server replays the last 256 replies per connection, so a gateway retry cannot double-debit and a late reply cannot clobber a newer request. HELLO/PONG carry server time; crash rounds announce started_at and push coalesced 10 Hz ticks. Crash now commits sha256(seed) before a round and reveals the seed when it ends, so clients recompute the crash point. Adds the webd/ gateway: one blocking thread, one ClientCore and one pinned upstream TLS connection per session, an HttpOnly session cookie, takeover, and a 60-second grace window. The browser sends Command JSON and renders View JSON; it never speaks the game protocol or stores credentials. Adds the vanilla TypeScript browser app at /play/ (login, lobby, all four games, history, notices, crash verification), the nginx /play and /ws routes, and the headless DOM smoke test. webd and the app stay out of the TUI/GUI/server build graph. The client/server version moves to 5.0.0 in lockstep; v4 peers are refused at HELLO.
Diffstat (limited to '')
-rw-r--r--src/bin/server/handler.rs47
1 files changed, 32 insertions, 15 deletions
diff --git a/src/bin/server/handler.rs b/src/bin/server/handler.rs
index dffae69..e6b623f 100644
--- a/src/bin/server/handler.rs
+++ b/src/bin/server/handler.rs
@@ -1,6 +1,7 @@
1use std::time::Duration; 1use std::time::Duration;
2 2
3use crate::tls::TlsStream; 3use crate::tls::TlsStream;
4use zagan_bet::protocol::{event_line, parse_request, reply_line};
4use zagan_bet::server::core::{ConnectionState, ServerCore}; 5use zagan_bet::server::core::{ConnectionState, ServerCore};
5 6
6/// How long the connection waits for a request before advancing timed games. 7/// How long the connection waits for a request before advancing timed games.
@@ -30,24 +31,35 @@ pub fn handle_client_tls(mut stream: TlsStream) {
30 } 31 }
31 32
32 let mut state = ConnectionState::default(); 33 let mut state = ConnectionState::default();
34 let mut event_seq = 0u64;
33 println!("[TLS Client connected] {client_addr}"); 35 println!("[TLS Client connected] {client_addr}");
34 36
35 'connection: loop { 37 'connection: loop {
36 let mut line = String::new(); 38 let mut frame = String::new();
37 match stream.read_line(&mut line) { 39 match stream.read_frame(&mut frame) {
38 Ok(0) => break, 40 Ok(0) => break,
39 Ok(_) => { 41 Ok(_) => {
40 let line = line.trim(); 42 let user = state.user().unwrap_or("guest").to_string();
41 if !line.is_empty() { 43 println!("[{user} @ {client_addr}] Received: {frame}");
42 let user = state.user().unwrap_or("guest").to_string(); 44 match parse_request(&frame) {
43 println!("[{user} @ {client_addr}] Received: {line}"); 45 Ok(request) => {
44 let response = core.handle_message(&mut state, line); 46 let response = core.handle_request(&mut state, &request);
45 if let Err(error) = stream.write_line(&response) { 47 let reply = reply_line(request.id, &response);
46 eprintln!("Write error: {error}"); 48 if let Err(error) = stream.write_frame(&reply) {
47 break; 49 eprintln!("Write error: {error}");
50 break;
51 }
52 let user = state.user().unwrap_or("guest");
53 println!("[{user} @ {client_addr}] Sent: {reply}");
54 }
55 Err(message) => {
56 let reply = reply_line(0, &format!("ERROR {message}"));
57 if let Err(error) = stream.write_frame(&reply) {
58 eprintln!("Write error: {error}");
59 break;
60 }
61 eprintln!("Malformed request from {client_addr}: {reply}");
48 } 62 }
49 let user = state.user().unwrap_or("guest");
50 println!("[{user} @ {client_addr}] Sent: {response}");
51 } 63 }
52 } 64 }
53 Err(error) 65 Err(error)
@@ -65,12 +77,17 @@ pub fn handle_client_tls(mut stream: TlsStream) {
65 } 77 }
66 78
67 for push in core.poll(&state) { 79 for push in core.poll(&state) {
68 if let Err(error) = stream.write_line(&push) { 80 event_seq = event_seq.wrapping_add(1);
81 let event = event_line(event_seq, &push);
82 if let Err(error) = stream.write_frame(&event) {
69 eprintln!("Write error: {error}"); 83 eprintln!("Write error: {error}");
70 break 'connection; 84 break 'connection;
71 } 85 }
72 let user = state.user().unwrap_or("guest"); 86 // Ticks are 10 Hz per live round; logging them floods the disk.
73 println!("[{user} @ {client_addr}] Pushed: {push}"); 87 if !push.starts_with("crash.tick ") {
88 let user = state.user().unwrap_or("guest");
89 println!("[{user} @ {client_addr}] Pushed: {event}");
90 }
74 } 91 }
75 } 92 }
76 93