Skip to main content

furmint_resources/
assets.rs

1use crate::loader::{Asset, ErasedAssetLoader};
2use crate::{ResourceError, ResourceResult};
3use log::debug;
4use specs::Component;
5use specs::VecStorage;
6use std::any::{Any, TypeId};
7use std::collections::HashMap;
8use std::fs::File;
9use std::io::Read;
10use std::marker::PhantomData;
11use std::ops::Deref;
12use std::path::{Path, PathBuf};
13use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
14
15/// Asset server struct
16#[derive(Debug, Default)]
17pub struct AssetServer {
18    root: Option<PathBuf>,
19    /// loaders for assets, asset  typeid -> loader
20    loaders: HashMap<TypeId, Box<dyn ErasedAssetLoader>>,
21    /// caches for assets, asset  typeid -> cache (which is an `Any`, but internally downcasted to a hashmap of assets of that type)
22    caches: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
23    /// metadat for assets, asset typeid -> metadata
24    metas: HashMap<UntypedAssetId, Metadata>,
25}
26
27/// Resource read-only handle
28#[derive(Clone, Component, Debug)]
29#[storage(VecStorage)]
30pub struct Handle<A: Asset + ?Sized> {
31    id: AssetId<A>,
32    inner: Arc<RwLock<A>>,
33}
34
35/// Asset ID
36#[derive(Debug, PartialEq, Eq, Hash)]
37pub struct AssetId<A: Asset + ?Sized> {
38    // honey look!!!! somebody's def gonna insert fucking u32::MAX assets and cause the funny thing
39    raw: u32,
40    generation: u32,
41    _marker: PhantomData<fn() -> A>,
42}
43
44impl<A: Asset + ?Sized> Copy for AssetId<A> {}
45
46impl<A: Asset + ?Sized> Clone for AssetId<A> {
47    fn clone(&self) -> Self {
48        *self
49    }
50}
51
52impl<A: Asset + ?Sized> AssetId<A> {
53    pub(crate) fn new(raw: u32, generation: u32) -> Self {
54        Self {
55            raw,
56            generation,
57            _marker: PhantomData,
58        }
59    }
60
61    pub(crate) fn index(&self) -> usize {
62        self.raw as usize
63    }
64
65    #[allow(unused)]
66    pub(crate) fn raw(&self) -> u32 {
67        self.raw
68    }
69
70    pub(crate) fn generation(&self) -> u32 {
71        self.generation
72    }
73}
74
75impl<A: Asset> AssetId<A> {
76    pub(crate) fn untyped(self) -> UntypedAssetId {
77        UntypedAssetId {
78            type_id: TypeId::of::<A>(),
79            raw: self.raw,
80            generation: self.generation,
81        }
82    }
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
86pub(crate) struct UntypedAssetId {
87    type_id: TypeId,
88    raw: u32,
89    generation: u32,
90}
91
92#[derive(Debug, Clone)]
93pub(crate) struct Metadata {
94    source: AssetSource,
95    _type_id: TypeId,
96}
97
98#[derive(Debug, Clone)]
99#[allow(unused)]
100pub(crate) enum AssetSource {
101    File {
102        path: PathBuf,
103        modified: std::time::SystemTime,
104    },
105    Reader,
106    Internal,
107}
108
109#[derive(Debug)]
110struct AssetSlot<A: Asset> {
111    generation: u32,
112    asset: Option<Arc<RwLock<A>>>,
113}
114
115#[derive(Debug)]
116struct AssetCache<A: Asset> {
117    assets: Vec<AssetSlot<A>>,
118    names: HashMap<String, AssetId<A>>,
119}
120
121impl<A: Asset> AssetCache<A> {
122    fn new() -> Self {
123        Self {
124            assets: Vec::new(),
125            names: HashMap::new(),
126        }
127    }
128
129    fn insert(&mut self, asset: A) -> Handle<A> {
130        let raw = self.assets.len() as u32;
131        let generation = 0;
132        let id = AssetId::new(raw, generation);
133
134        let handle = Handle::new(id, asset);
135
136        self.assets.push(AssetSlot {
137            generation,
138            asset: Some(handle.inner()),
139        });
140
141        handle
142    }
143
144    fn insert_named(&mut self, name: String, asset: A) -> Handle<A> {
145        let handle = self.insert(asset);
146        self.names.insert(name, handle.id());
147        handle
148    }
149
150    fn get(&self, id: AssetId<A>) -> Option<Handle<A>> {
151        let slot = self.assets.get(id.index())?;
152
153        if slot.generation != id.generation() {
154            return None;
155        }
156
157        let inner = slot.asset.as_ref()?.clone();
158
159        Some(Handle::from_inner(id, inner))
160    }
161
162    fn get_named(&self, name: &str) -> Option<Handle<A>> {
163        let id = self.names.get(name)?;
164        self.get(*id)
165    }
166
167    fn id_named(&self, name: &str) -> Option<AssetId<A>> {
168        self.names.get(name).copied()
169    }
170
171    fn reload(&mut self, id: AssetId<A>, new_asset: A) -> ResourceResult<()> {
172        let slot = self
173            .assets
174            .get_mut(id.index())
175            .ok_or(ResourceError::ResourceDoesNotExist)?;
176
177        if slot.generation != id.generation() {
178            return Err(ResourceError::ResourceDoesNotExist);
179        }
180
181        let existing = slot
182            .asset
183            .as_ref()
184            .ok_or(ResourceError::ResourceDoesNotExist)?;
185
186        *existing.write().expect("asset lock poisoned") = new_asset;
187
188        Ok(())
189    }
190}
191
192impl<A: Asset> Handle<A> {
193    /// Get [`AssetId`] of this handle
194    pub fn id(&self) -> AssetId<A> {
195        self.id
196    }
197
198    pub(crate) fn new(id: AssetId<A>, asset: A) -> Self {
199        Self {
200            id,
201            inner: Arc::new(RwLock::new(asset)),
202        }
203    }
204
205    pub(crate) fn from_inner(id: AssetId<A>, inner: Arc<RwLock<A>>) -> Self {
206        Self { id, inner }
207    }
208
209    pub(crate) fn inner(&self) -> Arc<RwLock<A>> {
210        self.inner.clone()
211    }
212
213    /// Get a reading lock from the asset
214    pub fn read(&self) -> AssetRead<'_, A> {
215        AssetRead {
216            guard: self.inner.read().expect("asset lock poisoned"),
217        }
218    }
219
220    /// Get a writing lock into the asset
221    pub fn write(&self) -> AssetWrite<'_, A> {
222        AssetWrite {
223            guard: self.inner.write().expect("asset lock poisoned"),
224        }
225    }
226}
227
228impl AssetServer {
229    /// Create a new instance of [`AssetServer`]
230    pub fn new() -> Self {
231        Self::default()
232    }
233
234    /// Create an [`AssetServer`] that can load assets from the filesystem
235    pub fn with_root(root: impl Into<PathBuf>) -> Self {
236        Self {
237            root: Some(root.into()),
238            ..Self::default()
239        }
240    }
241
242    /// Register an asset type without registering a loader
243    ///
244    /// Useful for internal/generated assets
245    pub fn register_asset_type<A: Asset>(&mut self) {
246        self.caches
247            .entry(TypeId::of::<A>())
248            .or_insert_with(|| Box::new(AssetCache::<A>::new()));
249    }
250
251    /// Register an asset loader
252    pub fn register_loader<A: Asset>(&mut self, loader: Box<dyn ErasedAssetLoader>) {
253        self.register_asset_type::<A>();
254        self.loaders.insert(TypeId::of::<A>(), loader);
255    }
256
257    /// Load asset into cache from a reader
258    ///
259    /// # Warning
260    /// Assets loaded with this function won't be hot-reloadable
261    pub fn load_reader<A: Asset>(
262        &mut self,
263        name: &str,
264        reader: &mut dyn Read,
265    ) -> ResourceResult<Handle<A>> {
266        let asset = self.load_asset_from_reader::<A>(reader)?;
267
268        let handle = self
269            .cache_mut::<A>()
270            .expect("asset cache was not registered")
271            .insert_named(name.to_string(), asset);
272
273        self.metas.insert(
274            handle.id().untyped(),
275            Metadata {
276                source: AssetSource::Reader,
277                _type_id: TypeId::of::<A>(),
278            },
279        );
280
281        Ok(handle)
282    }
283
284    /// Insert an internal/generated asset into the cache
285    pub fn insert<A: Asset>(&mut self, asset: A) -> ResourceResult<Handle<A>> {
286        let handle = self
287            .cache_mut::<A>()
288            .expect("asset cache was not registered")
289            .insert(asset);
290
291        self.metas.insert(
292            handle.id().untyped(),
293            Metadata {
294                source: AssetSource::Internal,
295                _type_id: TypeId::of::<A>(),
296            },
297        );
298
299        Ok(handle)
300    }
301
302    /// Insert an internal/generated asset with a name alias
303    pub fn insert_named<A: Asset>(
304        &mut self,
305        name: impl Into<String>,
306        asset: A,
307    ) -> ResourceResult<Handle<A>> {
308        let handle = self
309            .cache_mut::<A>()
310            .expect("asset cache was not registered")
311            .insert_named(name.into(), asset);
312
313        self.metas.insert(
314            handle.id().untyped(),
315            Metadata {
316                source: AssetSource::Internal,
317                _type_id: TypeId::of::<A>(),
318            },
319        );
320
321        Ok(handle)
322    }
323
324    /// Load asset into cache from file
325    ///
326    /// The extension will be stripped and the asset inserted under that name.
327    /// For example, `music.ogg` is inserted as `music`
328    pub fn load<A: Asset>(&mut self, path: impl Into<PathBuf>) -> ResourceResult<Handle<A>> {
329        let root = self
330            .root
331            .as_ref()
332            .ok_or(ResourceError::AssetServerUnsupported)?;
333
334        let path = path.into();
335        let name = asset_name(&path)?;
336        let full_path = root.join(&path);
337
338        debug!("loading asset from {:?}", full_path);
339
340        let modified = std::fs::metadata(&full_path)?.modified()?;
341
342        let mut reader = File::open(&full_path)?;
343        let asset = self.load_asset_from_reader::<A>(&mut reader)?;
344
345        let handle = self
346            .cache_mut::<A>()
347            .expect("asset cache was not registered")
348            .insert_named(name, asset);
349
350        self.metas.insert(
351            handle.id().untyped(),
352            Metadata {
353                source: AssetSource::File {
354                    path: full_path,
355                    modified,
356                },
357                _type_id: TypeId::of::<A>(),
358            },
359        );
360
361        Ok(handle)
362    }
363
364    /// Get asset handle from server by ID
365    pub fn get<A: Asset>(&self, id: AssetId<A>) -> Option<Handle<A>> {
366        self.cache::<A>()?.get(id)
367    }
368
369    /// Get asset handle from server by name
370    pub fn get_named<A: Asset>(&self, name: &str) -> Option<Handle<A>> {
371        self.cache::<A>()?.get_named(name)
372    }
373
374    /// Get asset ID from name
375    pub fn id_named<A: Asset>(&self, name: &str) -> Option<AssetId<A>> {
376        self.cache::<A>()?.id_named(name)
377    }
378
379    /// Hot-reload an asset by ID
380    pub fn reload<A: Asset>(&mut self, id: AssetId<A>) -> ResourceResult<()> {
381        self.root
382            .as_ref()
383            .ok_or(ResourceError::AssetServerUnsupported)?;
384
385        let source = self
386            .metas
387            .get(&id.untyped())
388            .ok_or(ResourceError::ResourceDoesNotExist)?
389            .source
390            .clone();
391
392        let AssetSource::File { path, .. } = source else {
393            return Err(ResourceError::ResourceDoesNotExist);
394        };
395
396        let mut reader = File::open(&path)?;
397        let new_asset = self.load_asset_from_reader::<A>(&mut reader)?;
398
399        self.cache_mut::<A>()
400            .expect("asset cache was not registered")
401            .reload(id, new_asset)?;
402
403        Ok(())
404    }
405
406    /// Hot-reload an asset by name
407    pub fn reload_named<A: Asset>(&mut self, name: &str) -> ResourceResult<()> {
408        let id = self
409            .id_named::<A>(name)
410            .ok_or(ResourceError::ResourceDoesNotExist)?;
411
412        self.reload(id)
413    }
414
415    fn load_asset_from_reader<A: Asset>(&self, reader: &mut dyn Read) -> ResourceResult<A> {
416        let loader = self
417            .loaders
418            .get(&TypeId::of::<A>())
419            .ok_or(ResourceError::LoaderNotFound)?;
420
421        loader
422            .load_erased(reader)?
423            .downcast::<A>()
424            .map(|asset| *asset)
425            .map_err(|_| ResourceError::LoaderReturnedWrongType)
426    }
427
428    fn cache<A: Asset>(&self) -> Option<&AssetCache<A>> {
429        self.caches
430            .get(&TypeId::of::<A>())?
431            .downcast_ref::<AssetCache<A>>()
432    }
433
434    fn cache_mut<A: Asset>(&mut self) -> Option<&mut AssetCache<A>> {
435        self.caches
436            .get_mut(&TypeId::of::<A>())?
437            .downcast_mut::<AssetCache<A>>()
438    }
439}
440
441fn asset_name(path: &Path) -> ResourceResult<String> {
442    path.with_extension("")
443        .to_str()
444        .map(ToOwned::to_owned)
445        .ok_or(ResourceError::NotFound(path.to_path_buf()))
446}
447
448/// Wrapper for reading from the asset
449pub struct AssetRead<'a, A> {
450    guard: RwLockReadGuard<'a, A>,
451}
452
453impl<A> Deref for AssetRead<'_, A> {
454    type Target = A;
455
456    fn deref(&self) -> &Self::Target {
457        &self.guard
458    }
459}
460
461/// Wrapper for writing into the asset
462pub struct AssetWrite<'a, A> {
463    guard: RwLockWriteGuard<'a, A>,
464}
465
466impl<A> Deref for AssetWrite<'_, A> {
467    type Target = A;
468
469    fn deref(&self) -> &Self::Target {
470        &self.guard
471    }
472}
473
474impl<A> std::ops::DerefMut for AssetWrite<'_, A> {
475    fn deref_mut(&mut self) -> &mut Self::Target {
476        &mut self.guard
477    }
478}