Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_asset/src/loader.rs
9366 views
1
use crate::{
2
io::{AssetReaderError, MissingAssetSourceError, MissingProcessedAssetReaderError, Reader},
3
loader_builders::{Deferred, NestedLoader, StaticTyped},
4
meta::{AssetHash, AssetMeta, AssetMetaDyn, ProcessedInfo, ProcessedInfoMinimal, Settings},
5
path::AssetPath,
6
Asset, AssetIndex, AssetLoadError, AssetServer, AssetServerMode, Assets, ErasedAssetIndex,
7
Handle, UntypedAssetId, UntypedHandle,
8
};
9
use alloc::{boxed::Box, string::ToString, vec::Vec};
10
use atomicow::CowArc;
11
use bevy_ecs::{error::BevyError, world::World};
12
use bevy_platform::collections::{HashMap, HashSet};
13
use bevy_reflect::TypePath;
14
use bevy_tasks::{BoxedFuture, ConditionalSendFuture};
15
use core::any::{Any, TypeId};
16
use downcast_rs::{impl_downcast, Downcast};
17
use ron::error::SpannedError;
18
use serde::{Deserialize, Serialize};
19
use std::path::PathBuf;
20
use thiserror::Error;
21
22
/// Loads an [`Asset`] from a given byte [`Reader`]. This can accept [`AssetLoader::Settings`], which configure how the [`Asset`]
23
/// should be loaded.
24
///
25
/// This trait is generally used in concert with [`AssetReader`](crate::io::AssetReader) to load assets from a byte source.
26
///
27
/// For a complementary version of this trait that can save assets, see [`AssetSaver`](crate::saver::AssetSaver).
28
pub trait AssetLoader: TypePath + Send + Sync + 'static {
29
/// The top level [`Asset`] loaded by this [`AssetLoader`].
30
type Asset: Asset;
31
/// The settings type used by this [`AssetLoader`].
32
type Settings: Settings + Default + Serialize + for<'a> Deserialize<'a>;
33
/// The type of [error](`std::error::Error`) which could be encountered by this loader.
34
type Error: Into<BevyError>;
35
/// Asynchronously loads [`AssetLoader::Asset`] (and any other labeled assets) from the bytes provided by [`Reader`].
36
fn load(
37
&self,
38
reader: &mut dyn Reader,
39
settings: &Self::Settings,
40
load_context: &mut LoadContext,
41
) -> impl ConditionalSendFuture<Output = Result<Self::Asset, Self::Error>>;
42
43
/// Returns a list of extensions supported by this [`AssetLoader`], without the preceding dot.
44
/// Note that users of this [`AssetLoader`] may choose to load files with a non-matching extension.
45
fn extensions(&self) -> &[&str] {
46
&[]
47
}
48
}
49
50
/// Provides type-erased access to an [`AssetLoader`].
51
pub trait ErasedAssetLoader: Send + Sync + 'static {
52
/// Asynchronously loads the asset(s) from the bytes provided by [`Reader`].
53
fn load<'a>(
54
&'a self,
55
reader: &'a mut dyn Reader,
56
settings: &'a dyn Settings,
57
load_context: LoadContext<'a>,
58
) -> BoxedFuture<'a, Result<ErasedLoadedAsset, BevyError>>;
59
60
/// Returns a list of extensions supported by this asset loader, without the preceding dot.
61
fn extensions(&self) -> &[&str];
62
/// Deserializes metadata from the input `meta` bytes into the appropriate type (erased as [`Box<dyn AssetMetaDyn>`]).
63
fn deserialize_meta(&self, meta: &[u8]) -> Result<Box<dyn AssetMetaDyn>, DeserializeMetaError>;
64
/// Returns the default meta value for the [`AssetLoader`] (erased as [`Box<dyn AssetMetaDyn>`]).
65
fn default_meta(&self) -> Box<dyn AssetMetaDyn>;
66
/// Returns the type path of the [`AssetLoader`].
67
fn type_path(&self) -> &'static str;
68
/// Returns the [`TypeId`] of the [`AssetLoader`].
69
fn type_id(&self) -> TypeId;
70
/// Returns the type name of the top-level [`Asset`] loaded by the [`AssetLoader`].
71
fn asset_type_name(&self) -> &'static str;
72
/// Returns the [`TypeId`] of the top-level [`Asset`] loaded by the [`AssetLoader`].
73
fn asset_type_id(&self) -> TypeId;
74
}
75
76
impl<L> ErasedAssetLoader for L
77
where
78
L: AssetLoader + Send + Sync,
79
{
80
/// Processes the asset in an asynchronous closure.
81
fn load<'a>(
82
&'a self,
83
reader: &'a mut dyn Reader,
84
settings: &'a dyn Settings,
85
mut load_context: LoadContext<'a>,
86
) -> BoxedFuture<'a, Result<ErasedLoadedAsset, BevyError>> {
87
Box::pin(async move {
88
let settings = settings
89
.downcast_ref::<L::Settings>()
90
.expect("AssetLoader settings should match the loader type");
91
let asset = <L as AssetLoader>::load(self, reader, settings, &mut load_context)
92
.await
93
.map_err(Into::into)?;
94
Ok(load_context.finish(asset).into())
95
})
96
}
97
98
fn extensions(&self) -> &[&str] {
99
<L as AssetLoader>::extensions(self)
100
}
101
102
fn deserialize_meta(&self, meta: &[u8]) -> Result<Box<dyn AssetMetaDyn>, DeserializeMetaError> {
103
let meta = AssetMeta::<L, ()>::deserialize(meta)?;
104
Ok(Box::new(meta))
105
}
106
107
fn default_meta(&self) -> Box<dyn AssetMetaDyn> {
108
Box::new(AssetMeta::<L, ()>::new(crate::meta::AssetAction::Load {
109
loader: self.type_path().to_string(),
110
settings: L::Settings::default(),
111
}))
112
}
113
114
fn type_path(&self) -> &'static str {
115
L::type_path()
116
}
117
118
fn type_id(&self) -> TypeId {
119
TypeId::of::<L>()
120
}
121
122
fn asset_type_name(&self) -> &'static str {
123
core::any::type_name::<L::Asset>()
124
}
125
126
fn asset_type_id(&self) -> TypeId {
127
TypeId::of::<L::Asset>()
128
}
129
}
130
131
pub(crate) struct LabeledAsset {
132
pub(crate) asset: ErasedLoadedAsset,
133
pub(crate) handle: UntypedHandle,
134
}
135
136
/// The successful result of an [`AssetLoader::load`] call. This contains the loaded "root" asset and any other "labeled" assets produced
137
/// by the loader. It also holds the input [`AssetMeta`] (if it exists) and tracks dependencies:
138
/// * normal dependencies: dependencies that must be loaded as part of this asset load (ex: assets a given asset has handles to).
139
/// * Loader dependencies: dependencies whose actual asset values are used during the load process
140
pub struct LoadedAsset<A: Asset> {
141
pub(crate) value: A,
142
pub(crate) dependencies: HashSet<ErasedAssetIndex>,
143
pub(crate) loader_dependencies: HashMap<AssetPath<'static>, AssetHash>,
144
pub(crate) labeled_assets: HashMap<CowArc<'static, str>, LabeledAsset>,
145
}
146
147
impl<A: Asset> LoadedAsset<A> {
148
/// Create a new loaded asset. This will use [`VisitAssetDependencies`](crate::VisitAssetDependencies) to populate `dependencies`.
149
pub fn new_with_dependencies(value: A) -> Self {
150
let mut dependencies = <HashSet<_>>::default();
151
value.visit_dependencies(&mut |id| {
152
let Ok(asset_index) = id.try_into() else {
153
return;
154
};
155
dependencies.insert(asset_index);
156
});
157
LoadedAsset {
158
value,
159
dependencies,
160
loader_dependencies: HashMap::default(),
161
labeled_assets: HashMap::default(),
162
}
163
}
164
165
/// Cast (and take ownership) of the [`Asset`] value of the given type.
166
pub fn take(self) -> A {
167
self.value
168
}
169
170
/// Retrieves a reference to the internal [`Asset`] type.
171
pub fn get(&self) -> &A {
172
&self.value
173
}
174
175
/// Returns the [`ErasedLoadedAsset`] for the given label, if it exists.
176
pub fn get_labeled(
177
&self,
178
label: impl Into<CowArc<'static, str>>,
179
) -> Option<&ErasedLoadedAsset> {
180
self.labeled_assets.get(&label.into()).map(|a| &a.asset)
181
}
182
183
/// Iterate over all labels for "labeled assets" in the loaded asset
184
pub fn iter_labels(&self) -> impl Iterator<Item = &str> {
185
self.labeled_assets.keys().map(|s| &**s)
186
}
187
}
188
189
impl<A: Asset> From<A> for LoadedAsset<A> {
190
fn from(asset: A) -> Self {
191
LoadedAsset::new_with_dependencies(asset)
192
}
193
}
194
195
/// A "type erased / boxed" counterpart to [`LoadedAsset`]. This is used in places where the loaded type is not statically known.
196
pub struct ErasedLoadedAsset {
197
pub(crate) value: Box<dyn AssetContainer>,
198
pub(crate) dependencies: HashSet<ErasedAssetIndex>,
199
pub(crate) loader_dependencies: HashMap<AssetPath<'static>, AssetHash>,
200
pub(crate) labeled_assets: HashMap<CowArc<'static, str>, LabeledAsset>,
201
}
202
203
impl<A: Asset> From<LoadedAsset<A>> for ErasedLoadedAsset {
204
fn from(asset: LoadedAsset<A>) -> Self {
205
ErasedLoadedAsset {
206
value: Box::new(asset.value),
207
dependencies: asset.dependencies,
208
loader_dependencies: asset.loader_dependencies,
209
labeled_assets: asset.labeled_assets,
210
}
211
}
212
}
213
214
impl ErasedLoadedAsset {
215
/// Cast (and take ownership) of the [`Asset`] value of the given type. This will return [`Some`] if
216
/// the stored type matches `A` and [`None`] if it does not.
217
pub fn take<A: Asset>(self) -> Option<A> {
218
self.value.downcast::<A>().map(|a| *a).ok()
219
}
220
221
/// Retrieves a reference to the internal [`Asset`] type, if it matches the type `A`. Otherwise returns [`None`].
222
pub fn get<A: Asset>(&self) -> Option<&A> {
223
self.value.downcast_ref::<A>()
224
}
225
226
/// Retrieves the [`TypeId`] of the stored [`Asset`] type.
227
pub fn asset_type_id(&self) -> TypeId {
228
(*self.value).type_id()
229
}
230
231
/// Retrieves the `type_name` of the stored [`Asset`] type.
232
pub fn asset_type_name(&self) -> &'static str {
233
self.value.asset_type_name()
234
}
235
236
/// Returns the [`ErasedLoadedAsset`] for the given label, if it exists.
237
pub fn get_labeled(
238
&self,
239
label: impl Into<CowArc<'static, str>>,
240
) -> Option<&ErasedLoadedAsset> {
241
self.labeled_assets.get(&label.into()).map(|a| &a.asset)
242
}
243
244
/// Iterate over all labels for "labeled assets" in the loaded asset
245
pub fn iter_labels(&self) -> impl Iterator<Item = &str> {
246
self.labeled_assets.keys().map(|s| &**s)
247
}
248
249
/// Cast this loaded asset as the given type. If the type does not match,
250
/// the original type-erased asset is returned.
251
pub fn downcast<A: Asset>(mut self) -> Result<LoadedAsset<A>, ErasedLoadedAsset> {
252
match self.value.downcast::<A>() {
253
Ok(value) => Ok(LoadedAsset {
254
value: *value,
255
dependencies: self.dependencies,
256
loader_dependencies: self.loader_dependencies,
257
labeled_assets: self.labeled_assets,
258
}),
259
Err(value) => {
260
self.value = value;
261
Err(self)
262
}
263
}
264
}
265
}
266
267
/// A type erased container for an [`Asset`] value that is capable of inserting the [`Asset`] into a [`World`]'s [`Assets`] collection.
268
pub(crate) trait AssetContainer: Downcast + Any + Send + Sync + 'static {
269
fn insert(self: Box<Self>, id: AssetIndex, world: &mut World);
270
fn asset_type_name(&self) -> &'static str;
271
}
272
273
impl_downcast!(AssetContainer);
274
275
impl<A: Asset> AssetContainer for A {
276
fn insert(self: Box<Self>, index: AssetIndex, world: &mut World) {
277
// We only ever call this if we know the asset is still alive, so it is fine to unwrap here.
278
world
279
.resource_mut::<Assets<A>>()
280
.insert(index, *self)
281
.expect("the AssetIndex is still valid");
282
}
283
284
fn asset_type_name(&self) -> &'static str {
285
core::any::type_name::<A>()
286
}
287
}
288
289
/// An error that occurs when attempting to call [`NestedLoader::load`] which
290
/// is configured to work [immediately].
291
///
292
/// [`NestedLoader::load`]: crate::NestedLoader::load
293
/// [immediately]: crate::Immediate
294
#[derive(Error, Debug)]
295
pub enum LoadDirectError {
296
#[error("Requested to load an asset path ({0:?}) with a subasset, but this is unsupported. See issue #18291")]
297
RequestedSubasset(AssetPath<'static>),
298
#[error("Failed to load dependency {dependency:?} {error}")]
299
LoadError {
300
dependency: AssetPath<'static>,
301
error: AssetLoadError,
302
},
303
}
304
305
/// An error that occurs while deserializing [`AssetMeta`].
306
#[derive(Error, Debug, Clone, PartialEq, Eq)]
307
pub enum DeserializeMetaError {
308
#[error("Failed to deserialize asset meta: {0:?}")]
309
DeserializeSettings(#[from] SpannedError),
310
#[error("Failed to deserialize minimal asset meta: {0:?}")]
311
DeserializeMinimal(SpannedError),
312
}
313
314
/// A context that provides access to assets in [`AssetLoader`]s, tracks dependencies, and collects asset load state.
315
///
316
/// Any asset state accessed by [`LoadContext`] will be tracked and stored for use in dependency events and asset preprocessing.
317
pub struct LoadContext<'a> {
318
pub(crate) asset_server: &'a AssetServer,
319
/// Specifies whether dependencies that are loaded deferred should be loaded.
320
///
321
/// This allows us to skip loads for cases where we're never going to use the asset and we just
322
/// need the dependency information, for example during asset processing.
323
pub(crate) should_load_dependencies: bool,
324
populate_hashes: bool,
325
asset_path: AssetPath<'static>,
326
pub(crate) dependencies: HashSet<ErasedAssetIndex>,
327
/// Direct dependencies used by this loader.
328
pub(crate) loader_dependencies: HashMap<AssetPath<'static>, AssetHash>,
329
pub(crate) labeled_assets: HashMap<CowArc<'static, str>, LabeledAsset>,
330
}
331
332
impl<'a> LoadContext<'a> {
333
/// Creates a new [`LoadContext`] instance.
334
pub(crate) fn new(
335
asset_server: &'a AssetServer,
336
asset_path: AssetPath<'static>,
337
should_load_dependencies: bool,
338
populate_hashes: bool,
339
) -> Self {
340
Self {
341
asset_server,
342
asset_path,
343
populate_hashes,
344
should_load_dependencies,
345
dependencies: HashSet::default(),
346
loader_dependencies: HashMap::default(),
347
labeled_assets: HashMap::default(),
348
}
349
}
350
351
/// Begins a new labeled asset load. Use the returned [`LoadContext`] to load
352
/// dependencies for the new asset and call [`LoadContext::finish`] to finalize the asset load.
353
/// When finished, make sure you call [`LoadContext::add_loaded_labeled_asset`] to add the results back to the parent
354
/// context.
355
/// Prefer [`LoadContext::labeled_asset_scope`] when possible, which will automatically add
356
/// the labeled [`LoadContext`] back to the parent context.
357
/// [`LoadContext::begin_labeled_asset`] exists largely to enable parallel asset loading.
358
///
359
/// See [`AssetPath`] for more on labeled assets.
360
///
361
/// ```no_run
362
/// # use bevy_asset::{Asset, LoadContext};
363
/// # use bevy_reflect::TypePath;
364
/// # #[derive(Asset, TypePath, Default)]
365
/// # struct Image;
366
/// # let load_context: LoadContext = panic!();
367
/// let mut handles = Vec::new();
368
/// for i in 0..2 {
369
/// let labeled = load_context.begin_labeled_asset();
370
/// handles.push(std::thread::spawn(move || {
371
/// (i.to_string(), labeled.finish(Image::default()))
372
/// }));
373
/// }
374
///
375
/// for handle in handles {
376
/// let (label, loaded_asset) = handle.join().unwrap();
377
/// load_context.add_loaded_labeled_asset(label, loaded_asset);
378
/// }
379
/// ```
380
pub fn begin_labeled_asset(&self) -> LoadContext<'_> {
381
LoadContext::new(
382
self.asset_server,
383
self.asset_path.clone(),
384
self.should_load_dependencies,
385
self.populate_hashes,
386
)
387
}
388
389
/// Creates a new [`LoadContext`] for the given `label`. The `load` function is responsible for loading an [`Asset`] of
390
/// type `A`. `load` will be called immediately and the result will be used to finalize the [`LoadContext`], resulting in a new
391
/// [`LoadedAsset`], which is registered under the `label` label.
392
///
393
/// This exists to remove the need to manually call [`LoadContext::begin_labeled_asset`] and then manually register the
394
/// result with [`LoadContext::add_loaded_labeled_asset`].
395
///
396
/// See [`AssetPath`] for more on labeled assets.
397
pub fn labeled_asset_scope<A: Asset, E>(
398
&mut self,
399
label: impl Into<CowArc<'static, str>>,
400
load: impl FnOnce(&mut LoadContext) -> Result<A, E>,
401
) -> Result<Handle<A>, E> {
402
let mut context = self.begin_labeled_asset();
403
let asset = load(&mut context)?;
404
let loaded_asset = context.finish(asset);
405
Ok(self.add_loaded_labeled_asset(label, loaded_asset))
406
}
407
408
/// This will add the given `asset` as a "labeled [`Asset`]" with the `label` label.
409
///
410
/// # Warning
411
///
412
/// This will not assign dependencies to the given `asset`. If adding an asset
413
/// with dependencies generated from calls such as [`LoadContext::load`], use
414
/// [`LoadContext::labeled_asset_scope`] or [`LoadContext::begin_labeled_asset`] to generate a
415
/// new [`LoadContext`] to track the dependencies for the labeled asset.
416
///
417
/// See [`AssetPath`] for more on labeled assets.
418
pub fn add_labeled_asset<A: Asset>(
419
&mut self,
420
label: impl Into<CowArc<'static, str>>,
421
asset: A,
422
) -> Handle<A> {
423
self.labeled_asset_scope(label, |_| Ok::<_, ()>(asset))
424
.expect("the closure returns Ok")
425
}
426
427
/// Add a [`LoadedAsset`] that is a "labeled sub asset" of the root path of this load context.
428
/// This can be used in combination with [`LoadContext::begin_labeled_asset`] to parallelize
429
/// sub asset loading.
430
///
431
/// See [`AssetPath`] for more on labeled assets.
432
pub fn add_loaded_labeled_asset<A: Asset>(
433
&mut self,
434
label: impl Into<CowArc<'static, str>>,
435
loaded_asset: LoadedAsset<A>,
436
) -> Handle<A> {
437
let label = label.into();
438
let loaded_asset: ErasedLoadedAsset = loaded_asset.into();
439
let labeled_path = self.asset_path.clone().with_label(label.clone());
440
let handle = self
441
.asset_server
442
.get_or_create_path_handle(labeled_path, None);
443
self.labeled_assets.insert(
444
label,
445
LabeledAsset {
446
asset: loaded_asset,
447
handle: handle.clone().untyped(),
448
},
449
);
450
handle
451
}
452
453
/// Returns `true` if an asset with the label `label` exists in this context.
454
///
455
/// See [`AssetPath`] for more on labeled assets.
456
pub fn has_labeled_asset<'b>(&self, label: impl Into<CowArc<'b, str>>) -> bool {
457
let path = self.asset_path.clone().with_label(label.into());
458
!self.asset_server.get_handles_untyped(&path).is_empty()
459
}
460
461
/// "Finishes" this context by populating the final [`Asset`] value.
462
pub fn finish<A: Asset>(mut self, value: A) -> LoadedAsset<A> {
463
// At this point, we assume the asset/subasset is "locked in" and won't be changed, so we
464
// can ensure all the dependencies are included (in case a handle was used without loading
465
// it through this `LoadContext`). If in the future we provide an API for mutating assets in
466
// `LoadedAsset`, `ErasedLoadedAsset`, or `LoadContext` (for mutating existing subassets),
467
// we should move this to some point after those mutations are not possible. This spot is
468
// convenient because we still have access to the static type of `A`.
469
value.visit_dependencies(&mut |asset_id| {
470
let (type_id, index) = match asset_id {
471
UntypedAssetId::Index { type_id, index } => (type_id, index),
472
// UUID assets can't be loaded anyway, so just ignore this ID.
473
UntypedAssetId::Uuid { .. } => return,
474
};
475
self.dependencies
476
.insert(ErasedAssetIndex { index, type_id });
477
});
478
LoadedAsset {
479
value,
480
dependencies: self.dependencies,
481
loader_dependencies: self.loader_dependencies,
482
labeled_assets: self.labeled_assets,
483
}
484
}
485
486
/// Gets the source asset path for this load context.
487
pub fn path(&self) -> &AssetPath<'static> {
488
&self.asset_path
489
}
490
491
/// Reads the asset at the given path and returns its bytes
492
pub async fn read_asset_bytes<'b, 'c>(
493
&'b mut self,
494
path: impl Into<AssetPath<'c>>,
495
) -> Result<Vec<u8>, ReadAssetBytesError> {
496
let path = path.into();
497
let source = self.asset_server.get_source(path.source())?;
498
let asset_reader = match self.asset_server.mode() {
499
AssetServerMode::Unprocessed => source.reader(),
500
AssetServerMode::Processed => source.processed_reader()?,
501
};
502
let mut reader = asset_reader.read(path.path()).await?;
503
let hash = if self.populate_hashes {
504
// NOTE: ensure meta is read while the asset bytes reader is still active to ensure transactionality
505
// See `ProcessorGatedReader` for more info
506
let meta_bytes = asset_reader.read_meta_bytes(path.path()).await?;
507
let minimal: ProcessedInfoMinimal = ron::de::from_bytes(&meta_bytes)
508
.map_err(DeserializeMetaError::DeserializeMinimal)?;
509
let processed_info = minimal
510
.processed_info
511
.ok_or(ReadAssetBytesError::MissingAssetHash)?;
512
processed_info.full_hash
513
} else {
514
Default::default()
515
};
516
let mut bytes = Vec::new();
517
reader
518
.read_to_end(&mut bytes)
519
.await
520
.map_err(|source| ReadAssetBytesError::Io {
521
path: path.path().to_path_buf(),
522
source,
523
})?;
524
self.loader_dependencies.insert(path.clone_owned(), hash);
525
Ok(bytes)
526
}
527
528
/// Returns a handle to an asset of type `A` with the label `label`. This [`LoadContext`] must produce an asset of the
529
/// given type and the given label or the dependencies of this asset will never be considered "fully loaded". However you
530
/// can call this method before _or_ after adding the labeled asset.
531
pub fn get_label_handle<'b, A: Asset>(
532
&mut self,
533
label: impl Into<CowArc<'b, str>>,
534
) -> Handle<A> {
535
let path = self.asset_path.clone().with_label(label);
536
let handle = self.asset_server.get_or_create_path_handle::<A>(path, None);
537
// `get_or_create_path_handle` always returns a Strong variant, so we are safe to unwrap.
538
let index = (&handle).try_into().unwrap();
539
self.dependencies.insert(index);
540
handle
541
}
542
543
pub(crate) async fn load_direct_internal(
544
&mut self,
545
path: AssetPath<'static>,
546
settings: &dyn Settings,
547
loader: &dyn ErasedAssetLoader,
548
reader: &mut dyn Reader,
549
processed_info: Option<&ProcessedInfo>,
550
) -> Result<ErasedLoadedAsset, LoadDirectError> {
551
let loaded_asset = self
552
.asset_server
553
.load_with_settings_loader_and_reader(
554
&path,
555
settings,
556
loader,
557
reader,
558
self.should_load_dependencies,
559
self.populate_hashes,
560
)
561
.await
562
.map_err(|error| LoadDirectError::LoadError {
563
dependency: path.clone(),
564
error,
565
})?;
566
let hash = processed_info.map(|i| i.full_hash).unwrap_or_default();
567
self.loader_dependencies.insert(path, hash);
568
Ok(loaded_asset)
569
}
570
571
/// Create a builder for loading nested assets in this context.
572
#[must_use]
573
pub fn loader(&mut self) -> NestedLoader<'a, '_, StaticTyped, Deferred> {
574
NestedLoader::new(self)
575
}
576
577
/// Retrieves a handle for the asset at the given path and adds that path as a dependency of the asset.
578
/// If the current context is a normal [`AssetServer::load`], an actual asset load will be kicked off immediately, which ensures the load happens
579
/// as soon as possible.
580
/// "Normal loads" kicked from within a normal Bevy App will generally configure the context to kick off loads immediately.
581
/// If the current context is configured to not load dependencies automatically (ex: [`AssetProcessor`](crate::processor::AssetProcessor)),
582
/// a load will not be kicked off automatically. It is then the calling context's responsibility to begin a load if necessary.
583
///
584
/// If you need to override asset settings, asset type, or load directly, please see [`LoadContext::loader`].
585
pub fn load<'b, A: Asset>(&mut self, path: impl Into<AssetPath<'b>>) -> Handle<A> {
586
self.loader().load(path)
587
}
588
}
589
590
/// An error produced when calling [`LoadContext::read_asset_bytes`]
591
#[derive(Error, Debug)]
592
pub enum ReadAssetBytesError {
593
#[error(transparent)]
594
DeserializeMetaError(#[from] DeserializeMetaError),
595
#[error(transparent)]
596
AssetReaderError(#[from] AssetReaderError),
597
#[error(transparent)]
598
MissingAssetSourceError(#[from] MissingAssetSourceError),
599
#[error(transparent)]
600
MissingProcessedAssetReaderError(#[from] MissingProcessedAssetReaderError),
601
/// Encountered an I/O error while loading an asset.
602
#[error("Encountered an io error while loading asset at `{}`: {source}", path.display())]
603
Io {
604
path: PathBuf,
605
source: std::io::Error,
606
},
607
#[error("The LoadContext for this read_asset_bytes call requires hash metadata, but it was not provided. This is likely an internal implementation error.")]
608
MissingAssetHash,
609
}
610
611