- 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