From b2f6e23fc16abb427b2adb4c80aa219b371be8ea Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Tue, 12 May 2026 09:55:45 +0200 Subject: [PATCH] Refactor NBT parsing logic and correct block data handling in `read_blocks`. Match schema changes and improve error handling across version loaders. --- schemsearch-files/src/lib.rs | 79 +++++++++++++++++++++++++----------- schemsearch-sql/src/lib.rs | 2 +- 2 files changed, 56 insertions(+), 25 deletions(-) diff --git a/schemsearch-files/src/lib.rs b/schemsearch-files/src/lib.rs index c1dda3f..dea57e4 100644 --- a/schemsearch-files/src/lib.rs +++ b/schemsearch-files/src/lib.rs @@ -15,10 +15,10 @@ * along with this program. If not, see . */ +use nbt::{CompoundTag, Tag}; use std::collections::hash_map::HashMap; use std::io::Read; use std::path::PathBuf; -use nbt::{CompoundTag, Tag}; #[derive(Clone, Debug)] pub struct SpongeSchematic { @@ -62,8 +62,12 @@ pub struct Entity { } impl SpongeSchematic { - pub fn load_data(data: &mut R) -> Result where R: Read { - let nbt: CompoundTag = nbt::decode::read_gzip_compound_tag(data).map_err(|e| e.to_string())?; + pub fn load_data(data: &mut R) -> Result + where + R: Read, + { + let nbt: CompoundTag = + nbt::decode::read_gzip_compound_tag(data).map_err(|e| e.to_string())?; let version = nbt.get_i32("Version").unwrap_or_else(|_| { return if nbt.contains_key("Blocks") { 3 @@ -79,7 +83,10 @@ impl SpongeSchematic { match version { 1 => SpongeSchematic::from_nbt_1(nbt), 2 => SpongeSchematic::from_nbt_2(nbt), - 3 => SpongeSchematic::from_nbt_3(nbt), + 3 => SpongeSchematic::from_nbt_3( + nbt.get_compound_tag("Schematic") + .map_err(|e| e.to_string())?, + ), _ => Err("Invalid schematic: Unknown Version".to_string()), } } @@ -92,7 +99,10 @@ impl SpongeSchematic { pub fn from_nbt_1(nbt: CompoundTag) -> Result { Ok(Self { data_version: 0, - metadata: nbt.get_compound_tag("Metadata").map_err(|e| e.to_string())?.clone(), + metadata: nbt + .get_compound_tag("Metadata") + .map(|v| v.clone()) + .unwrap_or_else(|_| CompoundTag::new()), width: nbt.get_i16("Width").map_err(|e| e.to_string())? as u16, height: nbt.get_i16("Height").map_err(|e| e.to_string())? as u16, length: nbt.get_i16("Length").map_err(|e| e.to_string())? as u16, @@ -100,15 +110,21 @@ impl SpongeSchematic { palette_max: nbt.get_i32("PaletteMax").map_err(|e| e.to_string())?, palette: read_palette(nbt.get_compound_tag("Palette").map_err(|e| e.to_string())?), block_data: read_blocks(nbt.get_i8_vec("BlockData").map_err(|e| e.to_string())?), - block_entities: read_tile_entities(nbt.get_compound_tag_vec("TileEntities").unwrap_or_else(|_| vec![]))?, + block_entities: read_tile_entities( + nbt.get_compound_tag_vec("TileEntities") + .unwrap_or_else(|_| vec![]), + )?, entities: None, }) } pub fn from_nbt_2(nbt: CompoundTag) -> Result { - Ok(Self{ + Ok(Self { data_version: nbt.get_i32("DataVersion").map_err(|e| e.to_string())?, - metadata: nbt.get_compound_tag("Metadata").map_err(|e| e.to_string())?.clone(), + metadata: nbt + .get_compound_tag("Metadata") + .map(|v| v.clone()) + .unwrap_or_else(|_| CompoundTag::new()), width: nbt.get_i16("Width").map_err(|e| e.to_string())? as u16, height: nbt.get_i16("Height").map_err(|e| e.to_string())? as u16, length: nbt.get_i16("Length").map_err(|e| e.to_string())? as u16, @@ -116,32 +132,37 @@ impl SpongeSchematic { palette_max: nbt.get_i32("PaletteMax").map_err(|e| e.to_string())?, palette: read_palette(nbt.get_compound_tag("Palette").map_err(|e| e.to_string())?), block_data: read_blocks(nbt.get_i8_vec("BlockData").map_err(|e| e.to_string())?), - block_entities: read_tile_entities(nbt.get_compound_tag_vec("BlockEntities").unwrap_or_else(|_| vec![]))?, + block_entities: read_tile_entities( + nbt.get_compound_tag_vec("BlockEntities") + .unwrap_or_else(|_| vec![]), + )?, entities: None, }) } - pub fn from_nbt_3(nbt: CompoundTag) -> Result { + pub fn from_nbt_3(nbt: &CompoundTag) -> Result { let blocks = nbt.get_compound_tag("Blocks").map_err(|e| e.to_string())?; - Ok(Self{ + Ok(Self { data_version: nbt.get_i32("DataVersion").map_err(|e| e.to_string())?, - metadata: nbt.get_compound_tag("Metadata").map_err(|e| e.to_string())?.clone(), + metadata: nbt + .get_compound_tag("Metadata") + .map(|v| v.clone()) + .unwrap_or_else(|_| CompoundTag::new()), width: nbt.get_i16("Width").map_err(|e| e.to_string())? as u16, height: nbt.get_i16("Height").map_err(|e| e.to_string())? as u16, length: nbt.get_i16("Length").map_err(|e| e.to_string())? as u16, offset: read_offset(nbt.get_i32_vec("Offset").map_err(|e| e.to_string())?)?, - palette_max: compute_palette_max(blocks.get_compound_tag("Palette").map_err(|e| e.to_string())?), + palette_max: compute_palette_max(blocks.get_compound_tag("Palette").map_err(|e| e.to_string())?) + 1, palette: read_palette(blocks.get_compound_tag("Palette").map_err(|e| e.to_string())?), - block_data: read_blocks(blocks.get_i8_vec("BlockData").map_err(|e| e.to_string())?), + block_data: read_blocks(blocks.get_i8_vec("Data").map_err(|e| e.to_string())?), block_entities: read_tile_entities(blocks.get_compound_tag_vec("BlockEntities").unwrap_or_else(|_| vec![]))?, entities: None, }) } - } fn read_tile_entities(tag: Vec<&CompoundTag>) -> Result, String> { -let mut tile_entities = Vec::new(); + let mut tile_entities = Vec::new(); for t in tag { tile_entities.push(BlockEntity { id: t.get_str("Id").map_err(|e| e.to_string())?.to_string(), @@ -164,8 +185,10 @@ fn read_palette(p: &CompoundTag) -> HashMap { let mut palette = HashMap::new(); for (key, value) in p.iter() { match value { - Tag::Int(n) => { palette.insert(key.clone(), *n); }, - _ => {}, + Tag::Int(n) => { + palette.insert(key.clone(), *n); + } + _ => {} }; } palette @@ -173,10 +196,15 @@ fn read_palette(p: &CompoundTag) -> HashMap { #[inline] fn compute_palette_max(palette: &CompoundTag) -> i32 { - palette.iter().map(|(_, v)| v).filter_map(|v| match v { - Tag::Int(n) => Some(*n), - _ => None, - }).max().unwrap_or(0) + palette + .iter() + .map(|(_, v)| v) + .filter_map(|v| match v { + Tag::Int(n) => Some(*n), + _ => None, + }) + .max() + .unwrap_or(0) } #[inline] @@ -193,13 +221,16 @@ pub fn read_varint_array(read: &Vec) -> Vec { let mut cursor = 0; loop { match read.get(cursor) { - Some(byte) => { current_byte = *byte as u8; cursor += 1; }, + Some(byte) => { + current_byte = *byte as u8; + cursor += 1; + } None => break, }; value |= (((current_byte & 0x7F) as u32) << position) as i32; - if(current_byte & 0x80) == 0 { + if (current_byte & 0x80) == 0 { data.push(value); value = 0; position = 0; diff --git a/schemsearch-sql/src/lib.rs b/schemsearch-sql/src/lib.rs index 068059f..f20fb47 100644 --- a/schemsearch-sql/src/lib.rs +++ b/schemsearch-sql/src/lib.rs @@ -49,7 +49,7 @@ pub async unsafe fn get_connection() { pub async fn load_all_schematics(filter: SchematicFilter) -> Vec { unsafe { get_connection().await; } let mut schematics = Vec::new(); - let rows = unsafe { &CONN }.lock().unwrap().as_mut().unwrap().fetch_all(format!("SELECT SN.NodeId, SN.NodeName FROM NodeData ND INNER JOIN SchematicNode SN ON SN.NodeId = ND.NodeId WHERE NodeFormat = true {}", filter.build()).as_str()).await.expect("Failed to fetch schematics"); + let rows = unsafe { &CONN }.lock().unwrap().as_mut().unwrap().fetch_all(format!("SELECT SN.NodeId, SN.NodeName FROM NodeData ND INNER JOIN SchematicNode SN ON SN.NodeId = ND.NodeId WHERE NodeFormat >= 2 {}", filter.build()).as_str()).await.expect("Failed to fetch schematics"); for row in rows { schematics.push(SchematicNode { id: row.get(0),