minecraft_assets/api/asset_pack.rs
1use std::{ops::Deref, path::Path};
2
3use serde::de::DeserializeOwned;
4
5use crate::{
6 api::{
7 FileSystemResourceProvider, ModelIdentifier, ResourceLocation, ResourceProvider, Result,
8 },
9 schemas::{BlockStates, Model},
10};
11
12/// Top-level API for accessing Minecraft assets.
13pub struct AssetPack {
14 provider: Box<dyn ResourceProvider>,
15}
16
17impl AssetPack {
18 /// Returns a new [`AssetPack`] that can read data from the given directory.
19 ///
20 /// The provided `root_dir` should be the directory that contains the
21 /// `assets/` and/or `data/` directories.
22 ///
23 /// # Example
24 ///
25 /// ```no_run
26 /// use minecraft_assets::api::AssetPack;
27 ///
28 /// let assets = AssetPack::at_path("~/.minecraft/");
29 ///
30 /// // Load the block states for `oak_planks`
31 /// let states = assets.load_blockstates("oak_planks").unwrap();
32 /// let variants = states.variants().unwrap();
33 ///
34 /// assert_eq!(variants.len(), 1);
35 ///
36 /// let model_properties = &variants[""].models()[0];
37 /// assert_eq!(model_properties.model, "block/oak_planks");
38 /// ```
39 pub fn at_path(root_dir: impl AsRef<Path>) -> Self {
40 let provider = FileSystemResourceProvider::new(root_dir);
41 Self {
42 provider: Box::new(provider),
43 }
44 }
45
46 /// Returns a new [`AssetPack`] that uses the given [`ResourceProvider`].
47 pub fn new<P>(provider: P) -> Self
48 where
49 P: ResourceProvider + 'static,
50 {
51 Self {
52 provider: Box::new(provider),
53 }
54 }
55
56 /// Loads the [`BlockStates`] of the block with the provided id.
57 ///
58 /// # Example
59 ///
60 /// ```no_run
61 /// # use minecraft_assets::api::*;
62 /// # let assets = AssetPack::at_path("foo");
63 /// let states = assets.load_blockstates("stone");
64 /// let states = assets.load_blockstates("minecraft:dirt");
65 /// ```
66 pub fn load_blockstates(&self, block_id: &str) -> Result<BlockStates> {
67 self.load_resource(&ResourceLocation::blockstates(block_id))
68 }
69
70 /// Loads the block [`Model`] identified by the given name or path.
71 ///
72 /// # Example
73 ///
74 /// ```no_run
75 /// # use minecraft_assets::api::*;
76 /// # let assets = AssetPack::at_path("foo");
77 /// let model = assets.load_block_model("stone");
78 /// let model = assets.load_block_model("block/dirt");
79 /// ```
80 pub fn load_block_model(&self, model: &str) -> Result<Model> {
81 self.load_resource(&ResourceLocation::block_model(model))
82 }
83
84 /// Loads the block [`Model`] identified by the given name or path, as well
85 /// as all of its parents and ancestors.
86 ///
87 /// The models are returned as a list, with the first element being the
88 /// model that was originally requested, the next element being its parent,
89 /// and so on with the last element being the topmost parent.
90 ///
91 /// # Example
92 ///
93 /// ```no_run
94 /// # use minecraft_assets::api::*;
95 /// # let assets = AssetPack::at_path("foo");
96 /// let models = assets.load_block_model_recursive("block/cube_all").unwrap();
97 ///
98 /// let expected = vec![
99 /// assets.load_block_model("block/cube_all").unwrap(),
100 /// assets.load_block_model("block/cube").unwrap(),
101 /// assets.load_block_model("block/block").unwrap(),
102 /// ];
103 /// assert_eq!(models, expected);
104 /// ```
105 pub fn load_block_model_recursive(&self, model: &str) -> Result<Vec<Model>> {
106 self.load_model_recursive(&ResourceLocation::block_model(model))
107 }
108
109 /// Loads the item [`Model`] identified by the given name or path.
110 ///
111 /// # Example
112 ///
113 /// ```no_run
114 /// # use minecraft_assets::api::*;
115 /// # let assets = AssetPack::at_path("foo");
116 /// let model = assets.load_item_model("compass");
117 /// let model = assets.load_item_model("item/diamond_hoe");
118 /// ```
119 pub fn load_item_model(&self, model: &str) -> Result<Model> {
120 self.load_resource(&ResourceLocation::item_model(model))
121 }
122
123 /// Loads the item [`Model`] identified by the given name or path, as well
124 /// as all of its parents and ancestors.
125 ///
126 /// The models are returned as a list, with the first element being the
127 /// model that was originally requested, the next element being its parent,
128 /// and so on with the last element being the topmost parent.
129 ///
130 /// # Example
131 ///
132 /// ```no_run
133 /// # use minecraft_assets::api::*;
134 /// # let assets = AssetPack::at_path("foo");
135 /// let models = assets.load_item_model_recursive("item/diamond_hoe").unwrap();
136 ///
137 /// let expected = vec![
138 /// assets.load_item_model("item/diamond_hoe").unwrap(),
139 /// assets.load_item_model("item/handheld").unwrap(),
140 /// assets.load_item_model("item/generated").unwrap(),
141 /// ];
142 /// assert_eq!(models, expected);
143 /// ```
144 pub fn load_item_model_recursive(&self, model: &str) -> Result<Vec<Model>> {
145 self.load_model_recursive(&ResourceLocation::item_model(model))
146 }
147
148 fn load_resource<T>(&self, resource: &ResourceLocation) -> Result<T>
149 where
150 T: DeserializeOwned,
151 {
152 let bytes = self.provider.load_resource(resource)?;
153 Ok(serde_json::from_reader(&bytes[..])?)
154 }
155
156 fn load_model_recursive(&self, resource: &ResourceLocation) -> Result<Vec<Model>> {
157 let mut models = Vec::new();
158
159 Self::for_each_parent(
160 resource.clone(),
161 |model| models.push(model),
162 |next_location| self.load_resource(next_location),
163 )?;
164
165 Ok(models)
166 }
167
168 pub(crate) fn for_each_parent<F, L, E>(
169 mut current: ResourceLocation,
170 mut op: F,
171 mut load_model: L,
172 ) -> Result<(), E>
173 where
174 F: FnMut(Model),
175 L: FnMut(&ResourceLocation) -> Result<Model, E>,
176 {
177 loop {
178 let model = load_model(¤t)?;
179
180 let parent_owned = model.parent.clone();
181
182 op(model);
183
184 match parent_owned {
185 Some(parent) if !ModelIdentifier::is_builtin(&parent) => {
186 //println!("{}", parent.as_str());
187 current = ResourceLocation::new_owned(current.kind, parent);
188 }
189 _ => break,
190 }
191 }
192
193 Ok(())
194 }
195}
196
197impl Deref for AssetPack {
198 type Target = dyn ResourceProvider;
199
200 fn deref(&self) -> &Self::Target {
201 &*self.provider
202 }
203}