diff --git a/crates/bevy_asset/src/io/wasm.rs b/crates/bevy_asset/src/io/wasm.rs index cd8f66bdf415e..f44445fa0cf32 100644 --- a/crates/bevy_asset/src/io/wasm.rs +++ b/crates/bevy_asset/src/io/wasm.rs @@ -3,7 +3,10 @@ use crate::io::{ }; use alloc::{borrow::ToOwned, boxed::Box, format}; use js_sys::{Uint8Array, JSON}; -use std::path::{Path, PathBuf}; +use std::{ + borrow::Cow, + path::{Path, PathBuf}, +}; use tracing::error; use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue}; use wasm_bindgen_futures::JsFuture; @@ -27,6 +30,7 @@ extern "C" { /// Reader implementation for loading assets via HTTP in Wasm. pub struct HttpWasmAssetReader { root_path: PathBuf, + request_mapper: Option Cow + Send + Sync + 'static>>, } impl HttpWasmAssetReader { @@ -34,8 +38,19 @@ impl HttpWasmAssetReader { pub fn new>(path: P) -> Self { Self { root_path: path.as_ref().to_owned(), + request_mapper: None, } } + + /// Sets a mapper function to modify the request URL for each asset fetch. This can be used to + /// add query parameters or modify the path in any way. + pub fn with_request_mapper(mut self, mapper: F) -> Self + where + F: Fn(&str) -> Cow + Send + Sync + 'static, + { + self.request_mapper = Some(Box::new(mapper)); + self + } } fn js_value_to_err(context: &str) -> impl FnOnce(JsValue) -> std::io::Error + '_ { @@ -57,14 +72,20 @@ impl HttpWasmAssetReader { &self, path: PathBuf, ) -> Result, AssetReaderError> { + let path = path.to_str().unwrap(); + let fetch_path = self + .request_mapper + .as_ref() + .map_or_else(|| Cow::Borrowed(path), |mapper| mapper(path)); + // The JS global scope includes a self-reference via a specializing name, which can be used to determine the type of global context available. let global: Global = js_sys::global().unchecked_into(); let promise = if !global.window().is_undefined() { let window: web_sys::Window = global.unchecked_into(); - window.fetch_with_str(path.to_str().unwrap()) + window.fetch_with_str(&fetch_path) } else if !global.worker().is_undefined() { let worker: web_sys::WorkerGlobalScope = global.unchecked_into(); - worker.fetch_with_str(path.to_str().unwrap()) + worker.fetch_with_str(&fetch_path) } else { let error = std::io::Error::other("Unsupported JavaScript global context"); return Err(AssetReaderError::Io(error.into())); @@ -85,7 +106,7 @@ impl HttpWasmAssetReader { // Some web servers, including itch.io's CDN, return 403 when a requested file isn't present. // TODO: remove handling of 403 as not found when it's easier to configure // see https://github.com/bevyengine/bevy/pull/19268#pullrequestreview-2882410105 - 403 | 404 => Err(AssetReaderError::NotFound(path)), + 403 | 404 => Err(AssetReaderError::NotFound((*fetch_path).into())), status => Err(AssetReaderError::HttpError(status)), } }