mirror of
https://github.com/louis-e/arnis.git
synced 2026-01-29 08:23:18 -05:00
Resolve merge conflicts
This commit is contained in:
@@ -267,6 +267,14 @@ impl Block {
|
||||
186 => "polished_andesite_stairs",
|
||||
187 => "nether_brick_stairs",
|
||||
188 => "barrel",
|
||||
189 => "fern",
|
||||
190 => "cobweb",
|
||||
191 => "chiseled_bookshelf",
|
||||
192 => "chiseled_bookshelf",
|
||||
193 => "chiseled_bookshelf",
|
||||
194 => "chiseled_bookshelf",
|
||||
195 => "chipped_anvil",
|
||||
196 => "damaged_anvil",
|
||||
_ => panic!("Invalid id"),
|
||||
}
|
||||
}
|
||||
@@ -464,6 +472,26 @@ impl Block {
|
||||
map.insert("half".to_string(), Value::String("top".to_string()));
|
||||
map
|
||||
})),
|
||||
191 => Some(Value::Compound({
|
||||
let mut map = HashMap::new();
|
||||
map.insert("facing".to_string(), Value::String("north".to_string()));
|
||||
map
|
||||
})),
|
||||
192 => Some(Value::Compound({
|
||||
let mut map = HashMap::new();
|
||||
map.insert("facing".to_string(), Value::String("east".to_string()));
|
||||
map
|
||||
})),
|
||||
193 => Some(Value::Compound({
|
||||
let mut map = HashMap::new();
|
||||
map.insert("facing".to_string(), Value::String("south".to_string()));
|
||||
map
|
||||
})),
|
||||
194 => Some(Value::Compound({
|
||||
let mut map = HashMap::new();
|
||||
map.insert("facing".to_string(), Value::String("west".to_string()));
|
||||
map
|
||||
})),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -699,6 +727,16 @@ pub const QUARTZ_STAIRS: Block = Block::new(185);
|
||||
pub const POLISHED_ANDESITE_STAIRS: Block = Block::new(186);
|
||||
pub const NETHER_BRICK_STAIRS: Block = Block::new(187);
|
||||
pub const BARREL: Block = Block::new(188);
|
||||
pub const FERN: Block = Block::new(189);
|
||||
pub const COBWEB: Block = Block::new(190);
|
||||
pub const CHISELLED_BOOKSHELF_NORTH: Block = Block::new(191);
|
||||
pub const CHISELLED_BOOKSHELF_EAST: Block = Block::new(192);
|
||||
pub const CHISELLED_BOOKSHELF_SOUTH: Block = Block::new(193);
|
||||
pub const CHISELLED_BOOKSHELF_WEST: Block = Block::new(194);
|
||||
// Backwards-compatible alias (defaults to north-facing)
|
||||
pub const CHISELLED_BOOKSHELF: Block = CHISELLED_BOOKSHELF_NORTH;
|
||||
pub const CHIPPED_ANVIL: Block = Block::new(195);
|
||||
pub const DAMAGED_ANVIL: Block = Block::new(196);
|
||||
|
||||
/// Maps a block to its corresponding stair variant
|
||||
#[inline]
|
||||
|
||||
@@ -2,11 +2,11 @@ use crate::args::Args;
|
||||
use crate::block_definitions::*;
|
||||
use crate::bresenham::bresenham_line;
|
||||
use crate::coordinate_system::cartesian::XZPoint;
|
||||
use crate::floodfill::flood_fill_area;
|
||||
use crate::floodfill::flood_fill_area; // Needed for inline amenity flood fills
|
||||
use crate::osm_parser::ProcessedElement;
|
||||
use crate::world_editor::WorldEditor;
|
||||
use fastnbt::Value;
|
||||
use rand::{seq::SliceRandom, Rng};
|
||||
use rand::{seq::SliceRandom, Rng, SeedableRng};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
pub fn generate_amenities(editor: &mut WorldEditor, element: &ProcessedElement, args: &Args) {
|
||||
@@ -88,18 +88,15 @@ pub fn generate_amenities(editor: &mut WorldEditor, element: &ProcessedElement,
|
||||
let ground_block: Block = OAK_PLANKS;
|
||||
let roof_block: Block = STONE_BLOCK_SLAB;
|
||||
|
||||
let polygon_coords: Vec<(i32, i32)> = element
|
||||
.nodes()
|
||||
.map(|n: &crate::osm_parser::ProcessedNode| (n.x, n.z))
|
||||
.collect();
|
||||
|
||||
if polygon_coords.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let polygon_coords: Vec<(i32, i32)> =
|
||||
element.nodes().map(|node| (node.x, node.z)).collect();
|
||||
let floor_area: Vec<(i32, i32)> =
|
||||
flood_fill_area(&polygon_coords, args.timeout.as_ref());
|
||||
|
||||
if floor_area.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fill the floor area
|
||||
for (x, z) in floor_area.iter() {
|
||||
editor.set_block(ground_block, *x, 0, *z, None, None);
|
||||
@@ -126,8 +123,10 @@ pub fn generate_amenities(editor: &mut WorldEditor, element: &ProcessedElement,
|
||||
"bench" => {
|
||||
// Place a bench
|
||||
if let Some(pt) = first_node {
|
||||
// 50% chance to 90 degrees rotate the bench using if
|
||||
if rand::random::<bool>() {
|
||||
// Use a deterministic RNG for consistent bench orientation across region boundaries.
|
||||
let mut rng = rand::rngs::StdRng::seed_from_u64(element.id());
|
||||
// 50% chance to 90 degrees rotate the bench
|
||||
if rng.gen_bool(0.5) {
|
||||
editor.set_block(SMOOTH_STONE, pt.x, 1, pt.z, None, None);
|
||||
editor.set_block(OAK_LOG, pt.x + 1, 1, pt.z, None, None);
|
||||
editor.set_block(OAK_LOG, pt.x - 1, 1, pt.z, None, None);
|
||||
@@ -141,10 +140,8 @@ pub fn generate_amenities(editor: &mut WorldEditor, element: &ProcessedElement,
|
||||
"shelter" => {
|
||||
let roof_block: Block = STONE_BRICK_SLAB;
|
||||
|
||||
let polygon_coords: Vec<(i32, i32)> = element
|
||||
.nodes()
|
||||
.map(|n: &crate::osm_parser::ProcessedNode| (n.x, n.z))
|
||||
.collect();
|
||||
let polygon_coords: Vec<(i32, i32)> =
|
||||
element.nodes().map(|node| (node.x, node.z)).collect();
|
||||
let roof_area: Vec<(i32, i32)> =
|
||||
flood_fill_area(&polygon_coords, args.timeout.as_ref());
|
||||
|
||||
|
||||
@@ -23,9 +23,11 @@ use crate::telemetry::{send_log, LogLevel};
|
||||
impl<'a> WorldEditor<'a> {
|
||||
/// Creates a region file for the given region coordinates.
|
||||
pub(super) fn create_region(&self, region_x: i32, region_z: i32) -> Region<File> {
|
||||
let out_path = self
|
||||
.world_dir
|
||||
.join(format!("region/r.{}.{}.mca", region_x, region_z));
|
||||
let region_dir = self.world_dir.join("region");
|
||||
let out_path = region_dir.join(format!("r.{}.{}.mca", region_x, region_z));
|
||||
|
||||
// Ensure region directory exists before creating region files
|
||||
std::fs::create_dir_all(®ion_dir).expect("Failed to create region directory");
|
||||
|
||||
const REGION_TEMPLATE: &[u8] = include_bytes!("../../assets/minecraft/region.template");
|
||||
|
||||
@@ -75,6 +77,8 @@ impl<'a> WorldEditor<'a> {
|
||||
}
|
||||
|
||||
/// Saves the world in Java Edition Anvil format.
|
||||
///
|
||||
/// Uses parallel processing with rayon for fast region saving.
|
||||
pub(super) fn save_java(&mut self) {
|
||||
println!("{} Saving world...", "[7/7]".bold());
|
||||
emit_gui_progress_update(90.0, "Saving world...");
|
||||
@@ -104,89 +108,12 @@ impl<'a> WorldEditor<'a> {
|
||||
.regions
|
||||
.par_iter()
|
||||
.for_each(|((region_x, region_z), region_to_modify)| {
|
||||
let mut region = self.create_region(*region_x, *region_z);
|
||||
let mut ser_buffer = Vec::with_capacity(8192);
|
||||
|
||||
for (&(chunk_x, chunk_z), chunk_to_modify) in ®ion_to_modify.chunks {
|
||||
if !chunk_to_modify.sections.is_empty() || !chunk_to_modify.other.is_empty() {
|
||||
// Read existing chunk data if it exists
|
||||
let existing_data = region
|
||||
.read_chunk(chunk_x as usize, chunk_z as usize)
|
||||
.unwrap()
|
||||
.unwrap_or_default();
|
||||
|
||||
// Parse existing chunk or create new one
|
||||
let mut chunk: Chunk = if !existing_data.is_empty() {
|
||||
fastnbt::from_bytes(&existing_data).unwrap()
|
||||
} else {
|
||||
Chunk {
|
||||
sections: Vec::new(),
|
||||
x_pos: chunk_x + (region_x * 32),
|
||||
z_pos: chunk_z + (region_z * 32),
|
||||
is_light_on: 0,
|
||||
other: FnvHashMap::default(),
|
||||
}
|
||||
};
|
||||
|
||||
// Update sections while preserving existing data
|
||||
let new_sections: Vec<Section> = chunk_to_modify.sections().collect();
|
||||
for new_section in new_sections {
|
||||
if let Some(existing_section) =
|
||||
chunk.sections.iter_mut().find(|s| s.y == new_section.y)
|
||||
{
|
||||
// Merge block states
|
||||
existing_section.block_states.palette =
|
||||
new_section.block_states.palette;
|
||||
existing_section.block_states.data = new_section.block_states.data;
|
||||
} else {
|
||||
// Add new section if it doesn't exist
|
||||
chunk.sections.push(new_section);
|
||||
}
|
||||
}
|
||||
|
||||
// Preserve existing block entities and merge with new ones
|
||||
merge_compound_list(&mut chunk, chunk_to_modify, "block_entities");
|
||||
merge_compound_list(&mut chunk, chunk_to_modify, "entities");
|
||||
|
||||
// Update chunk coordinates and flags
|
||||
chunk.x_pos = chunk_x + (region_x * 32);
|
||||
chunk.z_pos = chunk_z + (region_z * 32);
|
||||
|
||||
// Create Level wrapper and save
|
||||
let level_data = create_level_wrapper(&chunk);
|
||||
ser_buffer.clear();
|
||||
fastnbt::to_writer(&mut ser_buffer, &level_data).unwrap();
|
||||
region
|
||||
.write_chunk(chunk_x as usize, chunk_z as usize, &ser_buffer)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass: ensure all chunks exist
|
||||
for chunk_x in 0..32 {
|
||||
for chunk_z in 0..32 {
|
||||
let abs_chunk_x = chunk_x + (region_x * 32);
|
||||
let abs_chunk_z = chunk_z + (region_z * 32);
|
||||
|
||||
// Check if chunk exists in our modifications
|
||||
let chunk_exists =
|
||||
region_to_modify.chunks.contains_key(&(chunk_x, chunk_z));
|
||||
|
||||
// If chunk doesn't exist, create it with base layer
|
||||
if !chunk_exists {
|
||||
let (ser_buffer, _) = Self::create_base_chunk(abs_chunk_x, abs_chunk_z);
|
||||
region
|
||||
.write_chunk(chunk_x as usize, chunk_z as usize, &ser_buffer)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
self.save_single_region(*region_x, *region_z, region_to_modify);
|
||||
|
||||
// Update progress
|
||||
let regions_done = regions_processed.fetch_add(1, Ordering::SeqCst) + 1;
|
||||
|
||||
// Update progress at regular intervals (every ~1% or at least every 10 regions)
|
||||
// This ensures progress is visible even with many regions
|
||||
// Update progress at regular intervals (every ~10% or at least every 10 regions)
|
||||
let update_interval = (total_regions / 10).max(1);
|
||||
if regions_done.is_multiple_of(update_interval) || regions_done == total_regions {
|
||||
let progress = 90.0 + (regions_done as f64 / total_regions as f64) * 9.0;
|
||||
@@ -198,6 +125,92 @@ impl<'a> WorldEditor<'a> {
|
||||
|
||||
save_pb.finish();
|
||||
}
|
||||
|
||||
/// Saves a single region to disk.
|
||||
///
|
||||
/// This is extracted to allow streaming mode to save and release regions one at a time.
|
||||
fn save_single_region(
|
||||
&self,
|
||||
region_x: i32,
|
||||
region_z: i32,
|
||||
region_to_modify: &super::common::RegionToModify,
|
||||
) {
|
||||
let mut region = self.create_region(region_x, region_z);
|
||||
let mut ser_buffer = Vec::with_capacity(8192);
|
||||
|
||||
for (&(chunk_x, chunk_z), chunk_to_modify) in ®ion_to_modify.chunks {
|
||||
if !chunk_to_modify.sections.is_empty() || !chunk_to_modify.other.is_empty() {
|
||||
// Read existing chunk data if it exists
|
||||
let existing_data = region
|
||||
.read_chunk(chunk_x as usize, chunk_z as usize)
|
||||
.unwrap()
|
||||
.unwrap_or_default();
|
||||
|
||||
// Parse existing chunk or create new one
|
||||
let mut chunk: Chunk = if !existing_data.is_empty() {
|
||||
fastnbt::from_bytes(&existing_data).unwrap()
|
||||
} else {
|
||||
Chunk {
|
||||
sections: Vec::new(),
|
||||
x_pos: chunk_x + (region_x * 32),
|
||||
z_pos: chunk_z + (region_z * 32),
|
||||
is_light_on: 0,
|
||||
other: FnvHashMap::default(),
|
||||
}
|
||||
};
|
||||
|
||||
// Update sections while preserving existing data
|
||||
let new_sections: Vec<Section> = chunk_to_modify.sections().collect();
|
||||
for new_section in new_sections {
|
||||
if let Some(existing_section) =
|
||||
chunk.sections.iter_mut().find(|s| s.y == new_section.y)
|
||||
{
|
||||
// Merge block states
|
||||
existing_section.block_states.palette = new_section.block_states.palette;
|
||||
existing_section.block_states.data = new_section.block_states.data;
|
||||
} else {
|
||||
// Add new section if it doesn't exist
|
||||
chunk.sections.push(new_section);
|
||||
}
|
||||
}
|
||||
|
||||
// Preserve existing block entities and entities and merge with new ones
|
||||
merge_compound_list(&mut chunk, chunk_to_modify, "block_entities");
|
||||
merge_compound_list(&mut chunk, chunk_to_modify, "entities");
|
||||
|
||||
// Update chunk coordinates and flags
|
||||
chunk.x_pos = chunk_x + (region_x * 32);
|
||||
chunk.z_pos = chunk_z + (region_z * 32);
|
||||
|
||||
// Create Level wrapper and save
|
||||
let level_data = create_level_wrapper(&chunk);
|
||||
ser_buffer.clear();
|
||||
fastnbt::to_writer(&mut ser_buffer, &level_data).unwrap();
|
||||
region
|
||||
.write_chunk(chunk_x as usize, chunk_z as usize, &ser_buffer)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass: ensure all chunks exist
|
||||
for chunk_x in 0..32 {
|
||||
for chunk_z in 0..32 {
|
||||
let abs_chunk_x = chunk_x + (region_x * 32);
|
||||
let abs_chunk_z = chunk_z + (region_z * 32);
|
||||
|
||||
// Check if chunk exists in our modifications
|
||||
let chunk_exists = region_to_modify.chunks.contains_key(&(chunk_x, chunk_z));
|
||||
|
||||
// If chunk doesn't exist, create it with base layer
|
||||
if !chunk_exists {
|
||||
let (ser_buffer, _) = Self::create_base_chunk(abs_chunk_x, abs_chunk_z);
|
||||
region
|
||||
.write_chunk(chunk_x as usize, chunk_z as usize, &ser_buffer)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function to get entity coordinates
|
||||
|
||||
Reference in New Issue
Block a user