konnektoren_bevy/assets/
level_asset.rs1use bevy::{
2 asset::{io::Reader, AssetLoader, LoadContext},
3 prelude::*,
4 reflect::TypePath,
5};
6
7#[cfg(feature = "assets")]
8use konnektoren_core::game::GamePath;
9#[cfg(feature = "assets")]
10use serde_yaml;
11#[cfg(feature = "assets")]
12use thiserror::Error;
13
14#[derive(Asset, TypePath, Debug, Clone)]
16pub struct LevelAsset {
17 #[cfg(feature = "assets")]
18 pub game_path: GamePath,
19 #[cfg(not(feature = "assets"))]
20 pub game_path: String, pub file_path: String,
22}
23
24#[cfg(feature = "assets")]
25impl LevelAsset {
26 pub fn id(&self) -> &str {
28 &self.game_path.id
29 }
30
31 pub fn name(&self) -> &str {
33 &self.game_path.name
34 }
35
36 pub fn get_challenge_ids(&self) -> Vec<String> {
38 self.game_path
39 .challenges
40 .iter()
41 .map(|c| c.challenge.clone())
42 .collect()
43 }
44}
45
46#[cfg(not(feature = "assets"))]
47impl LevelAsset {
48 pub fn id(&self) -> &str {
50 &self.game_path
51 }
52
53 pub fn name(&self) -> &str {
55 &self.game_path
56 }
57
58 pub fn get_challenge_ids(&self) -> Vec<String> {
60 vec![]
61 }
62}
63
64#[derive(Default)]
66pub struct LevelAssetLoader;
67
68#[non_exhaustive]
70#[derive(Debug, Error)]
71#[cfg(feature = "assets")]
72pub enum LevelAssetLoaderError {
73 #[error("Could not load level asset: {0}")]
75 Io(#[from] std::io::Error),
76
77 #[error("Could not parse YAML level: {0}")]
79 YamlError(#[from] serde_yaml::Error),
80}
81
82#[cfg(not(feature = "assets"))]
83#[derive(Debug)]
84pub enum LevelAssetLoaderError {
85 Io(std::io::Error),
86 YamlError(String),
87}
88
89#[cfg(not(feature = "assets"))]
90impl std::fmt::Display for LevelAssetLoaderError {
91 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92 match self {
93 LevelAssetLoaderError::Io(e) => write!(f, "IO Error: {}", e),
94 LevelAssetLoaderError::YamlError(e) => write!(f, "YAML Error: {}", e),
95 }
96 }
97}
98
99#[cfg(not(feature = "assets"))]
100impl std::error::Error for LevelAssetLoaderError {}
101
102#[cfg(feature = "assets")]
103impl AssetLoader for LevelAssetLoader {
104 type Asset = LevelAsset;
105 type Settings = ();
106 type Error = LevelAssetLoaderError;
107
108 async fn load(
109 &self,
110 reader: &mut dyn Reader,
111 _settings: &(),
112 load_context: &mut LoadContext<'_>,
113 ) -> Result<Self::Asset, Self::Error> {
114 let mut bytes = Vec::new();
115 reader.read_to_end(&mut bytes).await?;
116
117 let game_path = serde_yaml::from_slice::<GamePath>(&bytes)?;
118 let file_path = load_context.path().to_string_lossy().to_string();
119
120 info!(
121 "Loaded level '{}' ({}) with {} challenges from {}",
122 game_path.name,
123 game_path.id,
124 game_path.challenges.len(),
125 file_path
126 );
127
128 Ok(LevelAsset {
129 game_path,
130 file_path,
131 })
132 }
133
134 fn extensions(&self) -> &[&str] {
135 &["level.yml", "level.yaml"]
136 }
137}
138
139#[cfg(not(feature = "assets"))]
140impl AssetLoader for LevelAssetLoader {
141 type Asset = LevelAsset;
142 type Settings = ();
143 type Error = LevelAssetLoaderError;
144
145 async fn load(
146 &self,
147 reader: &mut dyn Reader,
148 _settings: &(),
149 load_context: &mut LoadContext<'_>,
150 ) -> Result<Self::Asset, Self::Error> {
151 let mut bytes = Vec::new();
152 reader
153 .read_to_end(&mut bytes)
154 .await
155 .map_err(LevelAssetLoaderError::Io)?;
156
157 let file_path = load_context.path().to_string_lossy().to_string();
158 let game_path = "unknown".to_string(); Ok(LevelAsset {
161 game_path,
162 file_path,
163 })
164 }
165
166 fn extensions(&self) -> &[&str] {
167 &["level.yml", "level.yaml"]
168 }
169}