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
21 changes: 21 additions & 0 deletions _release-content/migration-guides/load_builder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
title: Advanced AssetServer load variants are now expose through a builder pattern.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
title: Advanced AssetServer load variants are now expose through a builder pattern.
title: Advanced AssetServer load variants are now exposed through a builder pattern.

pull_requests: []
---

In previous versions of Bevy, there were many different ways to load an asset:

- `AssetServer::load`
- `AssetServer::load_acquire`
- `AssetServer::load_untyped`
- `AssetServer::load_acquire_override_with_settings`
- etc.

All these variants have been simplified to only two variants:

1. `AssetServer::load()`: This is just a convenience and just calls the load builder internally.
2. `AssetServer::load_builder()`: allows for constructing more complex loads like untyped loads,
loads including guards, loads with settings, etc.

Every load variant above can be reimplemented using `load_builder`, and each one of these methods
has deprecation messages on them explaining their new equivalent.
Comment on lines +20 to +21
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Every load variant above can be reimplemented using `load_builder`, and each one of these methods
has deprecation messages on them explaining their new equivalent.
Every load variant above can be reimplemented using `load_builder`, and each one of these methods
has deprecation messages on them explaining their new equivalent.
For example, `load_with_settings_override` can be replaced by:
```rust
asset_server
.load_builder()
.with_settings(settings)
.override_unapproved()
.load(path)

I thought one example here might be nice so the user gets the flavor of things.

19 changes: 17 additions & 2 deletions crates/bevy_asset/src/direct_access_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

use bevy_ecs::world::World;

use crate::{meta::Settings, Asset, AssetPath, AssetServer, Assets, Handle};
use crate::{meta::Settings, Asset, AssetPath, AssetServer, Assets, Handle, LoadBuilder};

/// An extension trait for methods for working with assets directly from a [`World`].
pub trait DirectAssetAccessExt {
Expand All @@ -13,7 +13,11 @@ pub trait DirectAssetAccessExt {
/// Load an asset similarly to [`AssetServer::load`].
fn load_asset<'a, A: Asset>(&self, path: impl Into<AssetPath<'a>>) -> Handle<A>;

/// Creates a new [`LoadBuilder`] similar to [`AssetServer::load_builder`].
fn load_builder(&self) -> LoadBuilder<'_>;

/// Load an asset with settings, similarly to [`AssetServer::load_with_settings`].
#[deprecated(note = "Use `world.load_builder().with_settings(settings).load(path)`")]
fn load_asset_with_settings<'a, A: Asset, S: Settings>(
&self,
path: impl Into<AssetPath<'a>>,
Expand All @@ -37,6 +41,15 @@ impl DirectAssetAccessExt for World {
fn load_asset<'a, A: Asset>(&self, path: impl Into<AssetPath<'a>>) -> Handle<A> {
self.resource::<AssetServer>().load(path)
}

/// Creates a new [`LoadBuilder`] similar to [`AssetServer::load_builder`].
///
/// # Panics
/// If `self` doesn't have an [`AssetServer`] resource initialized yet.
fn load_builder(&self) -> LoadBuilder<'_> {
self.resource::<AssetServer>().load_builder()
}

/// Load an asset with settings, similarly to [`AssetServer::load_with_settings`].
///
/// # Panics
Expand All @@ -47,6 +60,8 @@ impl DirectAssetAccessExt for World {
settings: impl Fn(&mut S) + Send + Sync + 'static,
) -> Handle<A> {
self.resource::<AssetServer>()
.load_with_settings(path, settings)
.load_builder()
.with_settings(settings)
.load(path.into())
}
}
7 changes: 4 additions & 3 deletions crates/bevy_asset/src/io/embedded/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,9 @@ impl GetAssetServer for AssetServer {
/// This macro takes two arguments and an optional third one:
/// 1. The asset source. It may be `AssetServer`, `World` or `App`.
/// 2. The path to the asset to embed, as a string literal.
/// 3. Optionally, a closure of the same type as in [`AssetServer::load_with_settings`].
/// Consider explicitly typing the closure argument in case of type error.
/// 3. Optionally, a closure of the same type as in
/// [`LoadBuilder::with_settings`](crate::LoadBuilder::with_settings). Consider explicitly typing
/// the closure argument in case of type error.
///
/// # Usage
///
Expand All @@ -196,7 +197,7 @@ macro_rules! load_embedded_asset {
}};
($provider: expr, $path: literal, $settings: expr) => {{
let (path, asset_server) = $crate::load_embedded_asset!(@get: $path, $provider);
asset_server.load_with_settings(path, $settings)
asset_server.load_builder().with_settings($settings).load(path)
}};
($provider: expr, $path: literal) => {{
let (path, asset_server) = $crate::load_embedded_asset!(@get: $path, $provider);
Expand Down
22 changes: 14 additions & 8 deletions crates/bevy_asset/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,8 +277,8 @@ pub struct AssetPlugin {
pub enum UnapprovedPathMode {
/// Unapproved asset loading is allowed. This is strongly discouraged.
Allow,
/// Fails to load any asset that is unapproved, unless an override method is used, like
/// [`AssetServer::load_override`].
/// Fails to load any asset that is unapproved, unless [`LoadBuilder::override_unapproved`] is
/// used.
Deny,
/// Fails to load any asset that is unapproved.
#[default]
Expand Down Expand Up @@ -2116,7 +2116,10 @@ mod tests {

let asset_server = app.world().resource::<AssetServer>().clone();
assert_eq!(
asset_server.load_override::<CoolText>("../a.cool.ron"),
asset_server
.load_builder()
.override_unapproved()
.load::<CoolText>("../a.cool.ron"),
Handle::default()
);
}
Expand All @@ -2137,7 +2140,10 @@ mod tests {
let mut app = unapproved_path_setup(UnapprovedPathMode::Deny);

let asset_server = app.world().resource::<AssetServer>().clone();
let handle = asset_server.load_override::<CoolText>("../a.cool.ron");
let handle = asset_server
.load_builder()
.override_unapproved()
.load::<CoolText>("../a.cool.ron");
assert_ne!(handle, Handle::default());

// Make sure this asset actually loads.
Expand Down Expand Up @@ -2529,10 +2535,10 @@ mod tests {
// Load the test asset twice but with different settings.

fn load(asset_server: &AssetServer, path: &'static str, value: u8) -> Handle<U8Asset> {
asset_server.load_with_settings::<U8Asset, U8LoaderSettings>(
path,
move |s: &mut U8LoaderSettings| s.0 = value,
)
asset_server
.load_builder()
.with_settings(move |s: &mut U8LoaderSettings| s.0 = value)
.load::<U8Asset>(path)
}

let handle_1 = load(asset_server, "test.u8", 1);
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_asset/src/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -605,7 +605,7 @@ impl<'a> LoadContext<'a> {
label: impl Into<CowArc<'b, str>>,
) -> Handle<A> {
let path = self.asset_path.clone().with_label(label);
let handle = self.asset_server.get_or_create_path_handle::<A>(path, None);
let handle = self.asset_server.get_or_create_path_handle(path, None);
// `get_or_create_path_handle` always returns a Strong variant, so we are safe to unwrap.
let index = (&handle).try_into().unwrap();
self.dependencies.insert(index);
Expand Down
9 changes: 6 additions & 3 deletions crates/bevy_asset/src/loader_builders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,22 +337,25 @@ impl NestedLoader<'_, '_, DynamicTyped, Deferred> {
let handle = if self.load_context.should_load_dependencies {
self.load_context
.asset_server
.load_erased_with_meta_transform(
.load_with_meta_transform_erased(
path,
self.typing.asset_type_id,
None,
self.meta_transform,
(),
false,
)
} else {
self.load_context
.asset_server
.get_or_create_path_handle_erased(
path,
self.typing.asset_type_id,
None,
self.meta_transform,
)
};
// `load_erased_with_meta_transform` and `get_or_create_path_handle_erased` always returns a
// `load_with_meta_transform_erased` and `get_or_create_path_handle_erased` always returns a
// Strong variant, so we are safe to unwrap.
let index = (&handle).try_into().unwrap();
self.load_context.dependencies.insert(index);
Expand All @@ -370,7 +373,7 @@ impl NestedLoader<'_, '_, UnknownTyped, Deferred> {
let handle = if self.load_context.should_load_dependencies {
self.load_context
.asset_server
.load_unknown_type_with_meta_transform(path, self.meta_transform)
.load_unknown_type_with_meta_transform(path, self.meta_transform, (), false)
} else {
self.load_context
.asset_server
Expand Down
8 changes: 4 additions & 4 deletions crates/bevy_asset/src/reflect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ impl ReflectSerializerProcessor for HandleSerializeProcessor {
pub trait LoadFromPath {
/// Initiates the load for the given expected type ID, and the path.
///
/// See [`AssetServer::load_erased`] for more.
/// See [`LoadBuilder::load_erased`](crate::LoadBuilder::load_erased) for more.
fn load_from_path_erased(&mut self, type_id: TypeId, path: AssetPath<'static>)
-> UntypedHandle;
}
Expand All @@ -419,7 +419,7 @@ impl LoadFromPath for AssetServer {
type_id: TypeId,
path: AssetPath<'static>,
) -> UntypedHandle {
self.load_erased(type_id, path)
self.load_builder().load_erased(type_id, path)
}
}

Expand All @@ -429,7 +429,7 @@ impl LoadFromPath for &AssetServer {
type_id: TypeId,
path: AssetPath<'static>,
) -> UntypedHandle {
self.load_erased(type_id, path)
self.load_builder().load_erased(type_id, path)
}
}

Expand Down Expand Up @@ -625,7 +625,7 @@ mod tests {
let type_registry = app.world().resource::<AppTypeRegistry>().0.clone();
let asset_server = app.world().resource::<AssetServer>().clone();

let untyped = asset_server.load_untyped("def.cool.ron");
let untyped = asset_server.load_builder().load_untyped("def.cool.ron");
run_app_until(&mut app, |_| asset_server.is_loaded(&untyped).then_some(()));
let untyped = app
.world()
Expand Down
34 changes: 16 additions & 18 deletions crates/bevy_asset/src/server/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ use bevy_ecs::world::World;
use bevy_platform::collections::{hash_map::Entry, HashMap, HashSet};
use bevy_tasks::Task;
use bevy_utils::TypeIdMap;
use core::{any::TypeId, task::Waker};
use core::{
any::{type_name, TypeId},
task::Waker,
};
use crossbeam_channel::Sender;
use either::Either;
use thiserror::Error;
use tracing::warn;

Expand Down Expand Up @@ -123,7 +125,8 @@ impl AssetInfos {
None,
true,
),
Either::Left(type_name),
type_id,
Some(type_name),
)
.unwrap()
}
Expand Down Expand Up @@ -168,15 +171,13 @@ impl AssetInfos {
loading_mode: HandleLoadingMode,
meta_transform: Option<MetaTransform>,
) -> (Handle<A>, bool) {
let result = self.get_or_create_path_handle_internal(
let (handle, should_load) = self.get_or_create_path_handle_erased(
path,
Some(TypeId::of::<A>()),
TypeId::of::<A>(),
Some(type_name::<A>()),
loading_mode,
meta_transform,
);
// it is ok to unwrap because TypeId was specified above
let (handle, should_load) =
unwrap_with_context(result, Either::Left(core::any::type_name::<A>())).unwrap();
(handle.typed_unchecked(), should_load)
}

Expand All @@ -194,12 +195,8 @@ impl AssetInfos {
loading_mode,
meta_transform,
);
let type_info = match type_name {
Some(type_name) => Either::Left(type_name),
None => Either::Right(type_id),
};
unwrap_with_context(result, type_info)
.expect("type should be correct since the `TypeId` is specified above")
// it is ok to unwrap because TypeId was specified above
unwrap_with_context(result, type_id, type_name).unwrap()
}

/// Retrieves asset tracking data, or creates it if it doesn't exist.
Expand Down Expand Up @@ -809,17 +806,18 @@ pub(crate) enum GetOrCreateHandleInternalError {

pub(crate) fn unwrap_with_context<T>(
result: Result<T, GetOrCreateHandleInternalError>,
type_info: Either<&str, TypeId>,
type_id: TypeId,
type_name: Option<&str>,
) -> Option<T> {
match result {
Ok(value) => Some(value),
Err(GetOrCreateHandleInternalError::HandleMissingButTypeIdNotSpecified) => None,
Err(GetOrCreateHandleInternalError::MissingHandleProviderError(_)) => match type_info {
Either::Left(type_name) => {
Err(GetOrCreateHandleInternalError::MissingHandleProviderError(_)) => match type_name {
Some(type_name) => {
panic!("Cannot allocate an Asset Handle of type '{type_name}' because the asset type has not been initialized. \
Make sure you have called `app.init_asset::<{type_name}>()`");
}
Either::Right(type_id) => {
None => {
panic!("Cannot allocate an AssetHandle of type '{type_id:?}' because the asset type has not been initialized. \
Make sure you have called `app.init_asset::<(actual asset type)>()`")
}
Expand Down
Loading