feat/fix/perf(engine,data,assetgen): ambient audio, sync bug fixes, hot-path cleanup
**ambient_loop.wav (task 5)** - solitaire_assetgen: add ambient_loop() synthesizer — 5 s seamless loop, 55 Hz drone with 2nd/3rd harmonics, 0.2 Hz LFO breath, 16-bit mono 44100 Hz - audio_plugin: load ambient_loop.wav via include_bytes!() replacing the card_flip.wav placeholder; decouple start_ambient_loop() from SoundLibrary **sync bug fixes (task 11)** - sync_plugin: LocalOnlyProvider returning UnsupportedPlatform now sets SyncStatus::Idle instead of displaying a misleading "Sync not configured" error - sync_client: extract_pull_body / extract_push_body now return SyncError::Auth only for HTTP 401/403; all other non-2xx statuses return SyncError::Network - sync_plugin: push_on_exit now logs a warn! on failure instead of silently discarding the result **hot-path performance (task 12)** - card_plugin: card_positions() now returns &Card references (lifetime-bound to GameState) instead of owned Card clones — eliminates 52 Card clones per sync_cards() call (runs every animation frame) - input_plugin: card_position() takes &PileType instead of PileType, eliminating PileType copies at every drag hit-test call site - animation_plugin: eliminate intermediate AnimSpeed clone in handle_win_cascade() **docs (tasks 11, 13)** - docs/sync_test_runbook.md: manual test runbook for cross-machine sync - docs/android_investigation.md: cargo-mobile2 port investigation and effort estimate Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -364,6 +364,10 @@ impl SyncProvider for SolitaireServerClient {
|
||||
|
||||
/// Deserialize a pull response body as [`SyncResponse`] and return its
|
||||
/// `merged` field, or map non-200 statuses to the appropriate [`SyncError`].
|
||||
///
|
||||
/// Only HTTP 401 (Unauthorized) and 403 (Forbidden) are treated as
|
||||
/// authentication errors. All other non-2xx statuses (5xx, 429, etc.) are
|
||||
/// classified as network/transport errors so the UI shows the right message.
|
||||
async fn extract_pull_body(resp: reqwest::Response) -> Result<SyncPayload, SyncError> {
|
||||
let status = resp.status();
|
||||
if status.is_success() {
|
||||
@@ -372,8 +376,12 @@ async fn extract_pull_body(resp: reqwest::Response) -> Result<SyncPayload, SyncE
|
||||
.await
|
||||
.map_err(|e| SyncError::Serialization(e.to_string()))?;
|
||||
Ok(sync_resp.merged)
|
||||
} else {
|
||||
} else if status == reqwest::StatusCode::UNAUTHORIZED
|
||||
|| status == reqwest::StatusCode::FORBIDDEN
|
||||
{
|
||||
Err(SyncError::Auth(format!("server returned {status}")))
|
||||
} else {
|
||||
Err(SyncError::Network(format!("server returned {status}")))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,14 +399,22 @@ async fn extract_leaderboard_body(resp: reqwest::Response) -> Result<Vec<Leaderb
|
||||
|
||||
/// Deserialize a push response body as [`SyncResponse`], or map non-200
|
||||
/// statuses to the appropriate [`SyncError`].
|
||||
///
|
||||
/// Only HTTP 401 (Unauthorized) and 403 (Forbidden) are treated as
|
||||
/// authentication errors. All other non-2xx statuses (5xx, 429, etc.) are
|
||||
/// classified as network/transport errors so the UI shows the right message.
|
||||
async fn extract_push_body(resp: reqwest::Response) -> Result<SyncResponse, SyncError> {
|
||||
let status = resp.status();
|
||||
if status.is_success() {
|
||||
resp.json()
|
||||
.await
|
||||
.map_err(|e| SyncError::Serialization(e.to_string()))
|
||||
} else {
|
||||
} else if status == reqwest::StatusCode::UNAUTHORIZED
|
||||
|| status == reqwest::StatusCode::FORBIDDEN
|
||||
{
|
||||
Err(SyncError::Auth(format!("server returned {status}")))
|
||||
} else {
|
||||
Err(SyncError::Network(format!("server returned {status}")))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user