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:
@@ -198,13 +198,17 @@ fn poll_pull_result(
|
||||
progress.0 = merged.progress;
|
||||
status.0 = SyncStatus::LastSynced(Utc::now());
|
||||
}
|
||||
Err(SyncError::UnsupportedPlatform) => {
|
||||
// No backend configured — not an error, just leave status as Idle.
|
||||
status.0 = SyncStatus::Idle;
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("sync pull failed: {e}");
|
||||
let msg = match &e {
|
||||
SyncError::Network(_) => "Can't reach server — check your connection".to_string(),
|
||||
SyncError::Auth(_) => "Login expired — tap Sync Now after re-logging in".to_string(),
|
||||
SyncError::Serialization(_) => format!("Unexpected server response: {e}"),
|
||||
SyncError::UnsupportedPlatform => "Sync not configured".to_string(),
|
||||
SyncError::UnsupportedPlatform => unreachable!("handled above"),
|
||||
};
|
||||
status.0 = SyncStatus::Error(msg);
|
||||
}
|
||||
@@ -233,13 +237,14 @@ fn push_on_exit(
|
||||
|
||||
// Prefer an existing tokio runtime; fall back to futures_lite block_on
|
||||
// for environments (e.g. tests) that don't have one.
|
||||
match tokio::runtime::Handle::try_current() {
|
||||
Ok(handle) => {
|
||||
let _ = handle.block_on(provider.push(&payload));
|
||||
}
|
||||
Err(_) => {
|
||||
let _ = future::block_on(provider.push(&payload));
|
||||
}
|
||||
let result = match tokio::runtime::Handle::try_current() {
|
||||
Ok(handle) => handle.block_on(provider.push(&payload)),
|
||||
Err(_) => future::block_on(provider.push(&payload)),
|
||||
};
|
||||
if let Err(e) = result {
|
||||
// Log 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}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user