From 5fa06be0a7173fb28abc26856f915e7e8db8b6b4 Mon Sep 17 00:00:00 2001 From: Luigi Date: Wed, 8 Apr 2026 11:42:09 +0200 Subject: [PATCH 1/6] New Traffic lights --- src/bedrock_block_map.rs | 49 +++++++- src/block_definitions.rs | 4 + src/element_processing/highways.rs | 74 ++++++++++- src/element_processing/tree.rs | 2 +- src/world_editor/bedrock.rs | 12 +- src/world_editor/mod.rs | 191 +++++++++++++++++++++++++++++ 6 files changed, 323 insertions(+), 9 deletions(-) diff --git a/src/bedrock_block_map.rs b/src/bedrock_block_map.rs index 7527c7b..51f2c24 100644 --- a/src/bedrock_block_map.rs +++ b/src/bedrock_block_map.rs @@ -491,12 +491,23 @@ pub fn to_bedrock_block(block: Block) -> BedrockBlock { ), // Plain terracotta "terracotta" => BedrockBlock::simple("hardened_clay"), - + // Wall banner — Bedrock uses "minecraft:wall_banner" with a + // "facing_direction" int state: 2=north, 3=south, 4=west, 5=east. + // The color is stored in the block entity (Base field), not the block state. + // The facing string→int mapping is handled by to_bedrock_block_with_properties. + "light_gray_wall_banner" => BedrockBlock::with_states( + "wall_banner", + vec![("facing_direction", BedrockBlockStateValue::Int(2))], // default north + ), // Wool colors "white_wool" => BedrockBlock::with_states( "wool", vec![("color", BedrockBlockStateValue::String("white".to_string()))], ), + "black_wool" => BedrockBlock::with_states( + "wool", + vec![("color", BedrockBlockStateValue::String("black".to_string()))], + ), "red_wool" => BedrockBlock::with_states( "wool", vec![("color", BedrockBlockStateValue::String("red".to_string()))], @@ -821,6 +832,11 @@ pub fn to_bedrock_block_with_properties( return convert_rail(props_map); } + // Handle wall banners with facing property + if java_name == "light_gray_wall_banner" { + return convert_wall_banner(props_map); + } + // Fall back to basic conversion without properties to_bedrock_block(block) } @@ -1247,6 +1263,37 @@ fn convert_bed( } } +/// Convert Java wall banner to Bedrock format. +/// +/// Java stores facing as a string ("north"/"south"/"east"/"west") on the block state. +/// Bedrock uses `facing_direction` as an integer on `minecraft:wall_banner`: +/// 2 = north, 3 = south, 4 = west, 5 = east +/// +/// The banner color (light_gray = 7) and patterns live in the block entity, not here. +fn convert_wall_banner( + props: Option<&std::collections::HashMap>, +) -> BedrockBlock { + let facing_direction = props + .and_then(|p| p.get("facing")) + .and_then(|v| match v { + fastnbt::Value::String(s) => Some(s.as_str()), + _ => None, + }) + .map(|f| match f { + "north" => 2, + "south" => 3, + "west" => 4, + "east" => 5, + _ => 2, // default north + }) + .unwrap_or(2); + + BedrockBlock::with_states( + "wall_banner", + vec![("facing_direction", BedrockBlockStateValue::Int(facing_direction))], + ) +} + /// Convert Java rail to Bedrock format with rail_direction from shape property. /// /// Java uses `shape` strings ("north_south", "east_west", "ascending_east", etc.) diff --git a/src/block_definitions.rs b/src/block_definitions.rs index 9979963..3662765 100644 --- a/src/block_definitions.rs +++ b/src/block_definitions.rs @@ -332,6 +332,8 @@ impl Block { 251 => "cactus", 252 => "gray_concrete_powder", 253 => "cyan_terracotta", + 254 => "black_wool", + 255 => "light_gray_wall_banner", _ => panic!("Invalid id"), } } @@ -980,6 +982,8 @@ pub const RED_SANDSTONE: Block = Block::new(250); pub const CACTUS: Block = Block::new(251); pub const GRAY_CONCRETE_POWDER: Block = Block::new(252); pub const CYAN_TERRACOTTA: Block = Block::new(253); +pub const BLACK_WOOL: Block = Block::new(254); +pub const LIGHT_GRAY_WALL_BANNER: Block = Block::new(255); /// Maps a block to its corresponding stair variant #[inline] diff --git a/src/element_processing/highways.rs b/src/element_processing/highways.rs index 3be269b..a881b65 100644 --- a/src/element_processing/highways.rs +++ b/src/element_processing/highways.rs @@ -114,13 +114,75 @@ fn generate_highways_internal( let x: i32 = node.x; let z: i32 = node.z; - for dy in 1..=3 { - editor.set_block(COBBLESTONE_WALL, x, dy, z, None, None); - } + // Traffic light blocks + editor.set_block(COBBLESTONE_WALL, x, 1, z, None, None); + editor.set_block(IRON_BARS, x, 2, z, None, None); + editor.set_block(IRON_BARS, x, 3, z, None, None); + editor.set_block(BLACK_WOOL, x, 4, z, None, None); + editor.set_block(BLACK_WOOL, x, 5, z, None, None); - editor.set_block(GREEN_WOOL, x, 4, z, None, None); - editor.set_block(YELLOW_WOOL, x, 5, z, None, None); - editor.set_block(RED_WOOL, x, 6, z, None, None); + let banner_patterns_list = fastnbt::nbt!([ + { "color": "red", "pattern": "minecraft:triangle_top" }, + { "color": "lime", "pattern": "minecraft:triangle_bottom" }, + { "color": "yellow", "pattern": "minecraft:circle" }, + { "color": "black", "pattern": "minecraft:curly_border" }, + { "color": "black", "pattern": "minecraft:border" } + ]); + + // Banner placement logic + let abs_y = editor.get_absolute_y(x, 5, z); + let banner_offsets: [(i32, i32, &str); 4] = [ + (0, -1, "north"), + (0, 1, "south"), + (-1, 0, "west"), + (1, 0, "east"), + ]; + + // The 5 patterns expressed as (java_color, java_pattern_id) pairs + // so both Java and Bedrock writers can consume them. + const BANNER_PATTERNS: &[(&str, &str)] = &[ + ("red", "minecraft:triangle_top"), + ("lime", "minecraft:triangle_bottom"), + ("yellow", "minecraft:circle"), + ("black", "minecraft:curly_border"), + ("black", "minecraft:border"), + ]; + + for (dx, dz, facing) in &banner_offsets { + let bx = x + dx; + let bz = z + dz; + + // Apply Block rotation + editor.set_block_with_properties_absolute( + crate::block_definitions::BlockWithProperties::new( + LIGHT_GRAY_WALL_BANNER, + Some(fastnbt::nbt!({ "facing": facing })), + ), + bx, abs_y, bz, None, None, + ); + + // Write banner block entity in the correct format for + // the current output format (Java vs Bedrock). + match editor.format() { + crate::world_editor::WorldFormat::JavaAnvil => { + editor.set_banner_block_entity_absolute( + bx, + abs_y, + bz, + banner_patterns_list.clone(), + ); + } + crate::world_editor::WorldFormat::BedrockMcWorld => { + editor.set_bedrock_banner_block_entity_absolute( + bx, + abs_y, + bz, + "light_gray", + BANNER_PATTERNS, + ); + } + } + } } } } diff --git a/src/element_processing/tree.rs b/src/element_processing/tree.rs index fc95b0e..871f575 100644 --- a/src/element_processing/tree.rs +++ b/src/element_processing/tree.rs @@ -530,4 +530,4 @@ impl Tree<'_> { BEDROCK, ] } -} // impl Tree +} // impl Tree \ No newline at end of file diff --git a/src/world_editor/bedrock.rs b/src/world_editor/bedrock.rs index 299c113..0dfb343 100644 --- a/src/world_editor/bedrock.rs +++ b/src/world_editor/bedrock.rs @@ -526,7 +526,17 @@ impl BedrockWriter { return Ok(()); } - let data = nbtx::to_le_bytes(&deduped).map_err(|e| BedrockSaveError::Nbt(e.to_string()))?; + // Bedrock block entities and entities are stored as CONCATENATED individual + // NBT compounds — NOT as a single NBT list. Each compound is serialised + // back-to-back with no wrapper. nbtx::to_le_bytes() on a Vec would produce + // a TAG_List header, which Bedrock cannot parse. + let mut data: Vec = Vec::new(); + for compound in &deduped { + let bytes = nbtx::to_le_bytes(compound) + .map_err(|e| BedrockSaveError::Nbt(e.to_string()))?; + data.extend_from_slice(&bytes); + } + let key = build_chunk_key_bytes(chunk_pos, Dimension::Overworld, key_type, None); db.put(&key, &data) .map_err(|e| BedrockSaveError::Database(format!("{:?}", e)))?; diff --git a/src/world_editor/mod.rs b/src/world_editor/mod.rs index c00eed0..dee950a 100644 --- a/src/world_editor/mod.rs +++ b/src/world_editor/mod.rs @@ -237,6 +237,197 @@ impl<'a> WorldEditor<'a> { self.world.get_block(x, absolute_y, z).is_some() } + /// Places a banner block entity at the given coordinates (absolute Y). + /// This writes the pattern data into the chunk's block_entities list, + /// which is required for the banner patterns to appear in-game. + #[allow(dead_code)] + pub fn set_banner_block_entity_absolute( + &mut self, + x: i32, + absolute_y: i32, + z: i32, + patterns_list: Value, + ) { + if !self.xzbbox.contains(&XZPoint::new(x, z)) { + return; + } + + let chunk_x: i32 = x >> 4; + let chunk_z: i32 = z >> 4; + let region_x: i32 = chunk_x >> 5; + let region_z: i32 = chunk_z >> 5; + + let mut be = HashMap::new(); + be.insert("id".to_string(), Value::String("minecraft:banner".to_string())); + be.insert("x".to_string(), Value::Int(x)); + be.insert("y".to_string(), Value::Int(absolute_y)); + be.insert("z".to_string(), Value::Int(z)); + be.insert("keepPacked".to_string(), Value::Byte(0)); + be.insert("patterns".to_string(), patterns_list); + be.insert("components".to_string(), Value::Compound(HashMap::new())); + + let region = self.world.get_or_create_region(region_x, region_z); + let chunk = region.get_or_create_chunk(chunk_x & 31, chunk_z & 31); + + match chunk.other.entry("block_entities".to_string()) { + Entry::Occupied(mut entry) => { + if let Value::List(list) = entry.get_mut() { + list.push(Value::Compound(be)); + } + } + Entry::Vacant(entry) => { + entry.insert(Value::List(vec![Value::Compound(be)])); + } + } + } + + /// Places a Bedrock-format banner block entity at the given coordinates (absolute Y). + /// + /// Bedrock banners use a completely different block entity schema from Java: + /// - `Base`: Int — base color index (0=black … 15=white; light_gray=7) + /// - `Patterns`: List — each entry has `Color` (Int) and `Pattern` (String, short code) + /// - `Type`: Int — 0 = normal banner + /// + /// Java color names and pattern resource-path IDs are converted here to their + /// Bedrock integer color indices and short pattern codes. + #[allow(dead_code)] + pub fn set_bedrock_banner_block_entity_absolute( + &mut self, + x: i32, + absolute_y: i32, + z: i32, + base_color: &str, + patterns: &[(&str, &str)], // &[(java_color_name, java_pattern_id)] + ) { + if !self.xzbbox.contains(&XZPoint::new(x, z)) { + return; + } + + /// Maps a Java color name to the Bedrock integer color index used in banner + /// block entities. The ordering is the standard Minecraft dye index. + fn java_color_to_bedrock_int(color: &str) -> i32 { + match color { + "black" => 0, + "red" => 1, + "green" => 2, + "brown" => 3, + "blue" => 4, + "purple" => 5, + "cyan" => 6, + "light_gray" => 7, + "gray" => 8, + "pink" => 9, + "lime" => 10, + "yellow" => 11, + "light_blue" => 12, + "magenta" => 13, + "orange" => 14, + "white" => 15, + _ => 0, + } + } + + /// Maps a Java banner pattern resource-path ID (e.g. "minecraft:triangle_top") + /// to the Bedrock short pattern code (e.g. "tts"). + fn java_pattern_to_bedrock_code(pattern: &str) -> &'static str { + // Strip the optional "minecraft:" namespace prefix + let key = pattern.strip_prefix("minecraft:").unwrap_or(pattern); + match key { + "base" => "b", + "square_bottom_left" => "bl", + "square_bottom_right" => "br", + "square_top_left" => "tl", + "square_top_right" => "tr", + "stripe_bottom" => "bs", + "stripe_top" => "ts", + "stripe_left" => "ls", + "stripe_right" => "rs", + "stripe_center" => "cs", + "stripe_middle" => "ms", + "stripe_downright" => "drs", + "stripe_downleft" => "dls", + "stripe_small" => "ss", + "cross" => "cr", + "straight_cross" => "sc", + "triangle_bottom" => "bt", + "triangle_top" => "tt", + "triangles_bottom" => "bts", + "triangles_top" => "tts", + "diagonal_left" => "ld", + "diagonal_right" => "rd", + "diagonal_up_left" => "lud", + "diagonal_up_right" => "rud", + "circle" => "mc", + "rhombus" => "mr", + "half_vertical" => "vh", + "half_vertical_right" => "vhr", + "half_horizontal" => "hh", + "half_horizontal_bottom" => "hhb", + "border" => "bo", + "curly_border" => "cbo", + "gradient" => "gra", + "gradient_up" => "gru", + "bricks" => "bri", + "globe" => "glb", + "creeper" => "cre", + "skull" => "sku", + "flower" => "flo", + "mojang" => "moj", + "piglin" => "pig", + "flow" => "flw", + "guster" => "gus", + _ => "b", // fallback: solid base + } + } + + let chunk_x: i32 = x >> 4; + let chunk_z: i32 = z >> 4; + let region_x: i32 = chunk_x >> 5; + let region_z: i32 = chunk_z >> 5; + + let bedrock_patterns: Vec = patterns + .iter() + .map(|(color, pattern)| { + let mut entry = HashMap::new(); + entry.insert( + "Color".to_string(), + Value::Int(java_color_to_bedrock_int(color)), + ); + entry.insert( + "Pattern".to_string(), + Value::String(java_pattern_to_bedrock_code(pattern).to_string()), + ); + Value::Compound(entry) + }) + .collect(); + + let mut be = HashMap::new(); + be.insert("id".to_string(), Value::String("Banner".to_string())); + be.insert("x".to_string(), Value::Int(x)); + be.insert("y".to_string(), Value::Int(absolute_y)); + be.insert("z".to_string(), Value::Int(z)); + be.insert( + "Base".to_string(), + Value::Int(java_color_to_bedrock_int(base_color)), + ); + be.insert("Patterns".to_string(), Value::List(bedrock_patterns)); + be.insert("Type".to_string(), Value::Int(0)); + + let region = self.world.get_or_create_region(region_x, region_z); + let chunk = region.get_or_create_chunk(chunk_x & 31, chunk_z & 31); + + match chunk.other.entry("block_entities".to_string()) { + Entry::Occupied(mut entry) => { + if let Value::List(list) = entry.get_mut() { + list.push(Value::Compound(be)); + } + } + Entry::Vacant(entry) => { + entry.insert(Value::List(vec![Value::Compound(be)])); + } + } + } + /// Sets a sign at the given coordinates #[allow(clippy::too_many_arguments, dead_code)] pub fn set_sign( From 71f18c2f47df2eecc27aef5f710596c9ecbda4bf Mon Sep 17 00:00:00 2001 From: Luigi Date: Wed, 8 Apr 2026 12:39:58 +0200 Subject: [PATCH 2/6] Refactored Banner logic for better reusablilty --- src/element_processing/highways.rs | 48 ++++-------------------- src/world_editor/mod.rs | 60 +++++++++++++++++++++++++++++- 2 files changed, 65 insertions(+), 43 deletions(-) diff --git a/src/element_processing/highways.rs b/src/element_processing/highways.rs index a881b65..d416374 100644 --- a/src/element_processing/highways.rs +++ b/src/element_processing/highways.rs @@ -121,14 +121,6 @@ fn generate_highways_internal( editor.set_block(BLACK_WOOL, x, 4, z, None, None); editor.set_block(BLACK_WOOL, x, 5, z, None, None); - let banner_patterns_list = fastnbt::nbt!([ - { "color": "red", "pattern": "minecraft:triangle_top" }, - { "color": "lime", "pattern": "minecraft:triangle_bottom" }, - { "color": "yellow", "pattern": "minecraft:circle" }, - { "color": "black", "pattern": "minecraft:curly_border" }, - { "color": "black", "pattern": "minecraft:border" } - ]); - // Banner placement logic let abs_y = editor.get_absolute_y(x, 5, z); let banner_offsets: [(i32, i32, &str); 4] = [ @@ -138,7 +130,7 @@ fn generate_highways_internal( (1, 0, "east"), ]; - // The 5 patterns expressed as (java_color, java_pattern_id) pairs + // patterns expressed as (java_color, java_pattern_id) pairs // so both Java and Bedrock writers can consume them. const BANNER_PATTERNS: &[(&str, &str)] = &[ ("red", "minecraft:triangle_top"), @@ -149,39 +141,13 @@ fn generate_highways_internal( ]; for (dx, dz, facing) in &banner_offsets { - let bx = x + dx; - let bz = z + dz; - - // Apply Block rotation - editor.set_block_with_properties_absolute( - crate::block_definitions::BlockWithProperties::new( - LIGHT_GRAY_WALL_BANNER, - Some(fastnbt::nbt!({ "facing": facing })), - ), - bx, abs_y, bz, None, None, + editor.place_wall_banner( + LIGHT_GRAY_WALL_BANNER, + x + dx, abs_y, z + dz, + facing, + "light_gray", + BANNER_PATTERNS, ); - - // Write banner block entity in the correct format for - // the current output format (Java vs Bedrock). - match editor.format() { - crate::world_editor::WorldFormat::JavaAnvil => { - editor.set_banner_block_entity_absolute( - bx, - abs_y, - bz, - banner_patterns_list.clone(), - ); - } - crate::world_editor::WorldFormat::BedrockMcWorld => { - editor.set_bedrock_banner_block_entity_absolute( - bx, - abs_y, - bz, - "light_gray", - BANNER_PATTERNS, - ); - } - } } } } diff --git a/src/world_editor/mod.rs b/src/world_editor/mod.rs index dee950a..197cb4e 100644 --- a/src/world_editor/mod.rs +++ b/src/world_editor/mod.rs @@ -237,6 +237,46 @@ impl<'a> WorldEditor<'a> { self.world.get_block(x, absolute_y, z).is_some() } + pub fn place_wall_banner( + &mut self, + block: Block, // e.g. LIGHT_GRAY_WALL_BANNER + x: i32, + y: i32, // ground-relative + z: i32, + facing: &str, // "north" / "south" / "east" / "west" + base_color: &str, // "light_gray" etc. + patterns: &[(&str, &str)], // [("red", "minecraft:triangle_top"), ...] + ) { + // Apply Block rotation + self.set_block_with_properties_absolute( + crate::block_definitions::BlockWithProperties::new( + block, + Some(fastnbt::nbt!({ "facing": facing })), + ), + x, y, z, None, None, + ); + match self.format() { + + crate::world_editor::WorldFormat::JavaAnvil => { + self.set_banner_block_entity_absolute( + x, + y, + z, + patterns, + ); + } + crate::world_editor::WorldFormat::BedrockMcWorld => { + self.set_bedrock_banner_block_entity_absolute( + x, + y, + z, + base_color, + patterns, + ); + } + } + } + /// Places a banner block entity at the given coordinates (absolute Y). /// This writes the pattern data into the chunk's block_entities list, /// which is required for the banner patterns to appear in-game. @@ -246,7 +286,7 @@ impl<'a> WorldEditor<'a> { x: i32, absolute_y: i32, z: i32, - patterns_list: Value, + patterns_list: &[(&str, &str)], ) { if !self.xzbbox.contains(&XZPoint::new(x, z)) { return; @@ -263,7 +303,23 @@ impl<'a> WorldEditor<'a> { be.insert("y".to_string(), Value::Int(absolute_y)); be.insert("z".to_string(), Value::Int(z)); be.insert("keepPacked".to_string(), Value::Byte(0)); - be.insert("patterns".to_string(), patterns_list); + let patterns: Vec = patterns_list + .iter() + .map(|(color, pattern)| { + let mut entry = HashMap::new(); + + entry.insert( + "color".to_string(), + Value::String(color.to_string()), + ); + entry.insert( + "pattern".to_string(), + Value::String(pattern.to_string()), + ); + Value::Compound(entry) + }) + .collect(); + be.insert("patterns".to_string(), Value::List(patterns)); be.insert("components".to_string(), Value::Compound(HashMap::new())); let region = self.world.get_or_create_region(region_x, region_z); From ce79e6239a6bdde19d643e72a1a9a74aa1fbfa1a Mon Sep 17 00:00:00 2001 From: Luigi Date: Wed, 8 Apr 2026 13:39:24 +0200 Subject: [PATCH 3/6] Refactor: removed duplicate functions --- src/element_processing/highways.rs | 2 +- src/world_editor/mod.rs | 77 ++++++++++++------------------ 2 files changed, 31 insertions(+), 48 deletions(-) diff --git a/src/element_processing/highways.rs b/src/element_processing/highways.rs index d416374..d201d1e 100644 --- a/src/element_processing/highways.rs +++ b/src/element_processing/highways.rs @@ -131,7 +131,7 @@ fn generate_highways_internal( ]; // patterns expressed as (java_color, java_pattern_id) pairs - // so both Java and Bedrock writers can consume them. + // so both Java and Bedrock writers can use them. const BANNER_PATTERNS: &[(&str, &str)] = &[ ("red", "minecraft:triangle_top"), ("lime", "minecraft:triangle_bottom"), diff --git a/src/world_editor/mod.rs b/src/world_editor/mod.rs index 197cb4e..babc10b 100644 --- a/src/world_editor/mod.rs +++ b/src/world_editor/mod.rs @@ -277,26 +277,43 @@ impl<'a> WorldEditor<'a> { } } + fn insert_block_entity(&mut self, x: i32, z: i32, be: HashMap) { + if !self.xzbbox.contains(&XZPoint::new(x, z)) { + return; + } + let chunk_x = x >> 4; + let chunk_z = z >> 4; + let region_x = chunk_x >> 5; + let region_z = chunk_z >> 5; + + let region = self.world.get_or_create_region(region_x, region_z); + let chunk = region.get_or_create_chunk(chunk_x & 31, chunk_z & 31); + + const BLOCK_ENTITIES_KEY: &str = "block_entities"; + + match chunk.other.entry(BLOCK_ENTITIES_KEY.to_string()) { + Entry::Occupied(mut entry) => { + if let Value::List(list) = entry.get_mut() { + list.push(Value::Compound(be)); + } + } + Entry::Vacant(entry) => { + entry.insert(Value::List(vec![Value::Compound(be)])); + } + } + } + /// Places a banner block entity at the given coordinates (absolute Y). /// This writes the pattern data into the chunk's block_entities list, /// which is required for the banner patterns to appear in-game. #[allow(dead_code)] - pub fn set_banner_block_entity_absolute( + fn set_banner_block_entity_absolute( &mut self, x: i32, absolute_y: i32, z: i32, patterns_list: &[(&str, &str)], ) { - if !self.xzbbox.contains(&XZPoint::new(x, z)) { - return; - } - - let chunk_x: i32 = x >> 4; - let chunk_z: i32 = z >> 4; - let region_x: i32 = chunk_x >> 5; - let region_z: i32 = chunk_z >> 5; - let mut be = HashMap::new(); be.insert("id".to_string(), Value::String("minecraft:banner".to_string())); be.insert("x".to_string(), Value::Int(x)); @@ -321,20 +338,7 @@ impl<'a> WorldEditor<'a> { .collect(); be.insert("patterns".to_string(), Value::List(patterns)); be.insert("components".to_string(), Value::Compound(HashMap::new())); - - let region = self.world.get_or_create_region(region_x, region_z); - let chunk = region.get_or_create_chunk(chunk_x & 31, chunk_z & 31); - - match chunk.other.entry("block_entities".to_string()) { - Entry::Occupied(mut entry) => { - if let Value::List(list) = entry.get_mut() { - list.push(Value::Compound(be)); - } - } - Entry::Vacant(entry) => { - entry.insert(Value::List(vec![Value::Compound(be)])); - } - } + self.insert_block_entity(x, z, be); } /// Places a Bedrock-format banner block entity at the given coordinates (absolute Y). @@ -347,7 +351,7 @@ impl<'a> WorldEditor<'a> { /// Java color names and pattern resource-path IDs are converted here to their /// Bedrock integer color indices and short pattern codes. #[allow(dead_code)] - pub fn set_bedrock_banner_block_entity_absolute( + fn set_bedrock_banner_block_entity_absolute( &mut self, x: i32, absolute_y: i32, @@ -355,10 +359,6 @@ impl<'a> WorldEditor<'a> { base_color: &str, patterns: &[(&str, &str)], // &[(java_color_name, java_pattern_id)] ) { - if !self.xzbbox.contains(&XZPoint::new(x, z)) { - return; - } - /// Maps a Java color name to the Bedrock integer color index used in banner /// block entities. The ordering is the standard Minecraft dye index. fn java_color_to_bedrock_int(color: &str) -> i32 { @@ -436,11 +436,6 @@ impl<'a> WorldEditor<'a> { } } - let chunk_x: i32 = x >> 4; - let chunk_z: i32 = z >> 4; - let region_x: i32 = chunk_x >> 5; - let region_z: i32 = chunk_z >> 5; - let bedrock_patterns: Vec = patterns .iter() .map(|(color, pattern)| { @@ -469,19 +464,7 @@ impl<'a> WorldEditor<'a> { be.insert("Patterns".to_string(), Value::List(bedrock_patterns)); be.insert("Type".to_string(), Value::Int(0)); - let region = self.world.get_or_create_region(region_x, region_z); - let chunk = region.get_or_create_chunk(chunk_x & 31, chunk_z & 31); - - match chunk.other.entry("block_entities".to_string()) { - Entry::Occupied(mut entry) => { - if let Value::List(list) = entry.get_mut() { - list.push(Value::Compound(be)); - } - } - Entry::Vacant(entry) => { - entry.insert(Value::List(vec![Value::Compound(be)])); - } - } + self.insert_block_entity(x, z, be); } /// Sets a sign at the given coordinates From 682ad98142379f852703a903c21baf4196b67b79 Mon Sep 17 00:00:00 2001 From: Luigi Date: Wed, 8 Apr 2026 14:03:03 +0200 Subject: [PATCH 4/6] Apply cargo fmt --- src/bedrock_block_map.rs | 11 +- src/element_processing/highways.rs | 12 ++- src/element_processing/tree.rs | 2 +- src/world_editor/bedrock.rs | 4 +- src/world_editor/mod.rs | 157 ++++++++++++++--------------- 5 files changed, 90 insertions(+), 96 deletions(-) diff --git a/src/bedrock_block_map.rs b/src/bedrock_block_map.rs index 51f2c24..a704c47 100644 --- a/src/bedrock_block_map.rs +++ b/src/bedrock_block_map.rs @@ -1282,15 +1282,18 @@ fn convert_wall_banner( .map(|f| match f { "north" => 2, "south" => 3, - "west" => 4, - "east" => 5, - _ => 2, // default north + "west" => 4, + "east" => 5, + _ => 2, // default north }) .unwrap_or(2); BedrockBlock::with_states( "wall_banner", - vec![("facing_direction", BedrockBlockStateValue::Int(facing_direction))], + vec![( + "facing_direction", + BedrockBlockStateValue::Int(facing_direction), + )], ) } diff --git a/src/element_processing/highways.rs b/src/element_processing/highways.rs index d201d1e..f34e883 100644 --- a/src/element_processing/highways.rs +++ b/src/element_processing/highways.rs @@ -133,17 +133,19 @@ fn generate_highways_internal( // patterns expressed as (java_color, java_pattern_id) pairs // so both Java and Bedrock writers can use them. const BANNER_PATTERNS: &[(&str, &str)] = &[ - ("red", "minecraft:triangle_top"), - ("lime", "minecraft:triangle_bottom"), + ("red", "minecraft:triangle_top"), + ("lime", "minecraft:triangle_bottom"), ("yellow", "minecraft:circle"), - ("black", "minecraft:curly_border"), - ("black", "minecraft:border"), + ("black", "minecraft:curly_border"), + ("black", "minecraft:border"), ]; for (dx, dz, facing) in &banner_offsets { editor.place_wall_banner( LIGHT_GRAY_WALL_BANNER, - x + dx, abs_y, z + dz, + x + dx, + abs_y, + z + dz, facing, "light_gray", BANNER_PATTERNS, diff --git a/src/element_processing/tree.rs b/src/element_processing/tree.rs index 871f575..fc95b0e 100644 --- a/src/element_processing/tree.rs +++ b/src/element_processing/tree.rs @@ -530,4 +530,4 @@ impl Tree<'_> { BEDROCK, ] } -} // impl Tree \ No newline at end of file +} // impl Tree diff --git a/src/world_editor/bedrock.rs b/src/world_editor/bedrock.rs index 0dfb343..199bc8d 100644 --- a/src/world_editor/bedrock.rs +++ b/src/world_editor/bedrock.rs @@ -532,8 +532,8 @@ impl BedrockWriter { // a TAG_List header, which Bedrock cannot parse. let mut data: Vec = Vec::new(); for compound in &deduped { - let bytes = nbtx::to_le_bytes(compound) - .map_err(|e| BedrockSaveError::Nbt(e.to_string()))?; + let bytes = + nbtx::to_le_bytes(compound).map_err(|e| BedrockSaveError::Nbt(e.to_string()))?; data.extend_from_slice(&bytes); } diff --git a/src/world_editor/mod.rs b/src/world_editor/mod.rs index babc10b..b8fa60c 100644 --- a/src/world_editor/mod.rs +++ b/src/world_editor/mod.rs @@ -239,9 +239,9 @@ impl<'a> WorldEditor<'a> { pub fn place_wall_banner( &mut self, - block: Block, // e.g. LIGHT_GRAY_WALL_BANNER + block: Block, // e.g. LIGHT_GRAY_WALL_BANNER x: i32, - y: i32, // ground-relative + y: i32, // ground-relative z: i32, facing: &str, // "north" / "south" / "east" / "west" base_color: &str, // "light_gray" etc. @@ -253,26 +253,18 @@ impl<'a> WorldEditor<'a> { block, Some(fastnbt::nbt!({ "facing": facing })), ), - x, y, z, None, None, + x, + y, + z, + None, + None, ); match self.format() { - crate::world_editor::WorldFormat::JavaAnvil => { - self.set_banner_block_entity_absolute( - x, - y, - z, - patterns, - ); + self.set_banner_block_entity_absolute(x, y, z, patterns); } crate::world_editor::WorldFormat::BedrockMcWorld => { - self.set_bedrock_banner_block_entity_absolute( - x, - y, - z, - base_color, - patterns, - ); + self.set_bedrock_banner_block_entity_absolute(x, y, z, base_color, patterns); } } } @@ -315,7 +307,10 @@ impl<'a> WorldEditor<'a> { patterns_list: &[(&str, &str)], ) { let mut be = HashMap::new(); - be.insert("id".to_string(), Value::String("minecraft:banner".to_string())); + be.insert( + "id".to_string(), + Value::String("minecraft:banner".to_string()), + ); be.insert("x".to_string(), Value::Int(x)); be.insert("y".to_string(), Value::Int(absolute_y)); be.insert("z".to_string(), Value::Int(z)); @@ -325,14 +320,8 @@ impl<'a> WorldEditor<'a> { .map(|(color, pattern)| { let mut entry = HashMap::new(); - entry.insert( - "color".to_string(), - Value::String(color.to_string()), - ); - entry.insert( - "pattern".to_string(), - Value::String(pattern.to_string()), - ); + entry.insert("color".to_string(), Value::String(color.to_string())); + entry.insert("pattern".to_string(), Value::String(pattern.to_string())); Value::Compound(entry) }) .collect(); @@ -363,23 +352,23 @@ impl<'a> WorldEditor<'a> { /// block entities. The ordering is the standard Minecraft dye index. fn java_color_to_bedrock_int(color: &str) -> i32 { match color { - "black" => 0, - "red" => 1, - "green" => 2, - "brown" => 3, - "blue" => 4, - "purple" => 5, - "cyan" => 6, - "light_gray" => 7, - "gray" => 8, - "pink" => 9, - "lime" => 10, - "yellow" => 11, - "light_blue" => 12, - "magenta" => 13, - "orange" => 14, - "white" => 15, - _ => 0, + "black" => 0, + "red" => 1, + "green" => 2, + "brown" => 3, + "blue" => 4, + "purple" => 5, + "cyan" => 6, + "light_gray" => 7, + "gray" => 8, + "pink" => 9, + "lime" => 10, + "yellow" => 11, + "light_blue" => 12, + "magenta" => 13, + "orange" => 14, + "white" => 15, + _ => 0, } } @@ -389,50 +378,50 @@ impl<'a> WorldEditor<'a> { // Strip the optional "minecraft:" namespace prefix let key = pattern.strip_prefix("minecraft:").unwrap_or(pattern); match key { - "base" => "b", - "square_bottom_left" => "bl", + "base" => "b", + "square_bottom_left" => "bl", "square_bottom_right" => "br", - "square_top_left" => "tl", - "square_top_right" => "tr", - "stripe_bottom" => "bs", - "stripe_top" => "ts", - "stripe_left" => "ls", - "stripe_right" => "rs", - "stripe_center" => "cs", - "stripe_middle" => "ms", - "stripe_downright" => "drs", - "stripe_downleft" => "dls", - "stripe_small" => "ss", - "cross" => "cr", - "straight_cross" => "sc", - "triangle_bottom" => "bt", - "triangle_top" => "tt", - "triangles_bottom" => "bts", - "triangles_top" => "tts", - "diagonal_left" => "ld", - "diagonal_right" => "rd", - "diagonal_up_left" => "lud", - "diagonal_up_right" => "rud", - "circle" => "mc", - "rhombus" => "mr", - "half_vertical" => "vh", + "square_top_left" => "tl", + "square_top_right" => "tr", + "stripe_bottom" => "bs", + "stripe_top" => "ts", + "stripe_left" => "ls", + "stripe_right" => "rs", + "stripe_center" => "cs", + "stripe_middle" => "ms", + "stripe_downright" => "drs", + "stripe_downleft" => "dls", + "stripe_small" => "ss", + "cross" => "cr", + "straight_cross" => "sc", + "triangle_bottom" => "bt", + "triangle_top" => "tt", + "triangles_bottom" => "bts", + "triangles_top" => "tts", + "diagonal_left" => "ld", + "diagonal_right" => "rd", + "diagonal_up_left" => "lud", + "diagonal_up_right" => "rud", + "circle" => "mc", + "rhombus" => "mr", + "half_vertical" => "vh", "half_vertical_right" => "vhr", - "half_horizontal" => "hh", + "half_horizontal" => "hh", "half_horizontal_bottom" => "hhb", - "border" => "bo", - "curly_border" => "cbo", - "gradient" => "gra", - "gradient_up" => "gru", - "bricks" => "bri", - "globe" => "glb", - "creeper" => "cre", - "skull" => "sku", - "flower" => "flo", - "mojang" => "moj", - "piglin" => "pig", - "flow" => "flw", - "guster" => "gus", - _ => "b", // fallback: solid base + "border" => "bo", + "curly_border" => "cbo", + "gradient" => "gra", + "gradient_up" => "gru", + "bricks" => "bri", + "globe" => "glb", + "creeper" => "cre", + "skull" => "sku", + "flower" => "flo", + "mojang" => "moj", + "piglin" => "pig", + "flow" => "flw", + "guster" => "gus", + _ => "b", // fallback: solid base } } From b8beee0c3e5d76e723a0f1c77acaca75f41d1a92 Mon Sep 17 00:00:00 2001 From: Luigi Date: Wed, 8 Apr 2026 23:12:54 +0200 Subject: [PATCH 5/6] added allow too many arguments and removed worthless comments --- src/world_editor/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/world_editor/mod.rs b/src/world_editor/mod.rs index b8fa60c..c5c1d53 100644 --- a/src/world_editor/mod.rs +++ b/src/world_editor/mod.rs @@ -236,12 +236,12 @@ impl<'a> WorldEditor<'a> { let absolute_y = self.get_absolute_y(x, y, z); self.world.get_block(x, absolute_y, z).is_some() } - + #[allow(clippy::too_many_arguments)] pub fn place_wall_banner( &mut self, - block: Block, // e.g. LIGHT_GRAY_WALL_BANNER + block: Block, x: i32, - y: i32, // ground-relative + y: i32, z: i32, facing: &str, // "north" / "south" / "east" / "west" base_color: &str, // "light_gray" etc. @@ -298,7 +298,7 @@ impl<'a> WorldEditor<'a> { /// Places a banner block entity at the given coordinates (absolute Y). /// This writes the pattern data into the chunk's block_entities list, /// which is required for the banner patterns to appear in-game. - #[allow(dead_code)] + fn set_banner_block_entity_absolute( &mut self, x: i32, @@ -339,7 +339,7 @@ impl<'a> WorldEditor<'a> { /// /// Java color names and pattern resource-path IDs are converted here to their /// Bedrock integer color indices and short pattern codes. - #[allow(dead_code)] + fn set_bedrock_banner_block_entity_absolute( &mut self, x: i32, From 94913d7cd756c16eb805d938d60aff14efde747c Mon Sep 17 00:00:00 2001 From: Louis Erbkamm Date: Thu, 9 Apr 2026 17:13:17 +0200 Subject: [PATCH 6/6] Clean up whitespace in banner block entity methods (clippy) --- src/world_editor/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/world_editor/mod.rs b/src/world_editor/mod.rs index c5c1d53..68c4c03 100644 --- a/src/world_editor/mod.rs +++ b/src/world_editor/mod.rs @@ -298,7 +298,6 @@ impl<'a> WorldEditor<'a> { /// Places a banner block entity at the given coordinates (absolute Y). /// This writes the pattern data into the chunk's block_entities list, /// which is required for the banner patterns to appear in-game. - fn set_banner_block_entity_absolute( &mut self, x: i32, @@ -339,7 +338,6 @@ impl<'a> WorldEditor<'a> { /// /// Java color names and pattern resource-path IDs are converted here to their /// Bedrock integer color indices and short pattern codes. - fn set_bedrock_banner_block_entity_absolute( &mut self, x: i32,