fix saving of worlds

This commit is contained in:
T-x-T 2025-07-23 21:58:06 +02:00
parent b97b72cac5
commit bef0d034d1
3 changed files with 1679 additions and 34 deletions

File diff suppressed because it is too large Load diff

View file

@ -11,7 +11,8 @@ fn main() {
//do_blocks();
//do_block_types();
//do_properties();
do_get_block_state_id_from_raw();
//do_get_block_state_id_from_raw();
do_get_raw_properties_from_block_state_id();
}
fn do_items() {
@ -52,7 +53,7 @@ fn do_blocks() {
String::new()
};
println!("\t\tfn add_{}(map: &mut HashMap<String, Block>) {{", convert_to_upper_camel_case(key).to_lowercase());
println!("\tfn add_{}(map: &mut HashMap<String, Block>) {{", convert_to_upper_camel_case(key).to_lowercase());
println!("\t\tlet mut block = Block {{ block_type: Type::{block_type}, properties: vec![{properties}], states: vec![] }};");
for x in block["states"].as_array().unwrap().iter() {
println!("\t\tblock.states.push(State {{ id: {}, properties: vec![ {}], default: {} }});", x.as_object().unwrap()["id"].as_i32().unwrap(), x.as_object().unwrap()["properties"].as_object().unwrap_or(jzon::object! {}.as_object().unwrap()).iter().map(|y| format!("Property::{}{}({}{}::{}),", block_type, convert_to_upper_camel_case(y.0), block_type, convert_to_upper_camel_case(y.0), if (u8::MIN..u8::MAX).map(|z| z.to_string()).collect::<Vec<String>>().contains(&y.1.as_str().unwrap().to_string()) { format!("Num{}", convert_to_upper_camel_case(y.1.as_str().unwrap())) } else { convert_to_upper_camel_case(y.1.as_str().unwrap()) } )).collect::<String>(), if x.as_object().unwrap()["default"].is_boolean() { "true" } else { "false" } )
@ -180,6 +181,51 @@ fn do_get_block_state_id_from_raw() {
println!("}}");
}
fn do_get_raw_properties_from_block_state_id() {
let blocks_file = std::fs::read_to_string("../official_server/generated/reports/blocks.json").expect("failed to read blocks.json report");
let blocks_json = jzon::parse(&blocks_file).expect("failed to parse blocks.json report");
let mut block_types: Vec<String> = Vec::new();
for x in blocks_file.lines(){
if x.trim().starts_with("\"type\":") {
block_types.push(convert_to_upper_camel_case(&x.trim().replace("\"type\": \"", "").replace("\",", "")));
}
}
block_types.sort();
block_types.dedup();
let block_types: Vec<String> = block_types.into_iter().filter(|x| x != "\"type\": [").collect();
//The key is the type and then the property, because properties can have different values depending on their type
let mut properties: HashMap<(String, String), Vec<String>> = HashMap::new();
for block in blocks_json.as_object().unwrap().iter() {
if !block.1["properties"].is_object() {
continue;
}
for property in block.1["properties"].as_object().unwrap().iter() {
let property_entry = format!("{}{}", convert_to_upper_camel_case(block.1["definition"]["type"].as_str().unwrap()), convert_to_upper_camel_case(property.0));
properties.entry((convert_to_upper_camel_case(block.1["definition"]["type"].as_str().unwrap()), property.0.to_string())).or_insert(property.1.as_array().unwrap().iter().map(|x| x.as_str().unwrap().to_string()).collect());
}
}
println!("pub fn get_raw_properties_from_block_state_id(block_states: &HashMap<String, Block>, block_state_id: u16) -> Vec<(String, String)> {{");
println!("\tlet state = block_states.iter().find(|x| x.1.states.iter().any(|x| x.id == block_state_id)).unwrap().1.states.iter().find(|x| x.id == block_state_id).unwrap().clone();");
println!("\tlet mut output: Vec<(String, String)> = Vec::new();\n");
println!("\tfor property in state.properties {{");
println!("\t\tmatch property {{");
for property in properties {
let enum_variant = convert_to_upper_camel_case(&format!("{}{}", property.0.0, convert_to_upper_camel_case(&property.0.1)));
for option in property.1 {
let variant = if (u8::MIN..u8::MAX).map(|z| z.to_string()).collect::<Vec<String>>().contains(&option) {format!("Num{}", convert_to_upper_camel_case(&option))} else {convert_to_upper_camel_case(&option)};
println!("\t\t\tProperty::{enum_variant}({enum_variant}::{variant}) => output.push((\"{}\".to_string(), \"{option}\".to_string())),", property.0.1);
}
}
println!("\t\t}}");
println!("\t}}");
println!("\treturn output;");
println!("}}");
}
fn convert_to_upper_camel_case(input: &str) -> String {
let mut found_underscore = false;
return input

View file

@ -231,7 +231,11 @@ fn save_region_to_disk(region: (i32, i32), chunks: &[&Chunk], path: PathBuf) {
_ => 6,
};
let block_palette: Vec<u16> = section.blocks.iter().copied().collect::<HashSet<u16>>().into_iter().collect();
let block_palette: Vec<u16> = if section.blocks.is_empty() {
vec![0]
} else {
section.blocks.iter().copied().collect::<HashSet<u16>>().into_iter().collect()
};
let blocks_bits_per_entry = match block_palette.len() {
0..=16 => 4,
17..=32 => 5,
@ -243,45 +247,84 @@ fn save_region_to_disk(region: (i32, i32), chunks: &[&Chunk], path: PathBuf) {
1025..=2048 => 11,
_ => 12,
};
//TODO: handle only one block/biome with no data array
NbtTag::TagCompound(None, vec![
NbtTag::Byte(Some("Y".to_string()), (i as i8 - 4) as u8),
NbtTag::ByteArray(Some("BlockLight".to_string()), section.block_lights.clone()),
NbtTag::ByteArray(Some("SkyLight".to_string()), section.sky_lights.clone()),
NbtTag::TagCompound(Some("biomes".to_string()), vec![
NbtTag::List(Some("palette".to_string()), biome_palette.iter().map(|biome| {
let mut biome_data = vec![
NbtTag::List(Some("palette".to_string()), biome_palette.iter().map(|biome| {
NbtTag::String(None, data::biomes::get_biome_ids().into_iter().find(|(_, biome_id)| *biome_id == *biome).unwrap().0)
}).collect()),
NbtTag::LongArray(Some("data".to_string()), section.biomes.iter().map(|biome| {
*biome_palette.iter().find(|x| **x == *biome).unwrap()
}).collect::<Vec<u8>>().chunks(64/biomes_bits_per_entry).map(|byte_arr| {
let mut long = 0u64;
for byte in byte_arr {
long <<= biomes_bits_per_entry;
long &= *byte as u64;
}
return long as i64;
}).collect()
)]),
NbtTag::TagCompound(Some("block_states".to_string()), vec![
NbtTag::List(Some("palette".to_string()), block_palette.iter().map(|blockstate_id| {
NbtTag::TagCompound(None, vec![
NbtTag::String(Some("Name".to_string()), all_blocks.iter().find(|x| x.1.states.iter().any(|x| x.id == *blockstate_id)).unwrap().0.clone()),
//TODO: also add properties here
])
}).collect()),
}).collect())
];
if biome_palette.len() > 1 {
biome_data.push(
NbtTag::LongArray(Some("data".to_string()), section.biomes.iter().map(|biome| {
biome_palette.iter().enumerate().find(|x| *x.1 == *biome).unwrap().0 as u8
}).collect::<Vec<u8>>().chunks(64/biomes_bits_per_entry).map(|byte_arr| {
let mut long = 0u64;
for byte in byte_arr {
long >>= biomes_bits_per_entry;
long += (*byte as u64) << (64-biomes_bits_per_entry);
}
if 64/biomes_bits_per_entry > byte_arr.len() {
long >>= (64/biomes_bits_per_entry - byte_arr.len()) * biomes_bits_per_entry;
}
long >>= 64 % biomes_bits_per_entry;
return long as i64;
}).collect())
)
}
let mut block_data = vec![
NbtTag::List(Some("palette".to_string()), block_palette.iter().map(|blockstate_id| {
let block = all_blocks.iter().find(|x| x.1.states.iter().any(|x| x.id == *blockstate_id)).unwrap();
let mut children = vec![
NbtTag::String(Some("Name".to_string()), block.0.clone()),
];
if block.1.states.len() > 1 {
children.push(
NbtTag::TagCompound(Some("Properties".to_string()), data::blocks::get_raw_properties_from_block_state_id(&all_blocks, *blockstate_id).into_iter().map(|x| {
NbtTag::String(Some(x.0), x.1)
}).collect())
);
}
NbtTag::TagCompound(None, children)
}).collect())
];
if block_palette.len() > 1 {
block_data.push(
NbtTag::LongArray(Some("data".to_string()), section.blocks.iter().map(|block| {
block_palette.iter().enumerate().find(|x| *x.1 == *block).unwrap().0 as u8
}).collect::<Vec<u8>>().chunks(64/blocks_bits_per_entry).map(|byte_arr| {
let mut long = 0u64;
for byte in byte_arr {
long <<= blocks_bits_per_entry;
long += *byte as u64;
long >>= blocks_bits_per_entry;
long += (*byte as u64) << (64-blocks_bits_per_entry);
}
if 64/blocks_bits_per_entry > byte_arr.len() {
long >>= (64/blocks_bits_per_entry - byte_arr.len()) * blocks_bits_per_entry;
}
long >>= 64 % blocks_bits_per_entry;
return long as i64;
}).collect()
)]),
])
}).collect())
);
}
let mut nbt_arr = vec![
NbtTag::Byte(Some("Y".to_string()), (i as i8 - 4) as u8),
NbtTag::TagCompound(Some("biomes".to_string()), biome_data),
NbtTag::TagCompound(Some("block_states".to_string()), block_data),
];
if !section.block_lights.is_empty() {
nbt_arr.push(NbtTag::ByteArray(Some("BlockLight".to_string()), section.block_lights.clone()));
}
if !section.sky_lights.is_empty() {
nbt_arr.push(NbtTag::ByteArray(Some("SkyLight".to_string()), section.sky_lights.clone()));
}
return NbtTag::TagCompound(None, nbt_arr);
}).collect()),
]);
@ -310,6 +353,8 @@ fn save_region_to_disk(region: (i32, i32), chunks: &[&Chunk], path: PathBuf) {
let mut first_chunk = true;
let mut last_chunk_offset = 0;
let mut last_chunk_len = 0;
#[allow(clippy::needless_range_loop)] //tried to implement this but it broke so idk man
for i in 0..locations_table.len() {
if locations_table[i].1 == 0 {
locations_table[i] = (0, 0);