- Add `load_resource_with` extension function which inserts a loadable resource by value without requiring implementing `FromWorld` trait. - Add singleton-like `ManifestResource` which holds a `ManifestAsset` of a given asset type. Once loaded, the resource is added, just like `load_resource` did previously. - Add a `Manifest` system param with a helper lookup function to make looking up entries easier as you'd otherwise need 3 system params. - Add a `InitManifest` extension trait for easy setup - Add a `blocks.manifest.ron` file that references all blocks - Implement `ManifestEntry` on `BlockDefinition` and `BlockVisuals` - Remove `BlockDefinitionCollection` and `BlockVisualsCollection`main
parent
7e0dfd8fdd
commit
acf2667944
9 changed files with 194 additions and 100 deletions
@ -0,0 +1,4 @@ |
||||
[ |
||||
"default", |
||||
"platform", |
||||
] |
||||
@ -0,0 +1,109 @@ |
||||
use std::marker::PhantomData; |
||||
|
||||
use bevy::prelude::*; |
||||
|
||||
use bevy::asset::io::Reader; |
||||
use bevy::asset::{AssetLoader, AssetPath, LoadContext}; |
||||
use bevy::ecs::system::SystemParam; |
||||
use derive_where::derive_where; |
||||
|
||||
use crate::assets::asset_tracking::LoadResource; |
||||
use crate::identifier::{Identifier, IdentifierMap}; |
||||
|
||||
/// Trait implemented by [`Asset`]s which can appear in a [`Manifest`].
|
||||
pub trait ManifestEntry: Asset { |
||||
type Marker: TypePath; |
||||
} |
||||
|
||||
#[derive(Resource, Asset, Reflect)] |
||||
#[derive_where(Clone)] |
||||
struct ManifestResource<T: ManifestEntry> { |
||||
#[dependency] |
||||
handle: Handle<ManifestAsset<T>>, |
||||
} |
||||
|
||||
#[derive(Asset, TypePath)] |
||||
struct ManifestAsset<T: ManifestEntry> { |
||||
#[dependency] |
||||
map: IdentifierMap<T::Marker, Handle<T>>, |
||||
} |
||||
|
||||
impl<T: ManifestEntry> ManifestAsset<T> { |
||||
pub fn get(&self, id: &Identifier<T::Marker>) -> Option<&Handle<T>> { |
||||
self.map.get(id) |
||||
} |
||||
} |
||||
|
||||
#[derive_where(Default)] |
||||
struct ManifestAssetLoader<T: ManifestEntry> { |
||||
entry_type: PhantomData<T>, |
||||
} |
||||
|
||||
impl<T: ManifestEntry> AssetLoader for ManifestAssetLoader<T> { |
||||
type Asset = ManifestAsset<T>; |
||||
type Settings = (); |
||||
type Error = BevyError; |
||||
|
||||
async fn load( |
||||
&self, |
||||
reader: &mut dyn Reader, |
||||
_settings: &Self::Settings, |
||||
load_context: &mut LoadContext<'_>, |
||||
) -> Result<Self::Asset, Self::Error> { |
||||
let mut bytes = Vec::new(); |
||||
reader.read_to_end(&mut bytes).await?; |
||||
let raw = ron::de::from_bytes::<Vec<String>>(&bytes)?; |
||||
|
||||
let mut map = IdentifierMap::default(); |
||||
for id in raw { |
||||
let id = Identifier::<T::Marker>::new(id)?; |
||||
let handle = load_context.load(format!("blocks/{id}.ron")); |
||||
map.try_insert(id, handle).map_err(|_| "duplicate entry")?; |
||||
} |
||||
Ok(ManifestAsset { map }) |
||||
} |
||||
|
||||
fn extensions(&self) -> &[&str] { |
||||
&["manifest.ron"] |
||||
} |
||||
} |
||||
|
||||
#[derive(SystemParam)] |
||||
pub struct Manifest<'w, T: ManifestEntry> { |
||||
resource: Option<Res<'w, ManifestResource<T>>>, |
||||
manifest_assets: Res<'w, Assets<ManifestAsset<T>>>, |
||||
entry_assets: Res<'w, Assets<T>>, |
||||
} |
||||
|
||||
impl<T: ManifestEntry> Manifest<'_, T> { |
||||
/// Looks up an [`Asset`] of type `T` from its associated manifest.
|
||||
///
|
||||
/// Returns `None` if an asset with the given identifier is missing from
|
||||
/// the manifest, or if the manifest is not loaded, including if it has
|
||||
/// not been initialized with [`InitManifest::init_manifest`].
|
||||
pub fn get(&self, id: &Identifier<T::Marker>) -> Option<&T> { |
||||
let manifest_handle = &self.resource.as_ref()?.handle; |
||||
// SAFETY: Manifest loads this as a dependency, so it should exist.
|
||||
let manifest = self.manifest_assets.get(manifest_handle).unwrap(); |
||||
let entry_handle = manifest.get(id)?; |
||||
// SAFETY: Entry loads this as a dependency, so it should exist.
|
||||
let entry = self.entry_assets.get(entry_handle).unwrap(); |
||||
Some(entry) |
||||
} |
||||
} |
||||
|
||||
pub trait InitManifest { |
||||
fn init_manifest<'a, T: ManifestEntry>(&mut self, path: impl Into<AssetPath<'a>>); |
||||
} |
||||
|
||||
impl InitManifest for App { |
||||
fn init_manifest<'a, T: ManifestEntry>(&mut self, path: impl Into<AssetPath<'a>>) { |
||||
self.init_asset::<T>(); |
||||
self.init_asset::<ManifestAsset<T>>(); |
||||
self.init_asset_loader::<ManifestAssetLoader<T>>(); |
||||
|
||||
// Insert a `Resource` that holds a handle to the `ManifestAsset` once it's loaded.
|
||||
let handle = self.world_mut().load_asset(path); |
||||
self.load_resource_with(ManifestResource::<T> { handle }); |
||||
} |
||||
} |
||||
Loading…
Reference in new issue