fix(web): resolve wasm32 runtime panics; game boots and renders in Firefox
Build and Deploy / build-and-push (push) Failing after 1m6s
Build and Deploy / build-and-push (push) Failing after 1m6s
Fixes found while testing the Bevy WASM build in a real browser: 1. chrono wasmbind: add `wasmbind` feature to workspace chrono dep so Local::now()/Utc::now() use js-sys::Date on wasm32 (previously fell through to std::time::SystemTime which panics on wasm32). 2. std::time::SystemTime: replace all remaining direct SystemTime::now() calls (4 sites across game_plugin, difficulty_plugin, time_attack_plugin, solitaire_data/storage) with chrono::Utc::now() which is wasm32-safe. 3. user_dir: return empty PathBuf (instead of panicking) when data_dir() is None on wasm32; there is no filesystem in the browser so user themes are unsupported and a benign empty path is correct. 4. ThemeRegistryPlugin: gate build_registry_on_startup to non-wasm32 (the filesystem scan for user themes has nothing to scan in the browser; only the bundled embedded themes are available). 5. AssetMetaCheck::Never: configure AssetPlugin in solitaire_web to skip `.meta` sidecar fetches — we don't ship .meta files, so the default AssetMetaCheck::Always produced a 404 flood on every card/background asset. Result: `http://localhost:<port>/play` boots in Firefox with zero errors and renders the full Bevy game — home screen, onboarding modal, HUD all visible. Assets load correctly from /assets/. Chromium has a separate wgpu-27/ANGLE/GLES shader translation bug (not in our code); Firefox works. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -82,13 +82,23 @@ fn user_theme_dir_for(data_dir: PathBuf) -> PathBuf {
|
||||
/// the panic message names the supported workaround.
|
||||
fn detected_platform_data_dir() -> PathBuf {
|
||||
solitaire_data::data_dir().unwrap_or_else(|| {
|
||||
panic!(
|
||||
"user_theme_dir(): platform data directory is unavailable. \
|
||||
On Linux check $XDG_DATA_HOME or $HOME; on macOS / Windows \
|
||||
the OS reported no Application Support / AppData path. \
|
||||
As a workaround call solitaire_engine::assets::user_dir::\
|
||||
set_user_theme_dir() before App::run()."
|
||||
)
|
||||
// On wasm32, data_dir() always returns None — there is no filesystem.
|
||||
// User themes are not supported in the browser build; return an empty
|
||||
// path so callers produce a benign empty dir rather than panicking.
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
PathBuf::new()
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
panic!(
|
||||
"user_theme_dir(): platform data directory is unavailable. \
|
||||
On Linux check $XDG_DATA_HOME or $HOME; on macOS / Windows \
|
||||
the OS reported no Application Support / AppData path. \
|
||||
As a workaround call solitaire_engine::assets::user_dir::\
|
||||
set_user_theme_dir() before App::run()."
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
//! because the starting position is effectively random (player-chosen timing
|
||||
//! determines which seed in the 40-entry catalog they start at).
|
||||
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use chrono::Utc;
|
||||
|
||||
use bevy::prelude::*;
|
||||
use solitaire_core::game_state::{DifficultyLevel, GameMode};
|
||||
@@ -104,10 +104,9 @@ fn handle_difficulty_request(
|
||||
}
|
||||
|
||||
fn seed_from_system_time() -> u64 {
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map(|d| d.as_nanos() as u64)
|
||||
.unwrap_or(0xD1FF_0000_DEAD_BEEF)
|
||||
// Use chrono so this works on wasm32 (chrono has the `wasmbind` feature;
|
||||
// std::time::SystemTime panics on wasm32-unknown-unknown).
|
||||
Utc::now().timestamp_nanos_opt().unwrap_or(0) as u64
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
//! file is deleted so the next launch starts fresh.
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use chrono::Utc;
|
||||
|
||||
use bevy::prelude::*;
|
||||
use bevy::tasks::{AsyncComputeTaskPool, Task, futures_lite::future};
|
||||
use bevy::window::AppLifecycle;
|
||||
use chrono::Utc;
|
||||
use solitaire_core::game_state::{DrawMode, GameMode, GameState};
|
||||
use klondike::KlondikePile;
|
||||
use solitaire_core::solver::{SolverConfig, SolverResult, try_solve};
|
||||
@@ -312,9 +312,7 @@ fn tick_elapsed_time(
|
||||
}
|
||||
|
||||
fn seed_from_system_time() -> u64 {
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map_or(0, |d| d.as_nanos() as u64)
|
||||
Utc::now().timestamp_nanos_opt().unwrap_or(0) as u64
|
||||
}
|
||||
|
||||
/// Walks forward from `initial_seed` (incrementing by 1 with wrapping
|
||||
|
||||
@@ -85,13 +85,18 @@ pub struct ThemeRegistryPlugin;
|
||||
|
||||
impl Plugin for ThemeRegistryPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.init_resource::<ThemeRegistry>()
|
||||
.add_systems(Startup, build_registry_on_startup);
|
||||
app.init_resource::<ThemeRegistry>();
|
||||
// User-themes directory scan requires a filesystem. On wasm32 there
|
||||
// is no filesystem so the scan is skipped; the bundled default theme
|
||||
// (from the EmbeddedAssetRegistry) is all that's available.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
app.add_systems(Startup, build_registry_on_startup);
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads `user_theme_dir()` and replaces the registry's contents with
|
||||
/// the bundled default plus every valid user theme.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn build_registry_on_startup(mut registry: bevy::ecs::system::ResMut<ThemeRegistry>) {
|
||||
*registry = build_registry(&user_theme_dir());
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
//! was closed, the file is treated as missing.
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use chrono::Utc;
|
||||
|
||||
use bevy::prelude::*;
|
||||
use solitaire_core::game_state::GameMode;
|
||||
@@ -222,9 +223,9 @@ fn auto_deal_on_time_attack_win(
|
||||
/// the system time predates the epoch (impossible under any sane clock,
|
||||
/// but the fallback keeps the function infallible).
|
||||
fn current_unix_secs() -> u64 {
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map_or(0, |d| d.as_secs())
|
||||
// Use chrono so this works on wasm32 (chrono has the `wasmbind` feature;
|
||||
// std::time::SystemTime panics on wasm32-unknown-unknown).
|
||||
Utc::now().timestamp().max(0) as u64
|
||||
}
|
||||
|
||||
/// Periodically persists the live `TimeAttackResource` to
|
||||
|
||||
Reference in New Issue
Block a user