(self, save: &impl OdbReader, storage: &mut impl OdbWriter)
| 26 | } |
| 27 | |
| 28 | fn flatten(self, save: &impl OdbReader, storage: &mut impl OdbWriter) -> Result<Vec<String>> { |
| 29 | let mut processed = Vec::new(); |
| 30 | for pattern in FLATTEN_PATTERNS { |
| 31 | for key in save.glob(pattern)? { |
| 32 | // Parse region file |
| 33 | log::info!("Process chunk region file {key}"); |
| 34 | let data = save.get(&key)?; |
| 35 | let filename = key.split('/').next_back().unwrap_or(""); |
| 36 | let (region_x, region_z) = parse_xz(filename) |
| 37 | .with_context(|| format!("failed to parse region coordinates from {key}"))?; |
| 38 | let Some((timestamp_header, chunks)) = |
| 39 | read_region(Cursor::new(data), region_x, region_z) |
| 40 | .with_context(|| format!("failed to read region from {key}"))? |
| 41 | else { |
| 42 | processed.push(key); |
| 43 | continue; |
| 44 | }; |
| 45 | // Each section carries its own local palette, so chunks can be |
| 46 | // processed independently in parallel without a global mapping pass. |
| 47 | let mut result = chunks |
| 48 | .into_par_iter() |
| 49 | .map(|(chunk_x, chunk_z, nbt)| { |
| 50 | let nbt = |
| 51 | load_nbt(Cursor::new(&nbt)).context("failed to load chunk nbt")?; |
| 52 | if nbt |
| 53 | .string("Status") |
| 54 | .context("missing 'Status' field in chunk nbt")? |
| 55 | .to_string_lossy() |
| 56 | != "minecraft:full" |
| 57 | { |
| 58 | return Ok(None); |
| 59 | } |
| 60 | let (other, sections) = split_chunk(nbt).with_context(|| { |
| 61 | format!("failed to process chunk ({chunk_x}, {chunk_z}) at file {key}") |
| 62 | })?; |
| 63 | |
| 64 | // Extract InhabitedTime and LastUpdate from other (will store in timestamps) |
| 65 | let name = other.name().to_owned(); |
| 66 | let mut other_compound = other.as_compound(); |
| 67 | let inhabited_time = other_compound |
| 68 | .remove("InhabitedTime") |
| 69 | .and_then(simdnbt::owned::NbtTag::into_long) |
| 70 | .context("missing 'InhabitedTime' field")?; |
| 71 | let last_update = other_compound |
| 72 | .remove("LastUpdate") |
| 73 | .and_then(simdnbt::owned::NbtTag::into_long) |
| 74 | .context("missing 'LastUpdate' field")?; |
| 75 | let other = simdnbt::owned::BaseNbt::new(name, other_compound); |
| 76 | |
| 77 | let other = sort_nbt(other); |
| 78 | let mut sections_dump = Vec::with_capacity(200 * 1024); |
| 79 | sections.to_nbt().write(&mut sections_dump); |
| 80 | Ok(Some(( |
| 81 | chunk_x, |
| 82 | chunk_z, |
| 83 | other, |
| 84 | sections_dump, |
| 85 | inhabited_time, |
no test coverage detected