Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions crates/bevy_asset/src/assets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -733,11 +733,16 @@ impl<'a, A: Asset> Iterator for AssetsMutIterator<'a, A> {
/// An error returned when an [`AssetIndex`] has an invalid generation.
#[derive(Error, Debug, PartialEq, Eq)]
pub enum InvalidGenerationError {
/// The generation of this [`AssetIndex`] does not match the current generation.
/// That means its index entry has been replaced.
#[error("AssetIndex {index:?} has an invalid generation. The current generation is: '{current_generation}'.")]
Occupied {
/// The index being looked up.
index: AssetIndex,
/// The generation currently at that index.
current_generation: u32,
},
/// This index entry has been removed.
#[error("AssetIndex {index:?} has been removed")]
Removed { index: AssetIndex },
}
Expand Down
3 changes: 3 additions & 0 deletions crates/bevy_asset/src/io/android.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
//! Asset reader for Android devices.
//! See [`AndroidAssetReader`] for details.
use crate::io::{get_meta_path, AssetReader, AssetReaderError, PathStream, Reader, VecReader};
use alloc::{borrow::ToOwned, boxed::Box, ffi::CString, vec::Vec};
use futures_lite::stream;
Expand Down
26 changes: 17 additions & 9 deletions crates/bevy_asset/src/io/embedded/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
//! Support for embedded assets, which are assets that are included inside the application executable
//! instead of being deployed alongside it.
//!
//! See the [`embedded_asset!`] macro for details.
//!
//! [`embedded_asset!`]: crate::embedded_asset

#[cfg(feature = "embedded_watcher")]
mod embedded_watcher;

Expand Down Expand Up @@ -25,9 +32,9 @@ pub const EMBEDDED: &str = "embedded";

/// A [`Resource`] that manages "rust source files" in a virtual in memory [`Dir`], which is intended
/// to be shared with a [`MemoryAssetReader`].
/// Generally this should not be interacted with directly. The [`embedded_asset`] will populate this.
/// Generally this should not be interacted with directly. The [`embedded_asset!`] macro will populate this.
///
/// [`embedded_asset`]: crate::embedded_asset
/// [`embedded_asset!`]: crate::embedded_asset
#[derive(Resource, Default)]
pub struct EmbeddedAssetRegistry {
dir: Dir,
Expand Down Expand Up @@ -147,6 +154,7 @@ impl EmbeddedAssetRegistry {
///
/// [`load_embedded_asset!`]: crate::load_embedded_asset
pub trait GetAssetServer {
/// Return a reference to the relevant [`AssetServer`].
fn get_asset_server(&self) -> &AssetServer;
}

Expand Down Expand Up @@ -212,10 +220,10 @@ macro_rules! load_embedded_asset {
}

/// Returns the [`Path`] for a given `embedded` asset.
/// This is used internally by [`embedded_asset`] and can be used to get a [`Path`]
/// This is used internally by [`embedded_asset!`] and can be used to get a [`Path`]
/// that matches the [`AssetPath`](crate::AssetPath) used by that asset.
///
/// [`embedded_asset`]: crate::embedded_asset
/// [`embedded_asset!`]: crate::embedded_asset
#[macro_export]
macro_rules! embedded_path {
($path_str: expr) => {{
Expand Down Expand Up @@ -317,11 +325,11 @@ pub fn _embedded_asset_path(
/// 2. `src` is trimmed from the path
///
/// The default behavior also works for cargo workspaces. Pretend the `bevy_rock` crate now exists in a larger workspace in
/// `$SOME_WORKSPACE/crates/bevy_rock`. The asset path would remain the same, because [`embedded_asset`] searches for the
/// `$SOME_WORKSPACE/crates/bevy_rock`. The asset path would remain the same, because [`embedded_asset!`] searches for the
/// _first instance_ of `bevy_rock/src` in the path.
///
/// For most "standard crate structures" the default works just fine. But for some niche cases (such as cargo examples),
/// the `src` path will not be present. You can override this behavior by adding it as the second argument to [`embedded_asset`]:
/// the `src` path will not be present. You can override this behavior by adding it as the second argument to [`embedded_asset!`]:
///
/// `embedded_asset!(app, "/examples/rock_stuff/", "rock.wgsl")`
///
Expand All @@ -333,13 +341,13 @@ pub fn _embedded_asset_path(
///
/// This macro uses the [`include_bytes`] macro internally and _will not_ reallocate the bytes.
/// Generally the [`AssetPath`] generated will be predictable, but if your asset isn't
/// available for some reason, you can use the [`embedded_path`] macro to debug.
/// available for some reason, you can use the [`embedded_path!`] macro to debug.
///
/// Hot-reloading `embedded` assets is supported. Just enable the `embedded_watcher` cargo feature.
///
/// [`AssetPath`]: crate::AssetPath
/// [`embedded_asset`]: crate::embedded_asset
/// [`embedded_path`]: crate::embedded_path
/// [`embedded_asset!`]: crate::embedded_asset
/// [`embedded_path!`]: crate::embedded_path
#[macro_export]
macro_rules! embedded_asset {
($app: expr, $path: expr) => {{
Expand Down
6 changes: 6 additions & 0 deletions crates/bevy_asset/src/io/file/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
//! A backend for [`Asset`] storage in the local filesystem.
//!
//! It can watch for changed assets and hotload them if the `file_watcher` feature is enabled.
//!
//! [`Asset`]: crate::Asset
#[cfg(feature = "file_watcher")]
mod file_watcher;

Expand Down
37 changes: 32 additions & 5 deletions crates/bevy_asset/src/io/memory.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
//! In-memory [`Asset`] storage backend.
//!
//! See [`Dir`] for details.
//!
//! [`Asset`]: crate::Asset

use crate::io::{
AssetReader, AssetReaderError, AssetWriter, AssetWriterError, PathStream, Reader,
ReaderNotSeekableError, SeekableReader,
Expand Down Expand Up @@ -25,8 +31,12 @@ struct DirInternal {
path: PathBuf,
}

/// A clone-able (internally Arc-ed) / thread-safe "in memory" filesystem.
/// This is built for [`MemoryAssetReader`] and is primarily intended for unit tests.
/// A clone-able (internally [`Arc`]-ed), thread-safe in-memory filesystem.
///
/// This was built for [`MemoryAssetReader`] and is primarily used by unit tests
/// and by the [`embedded`] backend.
///
/// [`embedded`]: crate::io::embedded
#[derive(Default, Clone, Debug)]
pub struct Dir(Arc<RwLock<DirInternal>>);

Expand All @@ -39,14 +49,18 @@ impl Dir {
})))
}

/// Inserts textual data as an asset, at the `path` relative to this `Dir`.
pub fn insert_asset_text(&self, path: &Path, asset: &str) {
self.insert_asset(path, asset.as_bytes().to_vec());
}

/// Inserts textual data as asset metadata, at the `path` relative to this `Dir`.
pub fn insert_meta_text(&self, path: &Path, asset: &str) {
self.insert_meta(path, asset.as_bytes().to_vec());
}

/// Inserts a [`Vec`] of bytes or a `'static` array of bytes as an asset,
/// at the `path` relative to this `Dir`.
pub fn insert_asset(&self, path: &Path, value: impl Into<Value>) {
self.insert_asset_internal(path, value.into());
}
Expand Down Expand Up @@ -87,6 +101,8 @@ impl Dir {
.remove(&key)
}

/// Inserts a [`Vec`] of bytes or a `'static` array of bytes as asset metadata,
/// at the `path` relative to this `Dir`.
pub fn insert_meta(&self, path: &Path, value: impl Into<Value>) {
self.insert_meta_internal(path, value.into());
}
Expand Down Expand Up @@ -126,6 +142,8 @@ impl Dir {
.remove(&key)
}

/// Returns the `Dir` representing `path` relative to this `Dir`,
/// creating a new one for it if necessary.
pub fn get_or_insert_dir(&self, path: &Path) -> Dir {
let mut dir = self.clone();
let mut full_path = PathBuf::new();
Expand Down Expand Up @@ -159,6 +177,7 @@ impl Dir {
.remove(&key)
}

/// Returns the `Dir` representing `path` relative to this `Dir`, if it exists.
pub fn get_dir(&self, path: &Path) -> Option<Dir> {
let mut dir = self.clone();
for p in path.components() {
Expand All @@ -175,6 +194,7 @@ impl Dir {
Some(dir)
}

/// Returns the asset stored at `path` relative to this `Dir`, if it exists.
pub fn get_asset(&self, path: &Path) -> Option<Data> {
let mut dir = self.clone();
if let Some(parent) = path.parent() {
Expand All @@ -191,6 +211,7 @@ impl Dir {
})
}

/// Returns the asset metadata stored at `path` relative to this `Dir`, if it exists.
pub fn get_metadata(&self, path: &Path) -> Option<Data> {
let mut dir = self.clone();
if let Some(parent) = path.parent() {
Expand All @@ -207,6 +228,7 @@ impl Dir {
})
}

/// Returns the path represented by this `Dir`.
pub fn path(&self) -> PathBuf {
self.0
.read()
Expand All @@ -216,6 +238,8 @@ impl Dir {
}
}

/// A struct for iteration over subdirectory and asset paths of a [`Dir`].
/// It will return full pathnames.
pub struct DirStream {
dir: Dir,
index: usize,
Expand Down Expand Up @@ -260,17 +284,20 @@ impl Stream for DirStream {
}

/// In-memory [`AssetReader`] implementation.
/// This is primarily intended for unit tests.
///
/// This is primarily used by unit tests and the [`embedded`](super::embedded) backend.
#[derive(Default, Clone)]
pub struct MemoryAssetReader {
/// The root of the in-memory filesystem backing this asset reader.
pub root: Dir,
}

/// In-memory [`AssetWriter`] implementation.
///
/// This is primarily intended for unit tests.
/// This is primarily used by unit tests and the [`embedded`](super::embedded) backend.
#[derive(Default, Clone)]
pub struct MemoryAssetWriter {
/// The root of the in-memory filesystem backing this asset writer.
pub root: Dir,
}

Expand Down Expand Up @@ -588,7 +615,7 @@ impl AssetWriter for MemoryAssetWriter {
}

#[cfg(test)]
pub mod test {
mod test {
use super::Dir;
use std::path::Path;

Expand Down
10 changes: 10 additions & 0 deletions crates/bevy_asset/src/io/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
//! Readers and writers for byte streams that represent assets.
//!
//! These are implemented for a variety of backends.
//! See the submodules for details on those.

#[cfg(all(feature = "file_watcher", target_arch = "wasm32"))]
compile_error!(
"The \"file_watcher\" feature for hot reloading does not work \
Expand Down Expand Up @@ -307,8 +312,10 @@ impl<T: AssetReader> ErasedAssetReader for T {
}
}

/// A convenience type for an [`AsyncWrite`] object plus the traits it needs to satisfy.
pub type Writer = dyn AsyncWrite + Unpin + Send + Sync;

/// A convenience type for a stream of pathnames plus the traits it needs to satisfy.
pub type PathStream = dyn Stream<Item = PathBuf> + Unpin + Send;

/// Errors that occur while loading assets.
Expand Down Expand Up @@ -564,6 +571,7 @@ pub enum AssetSourceEvent {
/// An asset at this path was removed.
RemovedAsset(PathBuf),
/// An asset at this path was renamed.
#[expect(missing_docs, reason = "the names are self documenting")]
RenamedAsset { old: PathBuf, new: PathBuf },
/// Asset metadata at this path was added.
AddedMeta(PathBuf),
Expand All @@ -572,12 +580,14 @@ pub enum AssetSourceEvent {
/// Asset metadata at this path was removed.
RemovedMeta(PathBuf),
/// Asset metadata at this path was renamed.
#[expect(missing_docs, reason = "the names are self documenting")]
RenamedMeta { old: PathBuf, new: PathBuf },
/// A folder at the given path was added.
AddedFolder(PathBuf),
/// A folder at the given path was removed.
RemovedFolder(PathBuf),
/// A folder at the given path was renamed.
#[expect(missing_docs, reason = "the names are self documenting")]
RenamedFolder { old: PathBuf, new: PathBuf },
/// Something of unknown type was removed. It is the job of the event handler to determine the type.
/// This exists because notify-rs produces "untyped" rename events without destination paths for unwatched folders, so we can't determine the type of
Expand Down
5 changes: 5 additions & 0 deletions crates/bevy_asset/src/io/processor_gated.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
//! Tools for waiting for the [`AssetProcessor`] before loading an [`Asset`].
//!
//! [`AssetProcessor`]: crate::processor::AssetProcessor
//! [`Asset`]: crate::Asset
use crate::{
io::{
AssetReader, AssetReaderError, AssetSourceId, PathStream, Reader, ReaderNotSeekableError,
Expand Down
5 changes: 5 additions & 0 deletions crates/bevy_asset/src/io/web.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
//! Adds the `http` and `https` asset sources to the app.
//!
//! See [`WebAssetPlugin`] for details.

use crate::io::{AssetReader, AssetReaderError, AssetSourceBuilder, PathStream, Reader};
use crate::{AssetApp, AssetPlugin};
use alloc::boxed::Box;
Expand Down Expand Up @@ -56,6 +60,7 @@ use tracing::warn;
/// ```
#[derive(Default)]
pub struct WebAssetPlugin {
/// Set this if you have seen the warning about URL safety enough times.
pub silence_startup_warning: bool,
}

Expand Down
1 change: 1 addition & 0 deletions crates/bevy_asset/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,7 @@ pub trait AsAssetId: Component {
///
/// Note that this trait is automatically implemented when deriving [`Asset`].
pub trait VisitAssetDependencies {
/// Apply the `visit` closure to every asset dependency.
fn visit_dependencies(&self, visit: &mut impl FnMut(UntypedAssetId));
}

Expand Down
9 changes: 9 additions & 0 deletions crates/bevy_asset/src/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,22 +342,31 @@ impl<A: Asset> AssetContainer for A {
/// An error that occurs when attempting an async load using [`NestedLoadBuilder`].
#[derive(Error, Debug)]
pub enum LoadDirectError {
/// The asset path was empty.
#[error("Attempted to load an asset with an empty path \"{0}\"")]
EmptyPath(AssetPath<'static>),
/// Loading an asset path with a subasset at the end is unsupported. See issue [#18291].
///
/// [#18291]: https://github.com/bevyengine/bevy/issues/18291
#[error("Requested to load an asset path ({0:?}) with a subasset, but this is unsupported. See issue #18291")]
RequestedSubasset(AssetPath<'static>),
/// A general [`AssetLoadError`] for an asset dependency.
#[error("Failed to load dependency {dependency:?} {error}")]
LoadError {
/// Which dependency failed.
dependency: AssetPath<'static>,
/// The original error for that dependency.
error: Box<AssetLoadError>,
},
}

/// An error that occurs while deserializing [`AssetMeta`].
#[derive(Error, Debug, Clone, PartialEq, Eq)]
pub enum DeserializeMetaError {
/// Failed to deserialize the asset metadata.
#[error("Failed to deserialize asset meta: {0:?}")]
DeserializeSettings(#[from] SpannedError),
/// Failed to deserialize the minimal asset metadata.
#[error("Failed to deserialize minimal asset meta: {0:?}")]
DeserializeMinimal(SpannedError),
}
Expand Down
Loading
Loading