Skip to content

Commit

Permalink
minecraft: Update DDA crate and some minor improvements (#560)
Browse files Browse the repository at this point in the history
  • Loading branch information
ciscorn authored Jun 5, 2024
1 parent 5060942 commit d49dcf2
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 87 deletions.
2 changes: 1 addition & 1 deletion nusamai/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ flate2 = "1.0.28"
chrono = "0.4.35"
kv-extsort = { git = "https://github.com/MIERUNE/kv-extsort-rs.git" }
bytemuck = { version = "1.16.0", features = ["derive"] }
dda-voxelize = "0.0.1"
dda-voxelize = "0.1.0"

[dev-dependencies]
rand = "0.8.5"
Expand Down
4 changes: 2 additions & 2 deletions nusamai/src/sink/minecraft/block_colors.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::collections::HashMap;

pub fn get_typename_block() -> HashMap<&'static str, &'static str> {
pub fn get_block_for_typename() -> HashMap<&'static str, &'static str> {
let mut typename_blocks: HashMap<&'static str, &'static str> = HashMap::new();

typename_blocks.insert("bldg:Building", "iron_block");
Expand Down Expand Up @@ -31,7 +31,7 @@ mod tests {

#[test]
fn test_get_typename_block() {
let typename_blocks = get_typename_block();
let typename_blocks = get_block_for_typename();
assert_eq!(typename_blocks.get("bldg:Building"), Some(&"iron_block"));
assert_eq!(typename_blocks.get("tran:Road"), Some(&"gray_wool"));
assert_eq!(typename_blocks.get("tran:Railway"), Some(&"granite"));
Expand Down
25 changes: 11 additions & 14 deletions nusamai/src/sink/minecraft/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use crate::{
sink::{DataRequirements, DataSink, DataSinkProvider, SinkInfo},
};

use block_colors::get_typename_block;
use block_colors::get_block_for_typename;
use level::{Data, Level};
use region::{write_anvil, BlockData, ChunkData, Position2D, RegionData, SectionData, WorldData};

Expand Down Expand Up @@ -152,7 +152,7 @@ impl DataSink for MinecraftSink {
&nusamai_projection::ellipsoid::grs80(),
);

let typename_block = get_typename_block();
let typename_block = get_block_for_typename();

let (ra, rb) = rayon::join(
|| {
Expand All @@ -168,8 +168,6 @@ impl DataSink for MinecraftSink {
return Ok(());
};

let typename = &obj.typename.as_ref().to_string();

let ObjectStereotype::Feature { geometries, .. } = &obj.stereotype else {
return Ok(());
};
Expand All @@ -186,7 +184,7 @@ impl DataSink for MinecraftSink {
.iter()
.map(|v| match projection.project_forward(v[0], v[1], v[2]) {
// To match the Minecraft coordinate system, the y-coordinate is multiplied by -1 and replaced with z
Ok((x, y, z)) => [x, z, y * -1.0],
Ok((x, y, z)) => [x, z, -y],
Err(e) => {
println!("conversion error: {:?}", e);
[f64::NAN, f64::NAN, f64::NAN]
Expand Down Expand Up @@ -244,7 +242,7 @@ impl DataSink for MinecraftSink {
}
});

typename_map.insert(typename.clone(), voxelizer);
typename_map.insert(obj.typename.to_string(), voxelizer);

if sender.send(typename_map).is_err() {
return Err(PipelineError::Canceled);
Expand Down Expand Up @@ -323,7 +321,7 @@ impl DataSink for MinecraftSink {

world_data[region_index].chunks[chunk_index].sections[section_index]
.blocks
.push(block_data.unwrap());
.push(block_data);
});
});
});
Expand All @@ -332,29 +330,28 @@ impl DataSink for MinecraftSink {
file_path.push("region");
std::fs::create_dir_all(&file_path)?;

let _ = world_data.iter().try_for_each(|region| -> Result<()> {
world_data.iter().try_for_each(|region| -> Result<()> {
feedback.ensure_not_canceled()?;

write_anvil(region, &file_path)?;

Ok(())
});
})?;

// write level.dat
let dir_name = self.output_path.file_name().unwrap().to_str().unwrap();
let dir_name = self.output_path.file_name().unwrap().to_string_lossy();

// Set the entered directory name as the level name
let data = Data {
level_name: dir_name.to_string(),
..Default::default()
};
let level_dat = fastnbt::to_value(Level { data }).unwrap();

let bytes = fastnbt::to_bytes(&level_dat).unwrap();

let level_dat_file = std::fs::File::create(self.output_path.join("level.dat"))?;
let mut encoder = GzEncoder::new(level_dat_file, Compression::fast());
encoder.write_all(&bytes).unwrap();

let bytes = fastnbt::to_bytes(&Level { data }).unwrap();
encoder.write_all(&bytes)?;

Ok(())
},
Expand Down
137 changes: 67 additions & 70 deletions nusamai/src/sink/minecraft/region.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::sync::{Arc, Mutex};
use std::{fs::File, path::Path, path::PathBuf};
use std::{fs::File, path::Path};

use fastanvil::Region;
use fastanvil::{Error, Region};
use fastnbt::{to_bytes, LongArray};
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
Expand All @@ -20,11 +20,11 @@ pub struct BlockId {
}

impl BlockId {
pub fn new(block_name: String) -> Result<Self> {
Ok(BlockId {
pub fn new(block_name: String) -> Self {
BlockId {
name_space: "minecraft".to_string(),
block_name,
})
}
}

#[inline]
Expand All @@ -40,15 +40,13 @@ impl BlockId {

impl BlockPosition {
// Check that the input is in the range 0~15
pub fn new(x: u8, y: u8, z: u8) -> Result<Self> {
pub fn new(x: u8, y: u8, z: u8) -> Self {
if x > 15 || y > 15 || z > 15 {
Err(PipelineError::Other(format!(
"Invalid BlockPosition values: x={}, y={}, z={}. The position values must be within the range of 0 to 15.",
x, y, z
)))
} else {
Ok(BlockPosition([x, y, z]))
panic!(
"Invalid BlockPosition values: x={x}, y={y}, z={z}. The position values must be within the range of 0 to 15."
);
}
BlockPosition([x, y, z])
}
}

Expand All @@ -59,10 +57,10 @@ pub struct BlockData {
}

impl BlockData {
pub fn new(x: u8, y: u8, z: u8, block_name: String) -> Result<Self> {
let position = BlockPosition::new(x, y, z)?;
let block_id = BlockId::new(block_name)?;
Ok(BlockData { position, block_id })
pub fn new(x: u8, y: u8, z: u8, block_name: String) -> Self {
let position = BlockPosition::new(x, y, z);
let block_id = BlockId::new(block_name);
BlockData { position, block_id }
}
}
#[derive(Deserialize, Serialize, Debug)]
Expand All @@ -86,17 +84,23 @@ pub type WorldData = Vec<RegionData>;

#[derive(Serialize, Deserialize, Debug)]
struct Chunk {
/// The status of the chunk, such as whether it is fully generated or being generated.
#[serde(rename = "Status")]
status: String, // The status of the chunk, such as whether it is fully generated or being generated.
status: String,
/// The Z coordinate of the chunk (absolute value).
#[serde(rename = "zPos")]
z_pos: i32, // The Z coordinate of the chunk (absolute value).
z_pos: i32,
/// The Y coordinate of the lowest section in the chunk.
#[serde(rename = "yPos")]
y_pos: i32, // The Y coordinate of the lowest section in the chunk.
y_pos: i32,
/// The X coordinate of the chunk (absolute value).
#[serde(rename = "xPos")]
x_pos: i32, // The X coordinate of the chunk (absolute value).
sections: Vec<Section>, // A vector containing the sections that make up the chunk.
x_pos: i32,
/// A vector containing the sections that make up the chunk.
sections: Vec<Section>,
/// The version of the data format used to store this chunk.
#[serde(rename = "DataVersion")]
data_version: u32, // The version of the data format used to store this chunk.
data_version: u32,
}

impl Default for Chunk {
Expand Down Expand Up @@ -179,13 +183,10 @@ impl PaletteItem {
}

pub fn write_anvil(region_data: &RegionData, file_path: &Path) -> Result<()> {
let out_path = PathBuf::from(format!(
"{}/r.{}.{}.mca",
file_path.display(),
region_data.position[0],
region_data.position[1]
let out_path = file_path.join(format!(
"r.{}.{}.mca",
region_data.position[0], region_data.position[1]
));

let out_file = File::options()
.read(true)
.write(true)
Expand All @@ -197,34 +198,30 @@ pub fn write_anvil(region_data: &RegionData, file_path: &Path) -> Result<()> {
// Create a empty region object
let empty_region = Arc::new(Mutex::new(Region::new(out_file).unwrap()));

(0..32).into_par_iter().for_each(|chunk_z| {
(0..32).into_par_iter().for_each(|chunk_x| {
let absolute_chunk_x = region_data.position[0] * 32 + chunk_x;
let absolute_chunk_z = region_data.position[1] * 32 + chunk_z;

let chunk_data = region_data
.chunks
.iter()
.find(|c| c.position == [absolute_chunk_x, absolute_chunk_z]);

let chunk = create_chunk_structure(absolute_chunk_x, absolute_chunk_z, chunk_data);
if let Err(e) = region_data.chunks.par_iter().try_for_each(|chunk_data| {
let [absolute_chunk_x, absolute_chunk_z] = chunk_data.position;
let chunk_x = absolute_chunk_x - region_data.position[0] * 32;
let chunk_z = absolute_chunk_z - region_data.position[1] * 32;

let serialized_chunk = to_bytes(&chunk).unwrap();
let chunk = create_chunk_structure(absolute_chunk_x, absolute_chunk_z, Some(chunk_data));
let serialized_chunk = to_bytes(&chunk).unwrap();

{
// Write the chunk data to the region file
let mut region = empty_region.lock().unwrap();
region
.write_chunk(chunk_x as usize, chunk_z as usize, &serialized_chunk)
.unwrap();
}
});
});
{
// Write the chunk data to the region file
let mut region = empty_region.lock().unwrap();
region.write_chunk(chunk_x as usize, chunk_z as usize, &serialized_chunk)
}
}) {
return match e {
Error::IO(e) => Err(e.into()),
e => Err(PipelineError::Other(e.to_string())),
};
};

Ok(())
}

// Function to calculate the number of bits and data size of a palette.
/// Calculate the number of bits and data size of a palette.
fn calculate_bits_and_size(palette_len: usize) -> (u32, u32) {
let bits_per_block = match palette_len {
0..=16 => 4,
Expand All @@ -235,7 +232,7 @@ fn calculate_bits_and_size(palette_len: usize) -> (u32, u32) {
(bits_per_block, data_size)
}

// Functions to create sections
/// Create sections
fn create_chunk_section(
blocks: &[BlockData],
palette: &mut Vec<PaletteItem>,
Expand Down Expand Up @@ -290,7 +287,7 @@ fn create_chunk_section(
)
}

// Functions to create chunk structures
/// Create chunk structures
fn create_chunk_structure(chunk_x: i32, chunk_z: i32, chunk_data: Option<&ChunkData>) -> Chunk {
let palette = vec![PaletteItem::new("minecraft:air".to_string(), None)];

Expand Down Expand Up @@ -371,7 +368,7 @@ mod tests {
for x in 0..16 {
for y in 0..16 {
for z in 0..16 {
blocks.push(BlockData::new(x, y, z, "stone".to_string()).unwrap());
blocks.push(BlockData::new(x, y, z, "stone".to_string()));
}
}
}
Expand All @@ -394,22 +391,22 @@ mod tests {

fn test_palette_size_17() {
let blocks = vec![
BlockData::new(0, 0, 0, "white_wool".to_string()).unwrap(),
BlockData::new(1, 0, 0, "light_gray_wool".to_string()).unwrap(),
BlockData::new(2, 0, 0, "gray_wool".to_string()).unwrap(),
BlockData::new(3, 0, 0, "black_wool".to_string()).unwrap(),
BlockData::new(4, 0, 0, "brown_wool".to_string()).unwrap(),
BlockData::new(5, 0, 0, "red_wool".to_string()).unwrap(),
BlockData::new(6, 0, 0, "orange_wool".to_string()).unwrap(),
BlockData::new(7, 0, 0, "yellow_wool".to_string()).unwrap(),
BlockData::new(8, 0, 0, "lime_wool".to_string()).unwrap(),
BlockData::new(9, 0, 0, "green_wool".to_string()).unwrap(),
BlockData::new(10, 0, 0, "cyan_wool".to_string()).unwrap(),
BlockData::new(11, 0, 0, "light_blue_wool".to_string()).unwrap(),
BlockData::new(12, 0, 0, "blue_wool".to_string()).unwrap(),
BlockData::new(13, 0, 0, "purple_wool".to_string()).unwrap(),
BlockData::new(14, 0, 0, "magenta_wool".to_string()).unwrap(),
BlockData::new(15, 0, 0, "pink_wool".to_string()).unwrap(),
BlockData::new(0, 0, 0, "white_wool".to_string()),
BlockData::new(1, 0, 0, "light_gray_wool".to_string()),
BlockData::new(2, 0, 0, "gray_wool".to_string()),
BlockData::new(3, 0, 0, "black_wool".to_string()),
BlockData::new(4, 0, 0, "brown_wool".to_string()),
BlockData::new(5, 0, 0, "red_wool".to_string()),
BlockData::new(6, 0, 0, "orange_wool".to_string()),
BlockData::new(7, 0, 0, "yellow_wool".to_string()),
BlockData::new(8, 0, 0, "lime_wool".to_string()),
BlockData::new(9, 0, 0, "green_wool".to_string()),
BlockData::new(10, 0, 0, "cyan_wool".to_string()),
BlockData::new(11, 0, 0, "light_blue_wool".to_string()),
BlockData::new(12, 0, 0, "blue_wool".to_string()),
BlockData::new(13, 0, 0, "purple_wool".to_string()),
BlockData::new(14, 0, 0, "magenta_wool".to_string()),
BlockData::new(15, 0, 0, "pink_wool".to_string()),
];

let mut palette = vec![PaletteItem::new("minecraft:air".to_string(), None)];
Expand All @@ -429,7 +426,7 @@ mod tests {
position: [0, 0],
sections: vec![SectionData {
y: 0,
blocks: vec![BlockData::new(0, 0, 0, "stone".to_string()).unwrap()],
blocks: vec![BlockData::new(0, 0, 0, "stone".to_string())],
}],
};

Expand Down

0 comments on commit d49dcf2

Please sign in to comment.