From bbecae5e0d827060af908f7d901e49b9690eeba2 Mon Sep 17 00:00:00 2001 From: SinuS Von SifriduS Date: Mon, 21 Sep 2026 17:45:12 +0200 Subject: [PATCH] 0.3.4-alpha.3.fix.1 --- Cargo.toml | 8 +- README.md | 6 +- .../game-realtime-websocket-lib/Cargo.toml | 6 +- .../game-realtime-websocket-lib/src/lib.rs | 8 +- .../src/websocket.rs | 41 ++++----- .../tests/loopback.rs | 7 +- deltas/0.3.4/alpha.3.fix.1.md | 87 +++++++++++++++++++ ...0_3_4_REALTIME_TRANSPORT_WEBSOCKET_PLAN.md | 6 +- 8 files changed, 123 insertions(+), 46 deletions(-) create mode 100644 deltas/0.3.4/alpha.3.fix.1.md diff --git a/Cargo.toml b/Cargo.toml index e6e9e27..09d9414 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ # file: Cargo.toml -# version: 88 +# version: 89 [workspace] resolver = "3" @@ -23,7 +23,7 @@ members = [ ] [workspace.package] -version = "0.3.4-alpha.3" +version = "0.3.4-alpha.3.fix.1" edition = "2024" license = "MIT" repository = "https://git.sasedev.com/Sasedev/games" @@ -31,7 +31,7 @@ authors = ["Sasedev "] publish = false [workspace.dependencies] -futures-util = "0.3.34" +futures-util = { version = "0.3.34", default-features = false } serde = { version = "1", features = ["derive"] } sdl3 = "^0.20" tracing = "0.1.44" @@ -42,7 +42,7 @@ tauri = "2" tauri-build = "2" tauri-plugin-tracing = "^0.3" tokio = "1.53.1" -tokio-tungstenite = "0.30.0" +tokio-tungstenite = { version = "0.30.0", default-features = false } wasm-bindgen = "0.2" [workspace.lints.rust] diff --git a/README.md b/README.md index e31cdab..ddeecd1 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ - + # games.sasedev @@ -27,9 +27,9 @@ Workspace expérimental puis productif pour des jeux multiplateformes principale Version stable de référence : `0.3.3`. -Version candidate active : `0.3.4-alpha.3`. `0.3.2` reste différée. +Version candidate active : `0.3.4-alpha.3.fix.1`. `0.3.2` reste différée. -La stable `0.3.3` livre la voie Android SDL3 native multi-ABI : build Gradle/Cargo sans orchestrateur Python, APK Debug universal et AAB Release pour `arm64-v8a`, `armeabi-v7a`, `x86_64` et `x86`, `minSdk 21` réellement fumé et compatibilité pages mémoire 16 KB validée sur les ABI 64 bits. `0.3.4-alpha.3` conserve l'API transport-neutral de `alpha.2` et ajoute un backend WebSocket séparé fondé sur Tokio + `tokio-tungstenite`, avec client `ws://`, listener serveur, split sender/receiver, tracing et round-trip localhost déterministe. +La stable `0.3.3` livre la voie Android SDL3 native multi-ABI : build Gradle/Cargo sans orchestrateur Python, APK Debug universal et AAB Release pour `arm64-v8a`, `armeabi-v7a`, `x86_64` et `x86`, `minSdk 21` réellement fumé et compatibilité pages mémoire 16 KB validée sur les ABI 64 bits. `0.3.4-alpha.3.fix.1` corrige l'héritage Cargo des options `default-features = false` de `futures-util` et `tokio-tungstenite` sans changer le scope fonctionnel de `alpha.3` : API transport-neutral conservée, backend WebSocket séparé fondé sur Tokio + `tokio-tungstenite`, client `ws://`, listener serveur, split sender/receiver, tracing et round-trip localhost déterministe. Les deux premiers jeux sont des POC structurels : `game-reflex-poc` et `game-snake-poc`. Ils existent d'abord pour valider les frontières du workspace, le moteur, les assets et le packaging multiplateforme. diff --git a/crates/common/game-realtime-websocket-lib/Cargo.toml b/crates/common/game-realtime-websocket-lib/Cargo.toml index 34bc3eb..2511a43 100644 --- a/crates/common/game-realtime-websocket-lib/Cargo.toml +++ b/crates/common/game-realtime-websocket-lib/Cargo.toml @@ -1,5 +1,5 @@ # file: crates/common/game-realtime-websocket-lib/Cargo.toml -# version: 1 +# version: 2 [package] name = "game-realtime-websocket-lib" @@ -11,10 +11,10 @@ authors.workspace = true publish.workspace = true [dependencies] -futures-util = { workspace = true, default-features = false, features = ["sink", "std"] } +futures-util = { workspace = true, features = ["sink", "std"] } game-realtime-transport-lib = { path = "../game-realtime-transport-lib" } tokio = { workspace = true, features = ["net"] } -tokio-tungstenite = { workspace = true, default-features = false, features = ["connect", "handshake"] } +tokio-tungstenite = { workspace = true, features = ["connect", "handshake"] } tracing.workspace = true [dev-dependencies] diff --git a/crates/common/game-realtime-websocket-lib/src/lib.rs b/crates/common/game-realtime-websocket-lib/src/lib.rs index 9c763ad..c255bda 100644 --- a/crates/common/game-realtime-websocket-lib/src/lib.rs +++ b/crates/common/game-realtime-websocket-lib/src/lib.rs @@ -9,13 +9,13 @@ mod websocket; -/// Re-export of the WebSocket client connection constructor. -pub use self::websocket::connect; /// Re-export of an established WebSocket transport connection. pub use self::websocket::WebSocketConnection; -/// Re-export of the receive half of an established WebSocket connection. -pub use self::websocket::WebSocketReceiver; /// Re-export of a bound WebSocket server listener. pub use self::websocket::WebSocketListener; +/// Re-export of the receive half of an established WebSocket connection. +pub use self::websocket::WebSocketReceiver; /// Re-export of the send half of an established WebSocket connection. pub use self::websocket::WebSocketSender; +/// Re-export of the WebSocket client connection constructor. +pub use self::websocket::connect; diff --git a/crates/common/game-realtime-websocket-lib/src/websocket.rs b/crates/common/game-realtime-websocket-lib/src/websocket.rs index 5ce96a5..e987084 100644 --- a/crates/common/game-realtime-websocket-lib/src/websocket.rs +++ b/crates/common/game-realtime-websocket-lib/src/websocket.rs @@ -73,8 +73,14 @@ pub struct WebSocketSender { } impl game_realtime_transport_lib::RealtimeSender for WebSocketSender { - type SendFuture<'a> = futures_util::future::LocalBoxFuture<'a, Result<(), game_realtime_transport_lib::TransportError>> where Self: 'a; - type CloseFuture<'a> = futures_util::future::LocalBoxFuture<'a, Result<(), game_realtime_transport_lib::TransportError>> where Self: 'a; + type SendFuture<'a> + = futures_util::future::LocalBoxFuture<'a, Result<(), game_realtime_transport_lib::TransportError>> + where + Self: 'a; + type CloseFuture<'a> + = futures_util::future::LocalBoxFuture<'a, Result<(), game_realtime_transport_lib::TransportError>> + where + Self: 'a; fn send(&mut self, message: game_realtime_transport_lib::TransportMessage) -> Self::SendFuture<'_> { return Box::pin(async move { @@ -125,8 +131,8 @@ pub struct WebSocketReceiver { } impl game_realtime_transport_lib::RealtimeReceiver for WebSocketReceiver { - type ReceiveFuture<'a> = - futures_util::future::LocalBoxFuture<'a, Result> + type ReceiveFuture<'a> + = futures_util::future::LocalBoxFuture<'a, Result> where Self: 'a; @@ -140,16 +146,13 @@ impl game_realtime_transport_lib::RealtimeReceiver for WebSocketReceiver { match next_message { Some(Ok(tokio_tungstenite::tungstenite::Message::Binary(bytes))) => { tracing::trace!(target: TRACING_TARGET, payload_len = bytes.len(), "binary WebSocket payload received"); - return Ok(game_realtime_transport_lib::TransportReceive::Message( - game_realtime_transport_lib::TransportMessage::new(bytes.to_vec()), - )); + return Ok(game_realtime_transport_lib::TransportReceive::Message(game_realtime_transport_lib::TransportMessage::new(bytes.to_vec()))); }, Some(Ok(tokio_tungstenite::tungstenite::Message::Close(_))) => { tracing::debug!(target: TRACING_TARGET, "remote WebSocket close observed"); return Ok(game_realtime_transport_lib::TransportReceive::Closed); }, - Some(Ok(tokio_tungstenite::tungstenite::Message::Ping(_))) - | Some(Ok(tokio_tungstenite::tungstenite::Message::Pong(_))) => {}, + Some(Ok(tokio_tungstenite::tungstenite::Message::Ping(_))) | Some(Ok(tokio_tungstenite::tungstenite::Message::Pong(_))) => {}, Some(Ok(tokio_tungstenite::tungstenite::Message::Text(_))) => { let error = game_realtime_transport_lib::TransportError::new( game_realtime_transport_lib::TransportErrorKind::Protocol, @@ -193,10 +196,7 @@ impl WebSocketListener { let listener = match tokio::net::TcpListener::bind(address).await { Ok(value) => value, Err(error) => { - let mapped = game_realtime_transport_lib::TransportError::new( - game_realtime_transport_lib::TransportErrorKind::Bind, - error.to_string(), - ); + let mapped = game_realtime_transport_lib::TransportError::new(game_realtime_transport_lib::TransportErrorKind::Bind, error.to_string()); tracing::warn!(target: TRACING_TARGET, address = %address, detail = mapped.detail(), "WebSocket listener bind failed"); return Err(mapped); }, @@ -204,10 +204,7 @@ impl WebSocketListener { let local_addr = match listener.local_addr() { Ok(value) => value, Err(error) => { - let mapped = game_realtime_transport_lib::TransportError::new( - game_realtime_transport_lib::TransportErrorKind::Bind, - error.to_string(), - ); + let mapped = game_realtime_transport_lib::TransportError::new(game_realtime_transport_lib::TransportErrorKind::Bind, error.to_string()); tracing::warn!(target: TRACING_TARGET, detail = mapped.detail(), "bound WebSocket listener address lookup failed"); return Err(mapped); }, @@ -227,10 +224,7 @@ impl WebSocketListener { let (stream, peer_addr) = match self.listener.accept().await { Ok(value) => value, Err(error) => { - let mapped = game_realtime_transport_lib::TransportError::new( - game_realtime_transport_lib::TransportErrorKind::Accept, - error.to_string(), - ); + let mapped = game_realtime_transport_lib::TransportError::new(game_realtime_transport_lib::TransportErrorKind::Accept, error.to_string()); tracing::warn!(target: TRACING_TARGET, detail = mapped.detail(), "WebSocket TCP accept failed"); return Err(mapped); }, @@ -238,10 +232,7 @@ impl WebSocketListener { let websocket = match tokio_tungstenite::accept_async(stream).await { Ok(value) => value, Err(error) => { - let mapped = game_realtime_transport_lib::TransportError::new( - game_realtime_transport_lib::TransportErrorKind::Accept, - error.to_string(), - ); + let mapped = game_realtime_transport_lib::TransportError::new(game_realtime_transport_lib::TransportErrorKind::Accept, error.to_string()); tracing::warn!(target: TRACING_TARGET, peer = %peer_addr, detail = mapped.detail(), "WebSocket server handshake failed"); return Err(mapped); }, diff --git a/crates/common/game-realtime-websocket-lib/tests/loopback.rs b/crates/common/game-realtime-websocket-lib/tests/loopback.rs index d60d445..9785779 100644 --- a/crates/common/game-realtime-websocket-lib/tests/loopback.rs +++ b/crates/common/game-realtime-websocket-lib/tests/loopback.rs @@ -45,10 +45,9 @@ async fn binary_round_trip_and_clean_close_work_on_loopback() { assert!(matches!(server_send, Ok(Ok(())))); let client_receive = tokio::time::timeout(TEST_TIMEOUT, client_receiver.receive()).await; match client_receive { - Ok(Ok(received)) => assert_eq!( - received, - game_realtime_transport_lib::TransportReceive::Message(game_realtime_transport_lib::TransportMessage::new(vec![9, 8, 7, 6])) - ), + Ok(Ok(received)) => { + assert_eq!(received, game_realtime_transport_lib::TransportReceive::Message(game_realtime_transport_lib::TransportMessage::new(vec![9, 8, 7, 6]))) + }, Ok(Err(error)) => panic!("client receive failed: {error}"), Err(_) => panic!("client receive timed out"), } diff --git a/deltas/0.3.4/alpha.3.fix.1.md b/deltas/0.3.4/alpha.3.fix.1.md new file mode 100644 index 0000000..a538e17 --- /dev/null +++ b/deltas/0.3.4/alpha.3.fix.1.md @@ -0,0 +1,87 @@ + + + +# Delta 0.3.4-alpha.3.fix.1 + +## Cause + +La gate utilisateur de `0.3.4-alpha.3` s'est arrêtée avant compilation lors du chargement des manifests Cargo : + +```text +error inheriting `futures-util` from workspace root manifest's `workspace.dependencies.futures-util` + +Caused by: + `default-features = false` cannot override workspace's `default-features` +``` + +Le même défaut était présent sur `tokio-tungstenite` mais n'était pas encore affiché parce que Cargo s'arrêtait sur la première dépendance invalide. + +`alpha.3` n'est donc pas historisée comme validée et aucune tranche `alpha.4` n'est ouverte avant fermeture de ce défaut. + +## Correction + +Le workspace passe à : + +```text +0.3.4-alpha.3.fix.1 +``` + +Les deux dépendances concernées portent désormais la désactivation de leurs features par défaut directement dans `[workspace.dependencies]` : + +```toml +futures-util = { version = "0.3.34", default-features = false } +tokio-tungstenite = { version = "0.30.0", default-features = false } +``` + +La crate `game-realtime-websocket-lib` hérite ensuite de ces réglages et ajoute uniquement les features dont elle a besoin : + +```toml +futures-util = { workspace = true, features = ["sink", "std"] } +tokio-tungstenite = { workspace = true, features = ["connect", "handshake"] } +``` + +Les features Tokio restent inchangées. Aucun TLS n'est ajouté et aucun code Rust/API/comportement transport n'est modifié. + +## Pourquoi ce placement + +Cargo autorise une crate membre à enrichir les `features` d'une dépendance héritée du workspace, mais pas à remplacer localement la valeur `default-features` définie par l'héritage. La politique `default-features = false` doit donc être possédée par la déclaration workspace lorsqu'elle est commune à l'utilisation héritée. + +Ce fix corrige les deux dépendances concernées immédiatement afin d'éviter qu'une seconde erreur identique apparaisse après correction de `futures-util` seulement. + +## Fichiers modifiés + +```text +Cargo.toml +README.md +crates/common/game-realtime-websocket-lib/Cargo.toml +docs/plans/004-V0_3_4_REALTIME_TRANSPORT_WEBSOCKET_PLAN.md +``` + +Nouveau fichier : + +```text +deltas/0.3.4/alpha.3.fix.1.md +``` + +Aucun fichier `history/0.3.4/alpha.3.md` n'est créé : une entrée `history/` n'existe qu'après validation réussie du jalon qu'elle décrit. + +## Validation attendue + +```bash +cargo fmt --all +cargo fmt --all -- --check + +python3 scripts/audit_rust_workspace_rules.py +python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates Android Web deltas history +python3 scripts/audit_distribution_layout.py + +cargo check --workspace +cargo clippy --workspace --all-targets --all-features -- -D warnings + +cargo test -p game-realtime-transport-lib --all-targets --all-features +cargo test -p game-realtime-websocket-lib --all-targets --all-features + +cargo tree -p game-realtime-websocket-lib --edges normal +``` + +La validation recommence depuis le début de la gate, car `alpha.3` n'a jamais atteint une compilation Cargo valide. diff --git a/docs/plans/004-V0_3_4_REALTIME_TRANSPORT_WEBSOCKET_PLAN.md b/docs/plans/004-V0_3_4_REALTIME_TRANSPORT_WEBSOCKET_PLAN.md index b7995a8..b1fe74e 100644 --- a/docs/plans/004-V0_3_4_REALTIME_TRANSPORT_WEBSOCKET_PLAN.md +++ b/docs/plans/004-V0_3_4_REALTIME_TRANSPORT_WEBSOCKET_PLAN.md @@ -1,5 +1,5 @@ - + # Plan 0.3.4 — transport realtime et baseline WebSocket @@ -7,7 +7,7 @@ Plan actif créé pendant `0.3.4-alpha.1` à partir de l'archive taggée `v0.3.3`. -Les tranches `alpha.1` et `alpha.2` ont été validées le 2026-09-21. La tranche active `alpha.3` ajoute maintenant le backend WebSocket Tokio/tokio-tungstenite derrière le contrat transport-neutral validé ; les limites, timeouts et cas négatifs complets restent réservés à `alpha.4`. +Les tranches `alpha.1` et `alpha.2` ont été validées le 2026-09-21. La validation de `alpha.3` a révélé un défaut de manifeste Cargo avant compilation : une crate membre ne peut pas remplacer `default-features` d'une dépendance héritée du workspace. La tranche active `alpha.3.fix.1` corrige uniquement cet héritage ; les limites, timeouts et cas négatifs complets restent réservés à `alpha.4`. ## Mission @@ -82,7 +82,7 @@ Contraintes utiles : - `native-tls` et les variantes `rustls-*` sont optionnelles ; - Tungstenite expose déjà des limites de message/frame et de write buffer configurables. -`alpha.3` centralise désormais ces versions sous `[workspace.dependencies]` et active les features uniquement dans `game-realtime-websocket-lib`. `futures-util` est consommé avec `default-features = false` et seulement `sink + std`; `tokio-tungstenite` est consommé avec `default-features = false` et seulement `connect + handshake`; aucune feature TLS n'est activée. La compatibilité effective de la toolchain utilisateur avec ces MSRV sera attestée par la gate Cargo utilisateur de cette tranche. +`alpha.3.fix.1` centralise désormais aussi `default-features = false` sous `[workspace.dependencies]`, car Cargo interdit à une dépendance membre héritée de remplacer cette option. `game-realtime-websocket-lib` ajoute seulement les features locales `sink + std` pour `futures-util` et `connect + handshake` pour `tokio-tungstenite`; aucune feature TLS n'est activée. La compatibilité effective de la toolchain utilisateur sera attestée par la gate Cargo utilisateur du fix. ## Ownership physique retenu