conditional compilation of async compat

Problems with the previous code:

- `async-compat` is necessary for headless mode (cli server rendering)
  to load remote scenes from the API without blowing up. Without it, the
  code complains about the lack of a Tokio runtime.

- Unfortunately, `async-compat` doesn't work with WASM and the entire
  web build is broken by its presence.

This change introduces conditional compilation so `async-compat` is only
leveraged in headless mode.
This commit is contained in:
Brandon Thomas
2024-03-20 22:15:45 -04:00
parent 89487e5889
commit 247bfcbf47
2 changed files with 77 additions and 61 deletions
+2
View File
@@ -15,6 +15,7 @@ path = "src/main.rs"
[[bin]]
name = "studio-headless"
path = "src/main-headless.rs"
required-features = ["headless_cli"]
[dependencies]
bevy-inspector-egui = "0.22.0"
@@ -69,6 +70,7 @@ wasm = [
"bevy_rapier3d/wasm-bindgen",
]
desktop = []
headless_cli = []
[dependencies.bevy]
workspace = true
+75 -61
View File
@@ -16,6 +16,71 @@ pub struct RemoteAssetReader;
impl RemoteAssetReader {
const API_BASE_URL: &'static str = "https://api.fakeyou.com/v1/media_files/file";
const BUCKET_BASE_URL: &'static str = "https://storage.googleapis.com/vocodes-public";
async fn load<'a>(
&'a self,
path: &'a Path,
) -> Result<Box<Reader<'a>>, AssetReaderError> {
let token = path
.ext_file_prefix()
.ok_or_else(|| {
AssetReaderError::Io(std::io::Error::new(
ErrorKind::Other,
format!("Failed to parse token from '{path:?}'"),
))
})?
.to_str()
.unwrap();
info!("Loading asset from media token: '{token}'");
let response = reqwest::get(format!("{}/{token}", &Self::API_BASE_URL))
.await
.map_err(IntoError::<AssetReaderError>::into)?;
let response = match response.status() {
StatusCode::OK => response
.json::<GetMediaFileSuccessResponse>()
.await
.map_err(IntoError::<AssetReaderError>::into),
other => Err(AssetReaderError::Io(std::io::Error::new(
ErrorKind::Other,
format!("Status Code from endpoint: {other}"),
))),
}?;
let url = format!(
"{}{}",
&Self::BUCKET_BASE_URL,
&response.media_file.public_bucket_path
);
let response = reqwest::get(url)
.await
.map_err(IntoError::<AssetReaderError>::into)?;
let bytes = match response.status() {
// TODO: This is not thread-safe, but some variation of this
// would be better than the current solution once Bevy
// WASM becomes multi-threaded:
// StatusCode::OK => Ok(response.bytes_stream().map(|result| {
// result.map_err(|err| std::io::Error::new(ErrorKind::Other, format!("{err}")))
// })),
StatusCode::OK => Ok(response
.bytes()
.await
.map_err(IntoError::<AssetReaderError>::into)?
.into_iter()
.collect::<Vec<_>>()),
other => Err(AssetReaderError::Io(std::io::Error::new(
ErrorKind::Other,
format!("Status Code from bucket URL: {other}"),
))),
}?;
let reader: Box<Reader> = Box::new(VecReader::new(bytes));
Ok(reader)
}
}
impl AssetReader for RemoteAssetReader {
@@ -23,67 +88,16 @@ impl AssetReader for RemoteAssetReader {
&'a self,
path: &'a Path,
) -> BoxedFuture<'a, Result<Box<Reader<'a>>, AssetReaderError>> {
Box::pin(Compat::new(async move {
let token = path
.ext_file_prefix()
.ok_or_else(|| {
AssetReaderError::Io(std::io::Error::new(
ErrorKind::Other,
format!("Failed to parse token from '{path:?}'"),
))
})?
.to_str()
.unwrap();
info!("Loading asset from media token: '{token}'");
let response = reqwest::get(format!("{}/{token}", &Self::API_BASE_URL))
.await
.map_err(IntoError::<AssetReaderError>::into)?;
let response = match response.status() {
StatusCode::OK => response
.json::<GetMediaFileSuccessResponse>()
.await
.map_err(IntoError::<AssetReaderError>::into),
other => Err(AssetReaderError::Io(std::io::Error::new(
ErrorKind::Other,
format!("Status Code from endpoint: {other}"),
))),
}?;
let url = format!(
"{}{}",
&Self::BUCKET_BASE_URL,
&response.media_file.public_bucket_path
);
let response = reqwest::get(url)
.await
.map_err(IntoError::<AssetReaderError>::into)?;
let bytes = match response.status() {
// TODO: This is not thread-safe, but some variation of this
// would be better than the current solution once Bevy
// WASM becomes multi-threaded:
// StatusCode::OK => Ok(response.bytes_stream().map(|result| {
// result.map_err(|err| std::io::Error::new(ErrorKind::Other, format!("{err}")))
// })),
StatusCode::OK => Ok(response
.bytes()
.await
.map_err(IntoError::<AssetReaderError>::into)?
.into_iter()
.collect::<Vec<_>>()),
other => Err(AssetReaderError::Io(std::io::Error::new(
ErrorKind::Other,
format!("Status Code from bucket URL: {other}"),
))),
}?;
let reader: Box<Reader> = Box::new(VecReader::new(bytes));
Ok(reader)
}))
let unboxed = async move {
self.load(path).await
};
if cfg!(headless_cli) {
// NB: Headless mode (server rendering) is running without a Tokio runtime.
// The reqwest HTTP client requires async-compat to function at runtime.
Box::pin(Compat::new(unboxed))
} else {
Box::pin(unboxed)
}
}
fn read_meta<'a>(