valence/valence_anvil/benches/world_parsing.rs
Terminator 6de5de57a5
Anvil file support (blocks and biomes) (#145)
Adds the `valence_anvil` crate for loading anvil worlds. It can only read blocks and biomes currently. Support for saving data is to be added later.

Co-authored-by: Ryan <ryanj00a@gmail.com>
2022-12-26 06:55:52 -08:00

138 lines
4.2 KiB
Rust

use std::fs::create_dir_all;
use std::path::{Path, PathBuf};
use anyhow::{ensure, Context};
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use fs_extra::dir::CopyOptions;
use reqwest::IntoUrl;
use valence::chunk::UnloadedChunk;
use valence_anvil::AnvilWorld;
use zip::ZipArchive;
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
fn criterion_benchmark(c: &mut Criterion) {
let world_dir = get_world_asset(
"https://github.com/valence-rs/valence-test-data/archive/refs/heads/asset/sp_world_1.19.2.zip",
"1.19.2 benchmark world",
true
).expect("failed to get world asset");
let mut world = AnvilWorld::new(world_dir);
c.bench_function("Load square 10x10", |b| {
b.iter(|| {
let world = black_box(&mut world);
for z in -5..5 {
for x in -5..5 {
let nbt = world
.read_chunk(x, z)
.expect("failed to read chunk")
.expect("missing chunk at position")
.data;
let mut chunk = UnloadedChunk::new(24);
valence_anvil::to_valence(&nbt, &mut chunk, 4, |_| Default::default()).unwrap();
black_box(chunk);
}
}
});
});
}
/// Loads the asset. If the asset is already present on the system due to a
/// prior run, the cached asset is used instead. If the asset is not
/// cached yet, this function downloads the asset using the current thread.
/// This will block until the download is complete.
///
/// returns: `PathBuf` The reference to the asset on the file system
fn get_world_asset(
url: impl IntoUrl,
dest_path: impl AsRef<Path>,
remove_top_level_dir: bool,
) -> anyhow::Result<PathBuf> {
let url = url.into_url()?;
let dest_path = dest_path.as_ref();
let asset_cache_dir = Path::new(".asset_cache");
create_dir_all(asset_cache_dir).context("unable to create `.asset_cache` directory")?;
let final_path = asset_cache_dir.join(dest_path);
if final_path.exists() {
return Ok(final_path);
}
let mut response = reqwest::blocking::get(url.clone())?;
let cache_download_directory = asset_cache_dir.join("downloads");
create_dir_all(&cache_download_directory)
.context("unable to create `.asset_cache/downloads` directory")?;
let mut downloaded_zip_file =
tempfile::tempfile_in(&cache_download_directory).context("Could not create temp file")?;
println!("Downloading {dest_path:?} from {url}");
response
.copy_to(&mut downloaded_zip_file)
.context("could not write web contents to the temporary file")?;
let mut zip_archive = ZipArchive::new(downloaded_zip_file)
.context("unable to create zip archive from downloaded content")?;
if !remove_top_level_dir {
zip_archive
.extract(&final_path)
.context("unable to unzip downloaded contents")?;
return Ok(final_path);
}
let temp_dir = tempfile::tempdir_in(&cache_download_directory)
.context("unable to create temporary directory in `.asset_cache`")?;
zip_archive
.extract(&temp_dir)
.context("unable to unzip downloaded contents")?;
let mut entries = temp_dir.path().read_dir()?;
let top_level_dir = entries
.next()
.context("the downloaded zip file was empty")??;
ensure!(
entries.next().is_none(),
"found more than one entry in the top level directory of the Zip file"
);
ensure!(
top_level_dir.path().is_dir(),
"the only content in the zip archive is a file"
);
create_dir_all(&final_path).context("could not create a directory inside the asset cache")?;
let dir_entries = top_level_dir
.path()
.read_dir()?
.collect::<Result<Vec<_>, _>>()?;
let items_to_move: Vec<_> = dir_entries.into_iter().map(|d| d.path()).collect();
fs_extra::move_items(&items_to_move, &final_path, &CopyOptions::new())?;
// We keep the temporary directory around until we're done moving files out
// of it.
drop(temp_dir);
Ok(final_path)
}