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