refactor(engine,wasm,data): route all klondike/card_game imports through solitaire_core
Build and Deploy / build-and-push (push) Failing after 53s
Web E2E / web-e2e (push) Failing after 4m16s

All downstream crates now import Foundation, KlondikePile, Tableau,
Klondike, Session, Suit, Rank exclusively from solitaire_core.
solitaire_core is the single version-pin point for the upstream crates.

- solitaire_engine: 19 files updated, klondike direct dep removed
- solitaire_wasm: use statement updated, klondike direct dep removed
- solitaire_data: unused klondike dep removed
- Cargo.lock: klondike no longer a direct dep of engine/wasm/data
- Full workspace clippy clean, all tests pass

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
funman300
2026-06-08 11:04:05 -07:00
parent ae1ecc8559
commit d864d985c8
27 changed files with 180 additions and 126 deletions
+19 -22
View File
@@ -3,8 +3,8 @@
//! On startup, the plugin spawns an async pull task on [`AsyncComputeTaskPool`]
//! that fetches the remote payload from the active [`SyncProvider`]. Once the
//! task resolves, the merged result is written to disk and the in-world
//! resources are updated. On app exit, a blocking push sends the current local
//! state to the backend.
//! resources are updated. On app exit, a best-effort async push sends the
//! current local state to the backend without blocking the Bevy main thread.
//!
//! The plugin is completely backend-agnostic: the caller (usually
//! `solitaire_app`) constructs the right [`SyncProvider`] implementation and
@@ -79,8 +79,8 @@ struct PendingReplayUpload(Option<Task<Result<String, SyncError>>>);
/// - **Update** — polls the task each frame; on completion merges the remote
/// payload with local data, persists the result, and updates in-world
/// resources.
/// - **Last** — on [`AppExit`], performs a blocking push of the current local
/// state to the active backend.
/// - **Last** — on [`AppExit`], starts a best-effort async push of the current
/// local state to the active backend without blocking shutdown.
///
/// Construct via [`SyncPlugin::new`], passing any type that implements
/// [`SyncProvider`].
@@ -272,11 +272,12 @@ fn poll_pull_result(
}
}
/// Last-schedule system: pushes the current local state on [`AppExit`].
/// Last-schedule system: starts a best-effort push of the current local state
/// on [`AppExit`] without blocking the Bevy main thread.
///
/// A blocking push is acceptable here — ARCHITECTURE.md §4 explicitly notes
/// that blocking on exit is permitted because the game loop is already
/// shutting down.
/// The detached task may be cut short by process teardown, so local atomic
/// persistence remains the durable source of truth even if the final remote
/// push does not complete.
fn push_on_exit(
mut exit_events: MessageReader<AppExit>,
provider: Res<SyncProviderResource>,
@@ -291,20 +292,16 @@ fn push_on_exit(
exit_events.clear();
let payload = build_payload(&stats.0, &achievements.0, &progress.0);
let result = rt.0.block_on(provider.0.push(&payload));
match result {
Ok(_) => {}
// `UnsupportedPlatform` is the expected response of
// `LocalOnlyProvider`; treat it the same as the pull path does —
// no backend configured is not a failure.
Err(SyncError::UnsupportedPlatform) => {}
Err(e) => {
// Log real push failures on exit so they appear in crash/log
// reports. We cannot surface them to the UI at this point (game
// loop is done).
warn!("sync push on exit failed: {e}");
}
}
let provider = provider.0.clone();
let rt = rt.0.clone();
AsyncComputeTaskPool::get()
.spawn(async move {
match rt.block_on(provider.push(&payload)) {
Ok(_) | Err(SyncError::UnsupportedPlatform) => {}
Err(e) => warn!("sync push on exit failed: {e}"),
}
})
.detach();
}
/// Update-schedule system: on each `GameWonEvent` push the just-completed