summaryrefslogtreecommitdiffstats
path: root/gui/src/net.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 /gui/src/net.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--gui/src/net.rs17
1 files changed, 10 insertions, 7 deletions
diff --git a/gui/src/net.rs b/gui/src/net.rs
index 7cba604..7882cd3 100644
--- a/gui/src/net.rs
+++ b/gui/src/net.rs
@@ -14,6 +14,7 @@ use eframe::egui;
14use rusqlite::Connection; 14use rusqlite::Connection;
15use zagan_bet::client::transport::{self, TlsStream, TransportError, TrustPrompt}; 15use zagan_bet::client::transport::{self, TlsStream, TransportError, TrustPrompt};
16use zagan_bet::client::{database, paths}; 16use zagan_bet::client::{database, paths};
17use zagan_bet::protocol::Packet;
17 18
18/// How often the worker checks the socket for pushes when idle. 19/// How often the worker checks the socket for pushes when idle.
19const POLL: Duration = Duration::from_millis(8); 20const POLL: Duration = Duration::from_millis(8);
@@ -24,7 +25,7 @@ const TRUST_TIMEOUT: Duration = Duration::from_secs(120);
24#[derive(Debug, Clone, PartialEq, Eq)] 25#[derive(Debug, Clone, PartialEq, Eq)]
25pub enum Command { 26pub enum Command {
26 Connect { addr: String }, 27 Connect { addr: String },
27 Send(String), 28 Send { id: u64, payload: String },
28 Disconnect, 29 Disconnect,
29 Stop, 30 Stop,
30} 31}
@@ -49,6 +50,7 @@ pub struct Connected {
49 pub latest: String, 50 pub latest: String,
50 pub url: String, 51 pub url: String,
51 pub elapsed_ms: f32, 52 pub elapsed_ms: f32,
53 pub server_time: u64,
52} 54}
53 55
54/// What the worker tells the UI. 56/// What the worker tells the UI.
@@ -65,7 +67,7 @@ pub enum Event {
65 failure: Failure, 67 failure: Failure,
66 message: String, 68 message: String,
67 }, 69 },
68 Line(String), 70 Packet(Packet),
69 Closed, 71 Closed,
70} 72}
71 73
@@ -164,6 +166,7 @@ fn worker(
164 latest: connected.latest, 166 latest: connected.latest,
165 url: connected.url, 167 url: connected.url,
166 elapsed_ms: connected.elapsed.as_secs_f64() as f32 * 1000.0, 168 elapsed_ms: connected.elapsed.as_secs_f64() as f32 * 1000.0,
169 server_time: connected.server_time,
167 })); 170 }));
168 stream = Some(connected.stream); 171 stream = Some(connected.stream);
169 } 172 }
@@ -177,9 +180,9 @@ fn worker(
177 } 180 }
178 ctx.request_repaint(); 181 ctx.request_repaint();
179 } 182 }
180 Ok(Command::Send(line)) => { 183 Ok(Command::Send { id, payload }) => {
181 if let Some(open) = &mut stream { 184 if let Some(open) = &mut stream {
182 if open.write_line(&line).is_err() { 185 if open.write_request(id, None, &payload).is_err() {
183 stream = None; 186 stream = None;
184 let _ = events.send(Event::Closed); 187 let _ = events.send(Event::Closed);
185 } 188 }
@@ -198,9 +201,9 @@ fn worker(
198 // Drain pushes and replies without blocking the command channel. 201 // Drain pushes and replies without blocking the command channel.
199 if let Some(open) = &mut stream { 202 if let Some(open) = &mut stream {
200 loop { 203 loop {
201 match open.read_available_line() { 204 match open.read_available_packet() {
202 Ok(Some(line)) => { 205 Ok(Some(packet)) => {
203 if events.send(Event::Line(line)).is_err() { 206 if events.send(Event::Packet(packet)).is_err() {
204 return; 207 return;
205 } 208 }
206 } 209 }