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:
+1
-1
@@ -22,7 +22,7 @@ rust-version = "1.95"
|
|||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
uuid = { version = "1", features = ["v4", "serde"] }
|
uuid = { version = "1", features = ["v4", "serde"] }
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde", "wasmbind"] }
|
||||||
thiserror = "2"
|
thiserror = "2"
|
||||||
rand = "0.9"
|
rand = "0.9"
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use chrono::Utc;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use solitaire_core::game_state::{GAME_STATE_SCHEMA_VERSION, GameState};
|
use solitaire_core::game_state::{GAME_STATE_SCHEMA_VERSION, GameState};
|
||||||
@@ -234,9 +234,7 @@ pub fn load_time_attack_session_from_at(
|
|||||||
/// See [`load_time_attack_session_from_at`] for the rules under which
|
/// See [`load_time_attack_session_from_at`] for the rules under which
|
||||||
/// the call returns `None` (missing file, corrupt JSON, expired window).
|
/// the call returns `None` (missing file, corrupt JSON, expired window).
|
||||||
pub fn load_time_attack_session_from(path: &Path) -> Option<TimeAttackSession> {
|
pub fn load_time_attack_session_from(path: &Path) -> Option<TimeAttackSession> {
|
||||||
let now = SystemTime::now()
|
let now = Utc::now().timestamp().max(0) as u64;
|
||||||
.duration_since(UNIX_EPOCH)
|
|
||||||
.map_or(0, |d| d.as_secs());
|
|
||||||
load_time_attack_session_from_at(path, now)
|
load_time_attack_session_from_at(path, now)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,9 +252,7 @@ pub fn delete_time_attack_session_at(path: &Path) -> io::Result<()> {
|
|||||||
/// current wall-clock time. Equivalent to constructing the struct
|
/// current wall-clock time. Equivalent to constructing the struct
|
||||||
/// manually and setting `saved_at_unix_secs` to `SystemTime::now()`.
|
/// manually and setting `saved_at_unix_secs` to `SystemTime::now()`.
|
||||||
pub fn time_attack_session_with_now(remaining_secs: f32, wins: u32) -> TimeAttackSession {
|
pub fn time_attack_session_with_now(remaining_secs: f32, wins: u32) -> TimeAttackSession {
|
||||||
let now = SystemTime::now()
|
let now = Utc::now().timestamp().max(0) as u64;
|
||||||
.duration_since(UNIX_EPOCH)
|
|
||||||
.map_or(0, |d| d.as_secs());
|
|
||||||
TimeAttackSession {
|
TimeAttackSession {
|
||||||
remaining_secs,
|
remaining_secs,
|
||||||
wins,
|
wins,
|
||||||
|
|||||||
@@ -82,13 +82,23 @@ fn user_theme_dir_for(data_dir: PathBuf) -> PathBuf {
|
|||||||
/// the panic message names the supported workaround.
|
/// the panic message names the supported workaround.
|
||||||
fn detected_platform_data_dir() -> PathBuf {
|
fn detected_platform_data_dir() -> PathBuf {
|
||||||
solitaire_data::data_dir().unwrap_or_else(|| {
|
solitaire_data::data_dir().unwrap_or_else(|| {
|
||||||
panic!(
|
// On wasm32, data_dir() always returns None — there is no filesystem.
|
||||||
"user_theme_dir(): platform data directory is unavailable. \
|
// User themes are not supported in the browser build; return an empty
|
||||||
On Linux check $XDG_DATA_HOME or $HOME; on macOS / Windows \
|
// path so callers produce a benign empty dir rather than panicking.
|
||||||
the OS reported no Application Support / AppData path. \
|
#[cfg(target_arch = "wasm32")]
|
||||||
As a workaround call solitaire_engine::assets::user_dir::\
|
{
|
||||||
set_user_theme_dir() before App::run()."
|
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
|
//! because the starting position is effectively random (player-chosen timing
|
||||||
//! determines which seed in the 40-entry catalog they start at).
|
//! determines which seed in the 40-entry catalog they start at).
|
||||||
|
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use chrono::Utc;
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use solitaire_core::game_state::{DifficultyLevel, GameMode};
|
use solitaire_core::game_state::{DifficultyLevel, GameMode};
|
||||||
@@ -104,10 +104,9 @@ fn handle_difficulty_request(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn seed_from_system_time() -> u64 {
|
fn seed_from_system_time() -> u64 {
|
||||||
SystemTime::now()
|
// Use chrono so this works on wasm32 (chrono has the `wasmbind` feature;
|
||||||
.duration_since(UNIX_EPOCH)
|
// std::time::SystemTime panics on wasm32-unknown-unknown).
|
||||||
.map(|d| d.as_nanos() as u64)
|
Utc::now().timestamp_nanos_opt().unwrap_or(0) as u64
|
||||||
.unwrap_or(0xD1FF_0000_DEAD_BEEF)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -7,12 +7,12 @@
|
|||||||
//! file is deleted so the next launch starts fresh.
|
//! file is deleted so the next launch starts fresh.
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
|
||||||
|
use chrono::Utc;
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy::tasks::{AsyncComputeTaskPool, Task, futures_lite::future};
|
use bevy::tasks::{AsyncComputeTaskPool, Task, futures_lite::future};
|
||||||
use bevy::window::AppLifecycle;
|
use bevy::window::AppLifecycle;
|
||||||
use chrono::Utc;
|
|
||||||
use solitaire_core::game_state::{DrawMode, GameMode, GameState};
|
use solitaire_core::game_state::{DrawMode, GameMode, GameState};
|
||||||
use klondike::KlondikePile;
|
use klondike::KlondikePile;
|
||||||
use solitaire_core::solver::{SolverConfig, SolverResult, try_solve};
|
use solitaire_core::solver::{SolverConfig, SolverResult, try_solve};
|
||||||
@@ -312,9 +312,7 @@ fn tick_elapsed_time(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn seed_from_system_time() -> u64 {
|
fn seed_from_system_time() -> u64 {
|
||||||
SystemTime::now()
|
Utc::now().timestamp_nanos_opt().unwrap_or(0) as u64
|
||||||
.duration_since(UNIX_EPOCH)
|
|
||||||
.map_or(0, |d| d.as_nanos() as u64)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Walks forward from `initial_seed` (incrementing by 1 with wrapping
|
/// Walks forward from `initial_seed` (incrementing by 1 with wrapping
|
||||||
|
|||||||
@@ -85,13 +85,18 @@ pub struct ThemeRegistryPlugin;
|
|||||||
|
|
||||||
impl Plugin for ThemeRegistryPlugin {
|
impl Plugin for ThemeRegistryPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.init_resource::<ThemeRegistry>()
|
app.init_resource::<ThemeRegistry>();
|
||||||
.add_systems(Startup, build_registry_on_startup);
|
// 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
|
/// Reads `user_theme_dir()` and replaces the registry's contents with
|
||||||
/// the bundled default plus every valid user theme.
|
/// 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>) {
|
fn build_registry_on_startup(mut registry: bevy::ecs::system::ResMut<ThemeRegistry>) {
|
||||||
*registry = build_registry(&user_theme_dir());
|
*registry = build_registry(&user_theme_dir());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,8 @@
|
|||||||
//! was closed, the file is treated as missing.
|
//! was closed, the file is treated as missing.
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
|
||||||
|
use chrono::Utc;
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use solitaire_core::game_state::GameMode;
|
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,
|
/// the system time predates the epoch (impossible under any sane clock,
|
||||||
/// but the fallback keeps the function infallible).
|
/// but the fallback keeps the function infallible).
|
||||||
fn current_unix_secs() -> u64 {
|
fn current_unix_secs() -> u64 {
|
||||||
SystemTime::now()
|
// Use chrono so this works on wasm32 (chrono has the `wasmbind` feature;
|
||||||
.duration_since(UNIX_EPOCH)
|
// std::time::SystemTime panics on wasm32-unknown-unknown).
|
||||||
.map_or(0, |d| d.as_secs())
|
Utc::now().timestamp().max(0) as u64
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Periodically persists the live `TimeAttackResource` to
|
/// Periodically persists the live `TimeAttackResource` to
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
/* @ts-self-types="./canvas.d.ts" */
|
|
||||||
|
|
||||||
export function start() {
|
export function start() {
|
||||||
wasm.start();
|
wasm.start();
|
||||||
}
|
}
|
||||||
@@ -1651,62 +1649,62 @@ function __wbg_get_imports() {
|
|||||||
return ret;
|
return ret;
|
||||||
},
|
},
|
||||||
__wbindgen_cast_0000000000000001: function(arg0, arg1) {
|
__wbindgen_cast_0000000000000001: function(arg0, arg1) {
|
||||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 114649, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
|
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 114646, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
|
||||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__hf0188236128725a8);
|
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__hf0188236128725a8);
|
||||||
return ret;
|
return ret;
|
||||||
},
|
},
|
||||||
__wbindgen_cast_0000000000000002: function(arg0, arg1) {
|
__wbindgen_cast_0000000000000002: function(arg0, arg1) {
|
||||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 9793, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 9790, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h038e9392efba509b);
|
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h038e9392efba509b);
|
||||||
return ret;
|
return ret;
|
||||||
},
|
},
|
||||||
__wbindgen_cast_0000000000000003: function(arg0, arg1) {
|
__wbindgen_cast_0000000000000003: function(arg0, arg1) {
|
||||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("Array<any>"), NamedExternref("ResizeObserver")], shim_idx: 9795, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("Array<any>"), NamedExternref("ResizeObserver")], shim_idx: 9792, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__hb8334c8e03ee5ee1);
|
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__hb8334c8e03ee5ee1);
|
||||||
return ret;
|
return ret;
|
||||||
},
|
},
|
||||||
__wbindgen_cast_0000000000000004: function(arg0, arg1) {
|
__wbindgen_cast_0000000000000004: function(arg0, arg1) {
|
||||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("Array<any>")], shim_idx: 9793, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("Array<any>")], shim_idx: 9790, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h038e9392efba509b_3);
|
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h038e9392efba509b_3);
|
||||||
return ret;
|
return ret;
|
||||||
},
|
},
|
||||||
__wbindgen_cast_0000000000000005: function(arg0, arg1) {
|
__wbindgen_cast_0000000000000005: function(arg0, arg1) {
|
||||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("Event")], shim_idx: 9793, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("Event")], shim_idx: 9790, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h038e9392efba509b_4);
|
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h038e9392efba509b_4);
|
||||||
return ret;
|
return ret;
|
||||||
},
|
},
|
||||||
__wbindgen_cast_0000000000000006: function(arg0, arg1) {
|
__wbindgen_cast_0000000000000006: function(arg0, arg1) {
|
||||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("FocusEvent")], shim_idx: 9793, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("FocusEvent")], shim_idx: 9790, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h038e9392efba509b_5);
|
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h038e9392efba509b_5);
|
||||||
return ret;
|
return ret;
|
||||||
},
|
},
|
||||||
__wbindgen_cast_0000000000000007: function(arg0, arg1) {
|
__wbindgen_cast_0000000000000007: function(arg0, arg1) {
|
||||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("KeyboardEvent")], shim_idx: 9793, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("KeyboardEvent")], shim_idx: 9790, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h038e9392efba509b_6);
|
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h038e9392efba509b_6);
|
||||||
return ret;
|
return ret;
|
||||||
},
|
},
|
||||||
__wbindgen_cast_0000000000000008: function(arg0, arg1) {
|
__wbindgen_cast_0000000000000008: function(arg0, arg1) {
|
||||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("PageTransitionEvent")], shim_idx: 9793, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("PageTransitionEvent")], shim_idx: 9790, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h038e9392efba509b_7);
|
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h038e9392efba509b_7);
|
||||||
return ret;
|
return ret;
|
||||||
},
|
},
|
||||||
__wbindgen_cast_0000000000000009: function(arg0, arg1) {
|
__wbindgen_cast_0000000000000009: function(arg0, arg1) {
|
||||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("PointerEvent")], shim_idx: 9793, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("PointerEvent")], shim_idx: 9790, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h038e9392efba509b_8);
|
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h038e9392efba509b_8);
|
||||||
return ret;
|
return ret;
|
||||||
},
|
},
|
||||||
__wbindgen_cast_000000000000000a: function(arg0, arg1) {
|
__wbindgen_cast_000000000000000a: function(arg0, arg1) {
|
||||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("WheelEvent")], shim_idx: 9793, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("WheelEvent")], shim_idx: 9790, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h038e9392efba509b_9);
|
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h038e9392efba509b_9);
|
||||||
return ret;
|
return ret;
|
||||||
},
|
},
|
||||||
__wbindgen_cast_000000000000000b: function(arg0, arg1) {
|
__wbindgen_cast_000000000000000b: function(arg0, arg1) {
|
||||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Option(NamedExternref("Blob"))], shim_idx: 9803, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Option(NamedExternref("Blob"))], shim_idx: 9800, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h618c0cad9a289a93);
|
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h618c0cad9a289a93);
|
||||||
return ret;
|
return ret;
|
||||||
},
|
},
|
||||||
__wbindgen_cast_000000000000000c: function(arg0, arg1) {
|
__wbindgen_cast_000000000000000c: function(arg0, arg1) {
|
||||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 9797, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 9794, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h277d9d6b389a2871);
|
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h277d9d6b389a2871);
|
||||||
return ret;
|
return ret;
|
||||||
},
|
},
|
||||||
|
|||||||
Binary file not shown.
+20
-11
@@ -9,6 +9,7 @@
|
|||||||
//! - Storage is handled automatically by `WasmStorage` (localStorage-backed),
|
//! - Storage is handled automatically by `WasmStorage` (localStorage-backed),
|
||||||
//! wired by `CoreGamePlugin` via `default_storage_backend()`.
|
//! wired by `CoreGamePlugin` via `default_storage_backend()`.
|
||||||
|
|
||||||
|
use bevy::asset::AssetMetaCheck;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy::window::{Window, WindowPlugin};
|
use bevy::window::{Window, WindowPlugin};
|
||||||
use solitaire_data::LocalOnlyProvider;
|
use solitaire_data::LocalOnlyProvider;
|
||||||
@@ -21,19 +22,27 @@ pub fn start() {
|
|||||||
|
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins(
|
.add_plugins(
|
||||||
DefaultPlugins.set(WindowPlugin {
|
DefaultPlugins
|
||||||
primary_window: Some(Window {
|
.set(WindowPlugin {
|
||||||
// Bind to the existing <canvas id="bevy-canvas"> in play.html.
|
primary_window: Some(Window {
|
||||||
// Without this, Bevy appends its own canvas to <body>.
|
// Bind to the existing <canvas id="bevy-canvas"> in play.html.
|
||||||
canvas: Some("#bevy-canvas".into()),
|
// Without this, Bevy appends its own canvas to <body>.
|
||||||
// Let CSS size the canvas; Bevy follows the element's size.
|
canvas: Some("#bevy-canvas".into()),
|
||||||
fit_canvas_to_parent: true,
|
// Let CSS size the canvas; Bevy follows the element's size.
|
||||||
// Prevent the browser stealing keyboard events and scroll.
|
fit_canvas_to_parent: true,
|
||||||
prevent_default_event_handling: true,
|
// Prevent the browser stealing keyboard events and scroll.
|
||||||
|
prevent_default_event_handling: true,
|
||||||
|
..default()
|
||||||
|
}),
|
||||||
|
..default()
|
||||||
|
})
|
||||||
|
// Bevy's default AssetPlugin fetches a `.meta` sidecar file for
|
||||||
|
// every asset before loading the asset itself. We don't ship
|
||||||
|
// `.meta` files, so skip the check to avoid a flood of 404s.
|
||||||
|
.set(AssetPlugin {
|
||||||
|
meta_check: AssetMetaCheck::Never,
|
||||||
..default()
|
..default()
|
||||||
}),
|
}),
|
||||||
..default()
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
// LocalOnlyProvider disables cloud sync — correct for the web build
|
// LocalOnlyProvider disables cloud sync — correct for the web build
|
||||||
// since SyncPlugin is cfg-gated out on wasm32 anyway.
|
// since SyncPlugin is cfg-gated out on wasm32 anyway.
|
||||||
|
|||||||
Reference in New Issue
Block a user