konnektoren_bevy/assets/
mod.rs1pub mod challenge_asset;
2pub mod level_asset;
3
4use bevy::prelude::*;
5pub use challenge_asset::*;
6pub use level_asset::*;
7use std::collections::HashMap;
8
9pub struct KonnektorenAssetsPlugin;
12
13impl Plugin for KonnektorenAssetsPlugin {
14 fn build(&self, app: &mut App) {
15 app
16 .init_asset::<ChallengeAsset>()
18 .init_asset_loader::<ChallengeAssetLoader>()
19 .init_asset::<LevelAsset>()
20 .init_asset_loader::<LevelAssetLoader>()
21 .init_resource::<KonnektorenAssetRegistry>()
23 .add_systems(Update, update_asset_registry);
25 }
26}
27
28#[derive(Resource, Default, Debug)]
31pub struct KonnektorenAssetRegistry {
32 pub challenges: HashMap<String, Handle<ChallengeAsset>>,
33 pub levels: HashMap<String, Handle<LevelAsset>>,
34 pub loaded_challenges: HashMap<String, bool>,
35 pub loaded_levels: HashMap<String, bool>,
36 pub challenge_holders: HashMap<String, Handle<ChallengeAsset>>,
38 pub level_holders: HashMap<String, Handle<LevelAsset>>,
39}
40
41impl KonnektorenAssetRegistry {
42 pub fn register_challenge(&mut self, id: String, handle: Handle<ChallengeAsset>) {
44 self.challenges.insert(id.clone(), handle.clone());
46 self.challenge_holders.insert(id, handle);
47 }
48
49 pub fn register_level(&mut self, id: String, handle: Handle<LevelAsset>) {
51 self.levels.insert(id.clone(), handle.clone());
53 self.level_holders.insert(id, handle);
54 }
55
56 pub fn get_challenge_handle(&self, id: &str) -> Option<&Handle<ChallengeAsset>> {
58 self.challenges.get(id)
59 }
60
61 pub fn get_level_handle(&self, id: &str) -> Option<&Handle<LevelAsset>> {
63 self.levels.get(id)
64 }
65
66 pub fn is_challenge_loaded(&self, id: &str) -> bool {
68 self.loaded_challenges.get(id).copied().unwrap_or(false)
69 }
70
71 pub fn is_level_loaded(&self, id: &str) -> bool {
73 self.loaded_levels.get(id).copied().unwrap_or(false)
74 }
75
76 pub fn get_loaded_challenges(&self) -> Vec<String> {
78 self.loaded_challenges
79 .iter()
80 .filter_map(|(id, &loaded)| if loaded { Some(id.clone()) } else { None })
81 .collect()
82 }
83
84 pub fn get_loaded_levels(&self) -> Vec<String> {
86 self.loaded_levels
87 .iter()
88 .filter_map(|(id, &loaded)| if loaded { Some(id.clone()) } else { None })
89 .collect()
90 }
91
92 pub fn are_all_assets_loaded(&self) -> bool {
94 let all_challenges_loaded = self
95 .challenges
96 .keys()
97 .all(|id| self.is_challenge_loaded(id));
98 let all_levels_loaded = self.levels.keys().all(|id| self.is_level_loaded(id));
99
100 all_challenges_loaded && all_levels_loaded && !self.challenges.is_empty()
101 }
102
103 pub fn get_loading_progress(&self) -> f32 {
105 let total_assets = self.challenges.len() + self.levels.len();
106 if total_assets == 0 {
107 return 0.0;
108 }
109
110 let loaded_challenges = self
111 .loaded_challenges
112 .values()
113 .filter(|&&loaded| loaded)
114 .count();
115 let loaded_levels = self
116 .loaded_levels
117 .values()
118 .filter(|&&loaded| loaded)
119 .count();
120 let loaded_total = loaded_challenges + loaded_levels;
121
122 loaded_total as f32 / total_assets as f32
123 }
124}
125
126fn update_asset_registry(
128 mut registry: ResMut<KonnektorenAssetRegistry>,
129 challenge_assets: Res<Assets<ChallengeAsset>>,
130 level_assets: Res<Assets<LevelAsset>>,
131) {
132 let mut challenge_updates = Vec::new();
134 let mut level_updates = Vec::new();
135
136 for (id, handle) in ®istry.challenges {
138 let is_loaded = challenge_assets.get(handle).is_some();
139 let was_loaded = registry.loaded_challenges.get(id).copied().unwrap_or(false);
140
141 if is_loaded && !was_loaded {
142 challenge_updates.push(id.clone());
143 }
144 }
145
146 for (id, handle) in ®istry.levels {
148 let is_loaded = level_assets.get(handle).is_some();
149 let was_loaded = registry.loaded_levels.get(id).copied().unwrap_or(false);
150
151 if is_loaded && !was_loaded {
152 level_updates.push(id.clone());
153 }
154 }
155
156 for id in challenge_updates {
158 registry.loaded_challenges.insert(id.clone(), true);
159 info!("Challenge '{}' finished loading and is held in memory", id);
160 }
161
162 for id in level_updates {
163 registry.loaded_levels.insert(id.clone(), true);
164 info!("Level '{}' finished loading and is held in memory", id);
165 }
166
167 if !registry.challenges.is_empty() || !registry.levels.is_empty() {
169 let progress = registry.get_loading_progress();
170 if progress > 0.0 && progress < 1.0 {
171 debug!("Asset loading progress: {:.1}%", progress * 100.0);
172 }
173 }
174}
175
176pub trait KonnektorenAssetLoader {
178 fn load_challenge(&mut self, id: &str, path: &str) -> Handle<ChallengeAsset>;
180
181 fn load_level(&mut self, id: &str, path: &str) -> Handle<LevelAsset>;
183
184 fn load_common_assets(&mut self) -> &mut Self;
186}
187
188impl KonnektorenAssetLoader for App {
189 fn load_challenge(&mut self, id: &str, path: &str) -> Handle<ChallengeAsset> {
190 let asset_server = self.world().resource::<AssetServer>();
191 let handle = asset_server.load(path);
192
193 let mut registry = self.world_mut().resource_mut::<KonnektorenAssetRegistry>();
194 registry.register_challenge(id.to_string(), handle.clone());
195
196 info!(
197 "Registered challenge '{}' from '{}' (handle will be held)",
198 id, path
199 );
200 handle
201 }
202
203 fn load_level(&mut self, id: &str, path: &str) -> Handle<LevelAsset> {
204 let asset_server = self.world().resource::<AssetServer>();
205 let handle = asset_server.load(path);
206
207 let mut registry = self.world_mut().resource_mut::<KonnektorenAssetRegistry>();
208 registry.register_level(id.to_string(), handle.clone());
209
210 info!(
211 "Registered level '{}' from '{}' (handle will be held)",
212 id, path
213 );
214 handle
215 }
216
217 fn load_common_assets(&mut self) -> &mut Self {
218 self.load_challenge("articles", "challenges/articles.yml");
220
221 self.load_level("a1", "a1.level.yml");
223
224 self
225 }
226}
227
228#[cfg(test)]
229mod tests;