1use crate::data_source;
2use crate::error::McDataError;
3use crate::features;
4use crate::indexer;
5use crate::loader;
6use crate::structs::*;
7use crate::version::Version;
8use serde_json::Value;
9use std::collections::HashMap;
10use std::sync::Arc;
11
12#[derive(Debug, Clone)]
17pub struct IndexedData {
18 pub version: Version,
20
21 pub blocks_array: Arc<Vec<Block>>,
25 pub blocks_by_id: Arc<HashMap<u32, Block>>,
26 pub blocks_by_name: Arc<HashMap<String, Block>>,
27 pub blocks_by_state_id: Arc<HashMap<u32, Block>>,
28
29 pub items_array: Arc<Vec<Item>>,
31 pub items_by_id: Arc<HashMap<u32, Item>>,
32 pub items_by_name: Arc<HashMap<String, Item>>,
33
34 pub biomes_array: Arc<Vec<Biome>>,
36 pub biomes_by_id: Arc<HashMap<u32, Biome>>,
37 pub biomes_by_name: Arc<HashMap<String, Biome>>,
38
39 pub effects_array: Arc<Vec<Effect>>,
41 pub effects_by_id: Arc<HashMap<u32, Effect>>,
42 pub effects_by_name: Arc<HashMap<String, Effect>>,
43
44 pub entities_array: Arc<Vec<Entity>>,
46 pub entities_by_id: Arc<HashMap<u32, Entity>>,
47 pub entities_by_name: Arc<HashMap<String, Entity>>,
48 pub mobs_by_id: Arc<HashMap<u32, Entity>>, pub objects_by_id: Arc<HashMap<u32, Entity>>, pub sounds_array: Arc<Vec<Sound>>,
53 pub sounds_by_id: Arc<HashMap<u32, Sound>>,
54 pub sounds_by_name: Arc<HashMap<String, Sound>>,
55
56 pub particles_array: Arc<Vec<Particle>>,
58 pub particles_by_id: Arc<HashMap<u32, Particle>>,
59 pub particles_by_name: Arc<HashMap<String, Particle>>,
60
61 pub attributes_array: Arc<Vec<Attribute>>,
63 pub attributes_by_name: Arc<HashMap<String, Attribute>>,
64 pub attributes_by_resource: Arc<HashMap<String, Attribute>>, pub instruments_array: Arc<Vec<Instrument>>,
68 pub instruments_by_id: Arc<HashMap<u32, Instrument>>,
69 pub instruments_by_name: Arc<HashMap<String, Instrument>>,
70
71 pub foods_array: Arc<Vec<Food>>,
73 pub foods_by_id: Arc<HashMap<u32, Food>>,
74 pub foods_by_name: Arc<HashMap<String, Food>>,
75
76 pub enchantments_array: Arc<Vec<Enchantment>>,
78 pub enchantments_by_id: Arc<HashMap<u32, Enchantment>>,
79 pub enchantments_by_name: Arc<HashMap<String, Enchantment>>,
80
81 pub map_icons_array: Arc<Vec<MapIcon>>,
83 pub map_icons_by_id: Arc<HashMap<u32, MapIcon>>,
84 pub map_icons_by_name: Arc<HashMap<String, MapIcon>>,
85
86 pub windows_array: Arc<Vec<Window>>,
88 pub windows_by_id: Arc<HashMap<String, Window>>, pub windows_by_name: Arc<HashMap<String, Window>>,
90
91 pub block_loot_array: Arc<Vec<BlockLoot>>,
93 pub block_loot_by_name: Arc<HashMap<String, BlockLoot>>, pub entity_loot_array: Arc<Vec<EntityLoot>>,
97 pub entity_loot_by_name: Arc<HashMap<String, EntityLoot>>, pub block_shapes_by_state_id: Arc<HashMap<u32, Vec<[f64; 6]>>>, pub block_shapes_by_name: Arc<HashMap<String, Vec<[f64; 6]>>>, pub block_collision_shapes_raw: Arc<Option<BlockCollisionShapes>>,
106 pub tints: Arc<Option<Tints>>,
108 pub language: Arc<HashMap<String, String>>,
110 pub legacy: Arc<Option<Legacy>>,
112
113 pub recipes: Arc<Option<Value>>,
116 pub materials: Arc<Option<Value>>,
117 pub commands: Arc<Option<Value>>,
118 pub protocol: Arc<Option<Value>>, pub protocol_comments: Arc<Option<Value>>, pub login_packet: Arc<Option<Value>>, }
122
123impl IndexedData {
124 pub fn load(version: Version) -> Result<Self, McDataError> {
127 log::info!(
128 "Loading and indexing data for version: {} ({:?})",
129 version.minecraft_version,
130 version.edition
131 );
132 let major_version_str = &version.major_version; let edition = version.edition;
134
135 macro_rules! load_optional {
139 ($key:expr, $type:ty) => {
140 match loader::load_data::<$type>(edition, major_version_str, $key) {
141 Ok(data) => {
142 log::trace!("Successfully loaded optional data for key '{}'", $key);
143 Some(data)
144 }
145 Err(McDataError::DataPathNotFound { .. })
147 | Err(McDataError::DataFileNotFound { .. }) => {
148 log::trace!("Optional data key '{}' not found for this version.", $key);
149 None
150 }
151 Err(e) => {
153 log::error!("Error loading optional data for key '{}': {}", $key, e);
154 return Err(e);
155 }
156 }
157 };
158 }
159 macro_rules! load_optional_value {
161 ($key:expr) => {
162 match loader::load_data::<Value>(edition, major_version_str, $key) {
163 Ok(data) => {
164 log::trace!("Successfully loaded optional value for key '{}'", $key);
165 Some(data)
166 }
167 Err(McDataError::DataPathNotFound { .. })
168 | Err(McDataError::DataFileNotFound { .. }) => {
169 log::trace!("Optional value key '{}' not found for this version.", $key);
170 None
171 }
172 Err(e) => {
173 log::error!("Error loading optional value for key '{}': {}", $key, e);
174 return Err(e);
175 }
176 }
177 };
178 }
179
180 let blocks: Vec<Block> = loader::load_data(edition, major_version_str, "blocks")?;
183 let items: Vec<Item> = loader::load_data(edition, major_version_str, "items")?;
184
185 let biomes: Vec<Biome> = load_optional!("biomes", Vec<Biome>).unwrap_or_default();
187 let effects: Vec<Effect> = load_optional!("effects", Vec<Effect>).unwrap_or_default();
188 let entities: Vec<Entity> = load_optional!("entities", Vec<Entity>).unwrap_or_default();
189 let sounds: Vec<Sound> = load_optional!("sounds", Vec<Sound>).unwrap_or_default();
190 let particles: Vec<Particle> =
191 load_optional!("particles", Vec<Particle>).unwrap_or_default();
192 let attributes: Vec<Attribute> =
193 load_optional!("attributes", Vec<Attribute>).unwrap_or_default();
194 let instruments: Vec<Instrument> =
195 load_optional!("instruments", Vec<Instrument>).unwrap_or_default();
196 let foods: Vec<Food> = load_optional!("foods", Vec<Food>).unwrap_or_default();
197 let enchantments: Vec<Enchantment> =
198 load_optional!("enchantments", Vec<Enchantment>).unwrap_or_default();
199 let map_icons: Vec<MapIcon> = load_optional!("mapIcons", Vec<MapIcon>).unwrap_or_default();
200 let windows: Vec<Window> = load_optional!("windows", Vec<Window>).unwrap_or_default();
201 let block_loot: Vec<BlockLoot> =
202 load_optional!("blockLoot", Vec<BlockLoot>).unwrap_or_default();
203 let entity_loot: Vec<EntityLoot> =
204 load_optional!("entityLoot", Vec<EntityLoot>).unwrap_or_default();
205 let block_collision_shapes_raw: Option<BlockCollisionShapes> =
206 load_optional!("blockCollisionShapes", BlockCollisionShapes);
207 let tints: Option<Tints> = load_optional!("tints", Tints);
208 let language: HashMap<String, String> =
209 load_optional!("language", HashMap<String, String>).unwrap_or_default();
210
211 let recipes: Option<Value> = load_optional_value!("recipes");
213 let materials: Option<Value> = load_optional_value!("materials");
214 let commands: Option<Value> = load_optional_value!("commands");
215 let protocol: Option<Value> = load_optional_value!("protocol");
216 let protocol_comments: Option<Value> = load_optional_value!("protocolComments");
217 let login_packet: Option<Value> = load_optional_value!("loginPacket");
218
219 let legacy: Option<Legacy> = {
221 match data_source::get_data_root() {
222 Ok(data_root) => {
223 let legacy_path =
224 data_root.join(format!("{}/common/legacy.json", edition.path_prefix()));
225 match loader::load_data_from_path(&legacy_path) {
226 Ok(data) => {
227 log::trace!("Successfully loaded legacy.json for {:?}", edition);
228 Some(data)
229 }
230 Err(McDataError::IoError { source, .. })
232 if source.kind() == std::io::ErrorKind::NotFound =>
233 {
234 log::trace!("legacy.json not found for {:?}", edition);
235 None
236 }
237 Err(e) => {
239 log::warn!("Failed to load legacy.json for {:?}: {}", edition, e);
240 None
241 }
242 }
243 }
244 Err(e) => {
245 log::warn!("Could not get data root to load legacy.json: {}", e);
246 None }
248 }
249 };
250
251 log::debug!("Indexing loaded data...");
253 let (blocks_by_id, blocks_by_name, blocks_by_state_id) = indexer::index_blocks(&blocks);
254 let (items_by_id, items_by_name) = indexer::index_items(&items);
255 let (biomes_by_id, biomes_by_name) = indexer::index_biomes(&biomes);
256 let (effects_by_id, effects_by_name) = indexer::index_effects(&effects);
257 let (entities_by_id, entities_by_name, mobs_by_id, objects_by_id) =
258 indexer::index_entities(&entities);
259 let (sounds_by_id, sounds_by_name) = indexer::index_sounds(&sounds);
260 let (particles_by_id, particles_by_name) = indexer::index_particles(&particles);
261 let (attributes_by_name, attributes_by_resource) = indexer::index_attributes(&attributes);
262 let (instruments_by_id, instruments_by_name) = indexer::index_instruments(&instruments);
263 let (foods_by_id, foods_by_name) = indexer::index_foods(&foods);
264 let (enchantments_by_id, enchantments_by_name) = indexer::index_enchantments(&enchantments);
265 let (map_icons_by_id, map_icons_by_name) = indexer::index_map_icons(&map_icons);
266 let (windows_by_id, windows_by_name) = indexer::index_windows(&windows);
267 let block_loot_by_name = indexer::index_block_loot(&block_loot);
268 let entity_loot_by_name = indexer::index_entity_loot(&entity_loot);
269
270 let (block_shapes_by_state_id, block_shapes_by_name) = if let Some(ref collision_data) =
272 block_collision_shapes_raw
273 {
274 indexer::index_block_shapes(&blocks_by_state_id, &blocks_by_name, collision_data)
275 } else {
276 log::debug!(
278 "No blockCollisionShapes data found for this version, block shapes will be empty."
279 );
280 (HashMap::new(), HashMap::new())
281 };
282
283 log::info!(
284 "Finished loading and indexing data for {} ({:?})",
285 version.minecraft_version,
286 version.edition
287 );
288
289 Ok(IndexedData {
291 version,
292 blocks_array: Arc::new(blocks),
294 items_array: Arc::new(items),
295 biomes_array: Arc::new(biomes),
296 effects_array: Arc::new(effects),
297 entities_array: Arc::new(entities),
298 sounds_array: Arc::new(sounds),
299 particles_array: Arc::new(particles),
300 attributes_array: Arc::new(attributes),
301 instruments_array: Arc::new(instruments),
302 foods_array: Arc::new(foods),
303 enchantments_array: Arc::new(enchantments),
304 map_icons_array: Arc::new(map_icons),
305 windows_array: Arc::new(windows),
306 block_loot_array: Arc::new(block_loot),
307 entity_loot_array: Arc::new(entity_loot),
308 blocks_by_id: Arc::new(blocks_by_id),
310 blocks_by_name: Arc::new(blocks_by_name),
311 blocks_by_state_id: Arc::new(blocks_by_state_id),
312 items_by_id: Arc::new(items_by_id),
313 items_by_name: Arc::new(items_by_name),
314 biomes_by_id: Arc::new(biomes_by_id),
315 biomes_by_name: Arc::new(biomes_by_name),
316 effects_by_id: Arc::new(effects_by_id),
317 effects_by_name: Arc::new(effects_by_name),
318 entities_by_id: Arc::new(entities_by_id),
319 entities_by_name: Arc::new(entities_by_name),
320 mobs_by_id: Arc::new(mobs_by_id),
321 objects_by_id: Arc::new(objects_by_id),
322 sounds_by_id: Arc::new(sounds_by_id),
323 sounds_by_name: Arc::new(sounds_by_name),
324 particles_by_id: Arc::new(particles_by_id),
325 particles_by_name: Arc::new(particles_by_name),
326 attributes_by_name: Arc::new(attributes_by_name),
327 attributes_by_resource: Arc::new(attributes_by_resource),
328 instruments_by_id: Arc::new(instruments_by_id),
329 instruments_by_name: Arc::new(instruments_by_name),
330 foods_by_id: Arc::new(foods_by_id),
331 foods_by_name: Arc::new(foods_by_name),
332 enchantments_by_id: Arc::new(enchantments_by_id),
333 enchantments_by_name: Arc::new(enchantments_by_name),
334 map_icons_by_id: Arc::new(map_icons_by_id),
335 map_icons_by_name: Arc::new(map_icons_by_name),
336 windows_by_id: Arc::new(windows_by_id),
337 windows_by_name: Arc::new(windows_by_name),
338 block_loot_by_name: Arc::new(block_loot_by_name),
339 entity_loot_by_name: Arc::new(entity_loot_by_name),
340 block_shapes_by_state_id: Arc::new(block_shapes_by_state_id),
342 block_shapes_by_name: Arc::new(block_shapes_by_name),
343 block_collision_shapes_raw: Arc::new(block_collision_shapes_raw),
345 tints: Arc::new(tints),
346 language: Arc::new(language),
347 legacy: Arc::new(legacy),
348 recipes: Arc::new(recipes),
350 materials: Arc::new(materials),
351 commands: Arc::new(commands),
352 protocol: Arc::new(protocol),
353 protocol_comments: Arc::new(protocol_comments),
354 login_packet: Arc::new(login_packet),
355 })
356 }
357
358 pub fn is_newer_or_equal_to(&self, other_version_str: &str) -> Result<bool, McDataError> {
366 let other_version = crate::version::resolve_version(other_version_str)?;
367 if self.version.edition == other_version.edition {
369 Ok(self.version >= other_version) } else {
371 Err(McDataError::Internal(format!(
372 "Cannot compare versions from different editions: {:?} ({}) and {:?} ({})",
373 self.version.edition,
374 self.version.minecraft_version,
375 other_version.edition,
376 other_version.minecraft_version
377 )))
378 }
379 }
380
381 pub fn is_older_than(&self, other_version_str: &str) -> Result<bool, McDataError> {
389 let other_version = crate::version::resolve_version(other_version_str)?;
390 if self.version.edition == other_version.edition {
392 Ok(self.version < other_version) } else {
394 Err(McDataError::Internal(format!(
395 "Cannot compare versions from different editions: {:?} ({}) and {:?} ({})",
396 self.version.edition,
397 self.version.minecraft_version,
398 other_version.edition,
399 other_version.minecraft_version
400 )))
401 }
402 }
403
404 pub fn support_feature(&self, feature_name: &str) -> Result<Value, McDataError> {
412 features::get_feature_support(&self.version, feature_name)
413 }
414}