1use crate::{
2 self as bevy_asset, loader::AssetLoader, processor::Process, Asset, AssetPath,
3 DeserializeMetaError, VisitAssetDependencies,
4};
5use bevy_utils::tracing::error;
6use downcast_rs::{impl_downcast, Downcast};
7use ron::ser::PrettyConfig;
8use serde::{Deserialize, Serialize};
9
10pub const META_FORMAT_VERSION: &str = "1.0";
11pub type MetaTransform = Box<dyn Fn(&mut dyn AssetMetaDyn) + Send + Sync>;
12
13#[derive(Serialize, Deserialize)]
18pub struct AssetMeta<L: AssetLoader, P: Process> {
19 pub meta_format_version: String,
22 #[serde(skip_serializing_if = "Option::is_none")]
27 pub processed_info: Option<ProcessedInfo>,
28 pub asset: AssetAction<L::Settings, P::Settings>,
30}
31
32impl<L: AssetLoader, P: Process> AssetMeta<L, P> {
33 pub fn new(asset: AssetAction<L::Settings, P::Settings>) -> Self {
34 Self {
35 meta_format_version: META_FORMAT_VERSION.to_string(),
36 processed_info: None,
37 asset,
38 }
39 }
40
41 pub fn deserialize(bytes: &[u8]) -> Result<Self, DeserializeMetaError> {
43 Ok(ron::de::from_bytes(bytes)?)
44 }
45}
46
47#[derive(Serialize, Deserialize)]
49pub enum AssetAction<LoaderSettings, ProcessSettings> {
50 Load {
53 loader: String,
54 settings: LoaderSettings,
55 },
56 Process {
61 processor: String,
62 settings: ProcessSettings,
63 },
64 Ignore,
66}
67
68#[derive(Serialize, Deserialize, Default, Debug, Clone)]
73pub struct ProcessedInfo {
74 pub hash: AssetHash,
76 pub full_hash: AssetHash,
78 pub process_dependencies: Vec<ProcessDependencyInfo>,
80}
81
82#[derive(Serialize, Deserialize, Debug, Clone)]
85pub struct ProcessDependencyInfo {
86 pub full_hash: AssetHash,
87 pub path: AssetPath<'static>,
88}
89
90#[derive(Serialize, Deserialize)]
97pub struct AssetMetaMinimal {
98 pub asset: AssetActionMinimal,
99}
100
101#[derive(Serialize, Deserialize)]
104pub enum AssetActionMinimal {
105 Load { loader: String },
106 Process { processor: String },
107 Ignore,
108}
109
110#[derive(Serialize, Deserialize)]
113pub struct ProcessedInfoMinimal {
114 pub processed_info: Option<ProcessedInfo>,
115}
116
117pub trait AssetMetaDyn: Downcast + Send + Sync {
120 fn loader_settings(&self) -> Option<&dyn Settings>;
122 fn loader_settings_mut(&mut self) -> Option<&mut dyn Settings>;
124 fn serialize(&self) -> Vec<u8>;
126 fn processed_info(&self) -> &Option<ProcessedInfo>;
128 fn processed_info_mut(&mut self) -> &mut Option<ProcessedInfo>;
130}
131
132impl<L: AssetLoader, P: Process> AssetMetaDyn for AssetMeta<L, P> {
133 fn loader_settings(&self) -> Option<&dyn Settings> {
134 if let AssetAction::Load { settings, .. } = &self.asset {
135 Some(settings)
136 } else {
137 None
138 }
139 }
140 fn loader_settings_mut(&mut self) -> Option<&mut dyn Settings> {
141 if let AssetAction::Load { settings, .. } = &mut self.asset {
142 Some(settings)
143 } else {
144 None
145 }
146 }
147 fn serialize(&self) -> Vec<u8> {
148 ron::ser::to_string_pretty(&self, PrettyConfig::default())
149 .expect("type is convertible to ron")
150 .into_bytes()
151 }
152 fn processed_info(&self) -> &Option<ProcessedInfo> {
153 &self.processed_info
154 }
155 fn processed_info_mut(&mut self) -> &mut Option<ProcessedInfo> {
156 &mut self.processed_info
157 }
158}
159
160impl_downcast!(AssetMetaDyn);
161
162pub trait Settings: Downcast + Send + Sync + 'static {}
166
167impl<T: 'static> Settings for T where T: Send + Sync {}
168
169impl_downcast!(Settings);
170
171impl Process for () {
173 type Settings = ();
174 type OutputLoader = ();
175
176 async fn process(
177 &self,
178 _context: &mut bevy_asset::processor::ProcessContext<'_>,
179 _meta: AssetMeta<(), Self>,
180 _writer: &mut bevy_asset::io::Writer,
181 ) -> Result<(), bevy_asset::processor::ProcessError> {
182 unreachable!()
183 }
184}
185
186impl Asset for () {}
187
188impl VisitAssetDependencies for () {
189 fn visit_dependencies(&self, _visit: &mut impl FnMut(bevy_asset::UntypedAssetId)) {
190 unreachable!()
191 }
192}
193
194impl AssetLoader for () {
196 type Asset = ();
197 type Settings = ();
198 type Error = std::io::Error;
199 async fn load(
200 &self,
201 _reader: &mut dyn crate::io::Reader,
202 _settings: &Self::Settings,
203 _load_context: &mut crate::LoadContext<'_>,
204 ) -> Result<Self::Asset, Self::Error> {
205 unreachable!();
206 }
207
208 fn extensions(&self) -> &[&str] {
209 unreachable!();
210 }
211}
212
213pub(crate) fn meta_transform_settings<S: Settings>(
214 meta: &mut dyn AssetMetaDyn,
215 settings: &(impl Fn(&mut S) + Send + Sync + 'static),
216) {
217 if let Some(loader_settings) = meta.loader_settings_mut() {
218 if let Some(loader_settings) = loader_settings.downcast_mut::<S>() {
219 settings(loader_settings);
220 } else {
221 error!(
222 "Configured settings type {} does not match AssetLoader settings type",
223 core::any::type_name::<S>(),
224 );
225 }
226 }
227}
228
229pub(crate) fn loader_settings_meta_transform<S: Settings>(
230 settings: impl Fn(&mut S) + Send + Sync + 'static,
231) -> MetaTransform {
232 Box::new(move |meta| meta_transform_settings(meta, &settings))
233}
234
235pub type AssetHash = [u8; 32];
236
237pub(crate) fn get_asset_hash(meta_bytes: &[u8], asset_bytes: &[u8]) -> AssetHash {
239 let mut hasher = blake3::Hasher::new();
240 hasher.update(meta_bytes);
241 hasher.update(asset_bytes);
242 *hasher.finalize().as_bytes()
243}
244
245pub(crate) fn get_full_asset_hash(
247 asset_hash: AssetHash,
248 dependency_hashes: impl Iterator<Item = AssetHash>,
249) -> AssetHash {
250 let mut hasher = blake3::Hasher::new();
251 hasher.update(&asset_hash);
252 for hash in dependency_hashes {
253 hasher.update(&hash);
254 }
255 *hasher.finalize().as_bytes()
256}