1use std::any::{Any, TypeId};
2use std::collections::HashMap;
3use std::sync::mpsc::{self, Receiver, TryRecvError};
4
5use crate::{Assets, Handle, HandleAllocator};
6
7#[derive(thiserror::Error, Debug)]
8pub enum AssetServerError {
9 #[error("{0}")]
10 Message(String),
11 #[error("asset type mismatch during async load completion")]
12 TypeMismatch,
13 #[error("asset loading thread disconnected")]
14 ChannelDisconnected,
15}
16
17struct PendingAsset {
18 type_id: TypeId,
19 receiver: Receiver<Result<Box<dyn Any + Send>, AssetServerError>>,
20}
21
22pub struct AssetServer {
23 allocator: HandleAllocator,
24 pending: HashMap<u64, PendingAsset>,
25}
26
27impl Default for AssetServer {
28 fn default() -> Self {
29 Self::new()
30 }
31}
32
33impl AssetServer {
34 pub fn new() -> Self {
35 Self {
36 allocator: HandleAllocator::new(),
37 pending: HashMap::new(),
38 }
39 }
40
41 pub fn allocate_handle<T>(&mut self) -> Handle<T> {
42 self.allocator.allocate::<T>()
43 }
44
45 pub fn load_async<T, F>(&mut self, loader: F) -> Handle<T>
46 where
47 T: Send + 'static,
48 F: FnOnce() -> Result<T, AssetServerError> + Send + 'static,
49 {
50 let handle = self.allocate_handle::<T>();
51 let id = handle.id();
52 let (sender, receiver) = mpsc::channel();
53
54 std::thread::spawn(move || {
55 let result = loader().map(|asset| Box::new(asset) as Box<dyn Any + Send>);
56 let _ = sender.send(result);
57 });
58
59 self.pending.insert(
60 id,
61 PendingAsset {
62 type_id: TypeId::of::<T>(),
63 receiver,
64 },
65 );
66
67 handle
68 }
69
70 pub fn poll_ready<T: Send + 'static>(
72 &mut self,
73 ) -> Vec<Result<(Handle<T>, T), AssetServerError>> {
74 let mut completed = Vec::new();
75 let pending_ids: Vec<u64> = self
76 .pending
77 .iter()
78 .filter_map(|(id, pending)| (pending.type_id == TypeId::of::<T>()).then_some(*id))
79 .collect();
80
81 for id in pending_ids {
82 let status = match self.pending.get(&id) {
83 Some(pending) => pending.receiver.try_recv(),
84 None => continue,
85 };
86
87 match status {
88 Ok(result) => {
89 let _ = self.pending.remove(&id);
90 match result {
91 Ok(boxed_asset) => match boxed_asset.downcast::<T>() {
92 Ok(asset) => {
93 let handle = Handle::new(id);
94 completed.push(Ok((handle, *asset)));
95 }
96 Err(_) => completed.push(Err(AssetServerError::TypeMismatch)),
97 },
98 Err(err) => completed.push(Err(err)),
99 }
100 }
101 Err(TryRecvError::Empty) => {}
102 Err(TryRecvError::Disconnected) => {
103 let _ = self.pending.remove(&id);
104 completed.push(Err(AssetServerError::ChannelDisconnected));
105 }
106 }
107 }
108
109 completed
110 }
111
112 pub fn poll_loaded<T: Send + 'static>(
113 &mut self,
114 assets: &mut Assets<T>,
115 ) -> Vec<Result<Handle<T>, AssetServerError>> {
116 let mut completed_handles = Vec::new();
117 for result in self.poll_ready::<T>() {
118 match result {
119 Ok((handle, asset)) => {
120 assets.insert(handle, asset);
121 completed_handles.push(Ok(handle));
122 }
123 Err(err) => completed_handles.push(Err(err)),
124 }
125 }
126 completed_handles
127 }
128
129 pub fn is_loading<T: 'static>(&self, handle: &Handle<T>) -> bool {
130 self.pending.contains_key(&handle.id())
131 }
132
133 pub fn pending_count(&self) -> usize {
134 self.pending.len()
135 }
136}