diff options
| author | salmonik <salmonik@tuta.io> | 2026-11-08 03:29:35 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-11-08 03:29:35 +0100 |
| commit | 058432130628d00764663ecf819a9adc6793768d (patch) | |
| tree | d3f49f111fbfaa48647cca2600adcf9017af2d30 /src/version.rs | |
| parent | web: serve latest.php through PHP and refuse stray PHP files (diff) | |
| download | zagan-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 'src/version.rs')
| -rw-r--r-- | src/version.rs | 34 |
1 files changed, 30 insertions, 4 deletions
diff --git a/src/version.rs b/src/version.rs index c1dcee5..adc2a8d 100644 --- a/src/version.rs +++ b/src/version.rs | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | //! accepted until the server has answered it. | 5 | //! accepted until the server has answered it. |
| 6 | 6 | ||
| 7 | use std::env; | 7 | use std::env; |
| 8 | use std::time::{SystemTime, UNIX_EPOCH}; | ||
| 8 | 9 | ||
| 9 | /// Version this build reports. The client and the server share it. | 10 | /// Version this build reports. The client and the server share it. |
| 10 | pub const VERSION: &str = env!("CARGO_PKG_VERSION"); | 11 | pub const VERSION: &str = env!("CARGO_PKG_VERSION"); |
| @@ -73,9 +74,9 @@ pub fn is_newer(latest: &str, current: &str) -> bool { | |||
| 73 | } | 74 | } |
| 74 | } | 75 | } |
| 75 | 76 | ||
| 76 | /// The server line that accepts a handshake. | 77 | /// The server line that accepts a handshake. `time` is Unix milliseconds. |
| 77 | pub fn hello_line(version: &str, latest: &str, url: &str) -> String { | 78 | pub fn hello_line(version: &str, latest: &str, url: &str, time: u64) -> String { |
| 78 | format!("HELLO {version} latest={latest} url={url}") | 79 | format!("HELLO {version} latest={latest} url={url} time={time}") |
| 79 | } | 80 | } |
| 80 | 81 | ||
| 81 | #[derive(Debug, Clone, PartialEq, Eq)] | 82 | #[derive(Debug, Clone, PartialEq, Eq)] |
| @@ -83,9 +84,11 @@ pub struct HelloReply { | |||
| 83 | pub version: String, | 84 | pub version: String, |
| 84 | pub latest: String, | 85 | pub latest: String, |
| 85 | pub url: String, | 86 | pub url: String, |
| 87 | /// Server clock at the moment the reply was built, Unix milliseconds. | ||
| 88 | pub time: u64, | ||
| 86 | } | 89 | } |
| 87 | 90 | ||
| 88 | /// Parse `HELLO <version> latest=<version> url=<url>`. | 91 | /// Parse `HELLO <version> latest=<version> url=<url> time=<unix_ms>`. |
| 89 | pub fn parse_hello(line: &str) -> Option<HelloReply> { | 92 | pub fn parse_hello(line: &str) -> Option<HelloReply> { |
| 90 | let rest = line.trim().strip_prefix("HELLO ")?; | 93 | let rest = line.trim().strip_prefix("HELLO ")?; |
| 91 | let mut tokens = rest.split_whitespace(); | 94 | let mut tokens = rest.split_whitespace(); |
| @@ -93,11 +96,14 @@ pub fn parse_hello(line: &str) -> Option<HelloReply> { | |||
| 93 | 96 | ||
| 94 | let mut latest = None; | 97 | let mut latest = None; |
| 95 | let mut url = None; | 98 | let mut url = None; |
| 99 | let mut time = None; | ||
| 96 | for token in tokens { | 100 | for token in tokens { |
| 97 | if let Some(value) = token.strip_prefix("latest=") { | 101 | if let Some(value) = token.strip_prefix("latest=") { |
| 98 | latest = Some(value.to_string()); | 102 | latest = Some(value.to_string()); |
| 99 | } else if let Some(value) = token.strip_prefix("url=") { | 103 | } else if let Some(value) = token.strip_prefix("url=") { |
| 100 | url = Some(value.to_string()); | 104 | url = Some(value.to_string()); |
| 105 | } else if let Some(value) = token.strip_prefix("time=") { | ||
| 106 | time = value.parse().ok(); | ||
| 101 | } | 107 | } |
| 102 | } | 108 | } |
| 103 | 109 | ||
| @@ -105,9 +111,29 @@ pub fn parse_hello(line: &str) -> Option<HelloReply> { | |||
| 105 | version, | 111 | version, |
| 106 | latest: latest?, | 112 | latest: latest?, |
| 107 | url: url?, | 113 | url: url?, |
| 114 | time: time?, | ||
| 108 | }) | 115 | }) |
| 109 | } | 116 | } |
| 110 | 117 | ||
| 118 | /// The reply to `PING`: `PONG <unix_ms>`. | ||
| 119 | pub fn pong_line(time: u64) -> String { | ||
| 120 | format!("PONG {time}") | ||
| 121 | } | ||
| 122 | |||
| 123 | /// Parse a `PONG` reply's server time. | ||
| 124 | pub fn parse_pong(line: &str) -> Option<u64> { | ||
| 125 | line.trim().strip_prefix("PONG ")?.trim().parse().ok() | ||
| 126 | } | ||
| 127 | |||
| 128 | /// Current Unix time in milliseconds. Used for handshake timestamps and crash | ||
| 129 | /// anchors; a system clock before the epoch reads as zero. | ||
| 130 | pub fn unix_time_ms() -> u64 { | ||
| 131 | SystemTime::now() | ||
| 132 | .duration_since(UNIX_EPOCH) | ||
| 133 | .map(|duration| duration.as_millis().min(u128::from(u64::MAX)) as u64) | ||
| 134 | .unwrap_or(0) | ||
| 135 | } | ||
| 136 | |||
| 111 | /// Download page advertised by the server. `DOWNLOAD_URL` wins when set. | 137 | /// Download page advertised by the server. `DOWNLOAD_URL` wins when set. |
| 112 | pub fn download_url() -> String { | 138 | pub fn download_url() -> String { |
| 113 | env::var("DOWNLOAD_URL") | 139 | env::var("DOWNLOAD_URL") |
