27 releases (breaking)
0.31.0 | Jan 29, 2024 |
---|---|
0.30.0 | Sep 23, 2023 |
0.29.0 | Aug 14, 2023 |
0.28.0 | Apr 8, 2023 |
0.14.1 | Nov 27, 2020 |
#150 in Games
514 downloads per month
Used in 3 crates
3.5MB
14K
SLoC
fastnbt project
FastNBT is a serde serializer and deserializer for
Minecraft: Java Edition's NBT format, including
Value
type and
nbt!
macro. For
stringified NBT (sNBT) see FastSNBT.
FastAnvil allows rendering maps of worlds, and a
Region
for
using the Region file format. Supports 1.20 down to 1.13 inclusive, and slightly
flaky support for 1.12.
An in-browser Rust-to-WASM powered Minecraft map renderer demo is below.
Demos
Demo of Hermitcraft season 8 and more at owengage.com/anvil
The anvil
binary from fastnbt-tools
can render your world leveraging all of
your CPU.
Examples
A bunch of examples can be found in
fastnbt/examples
,
fastanvil/examples
and tools/src
. Some examples are recreated below.
Example: editing level.dat
The following edits the world spawn to 250, 200, 250 (probably not a good idea!). Full example in fastnbt/examples directory.
#[derive(Serialize, Deserialize)]
struct LevelDat {
#[serde(rename = "Data")]
data: Data,
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
struct Data {
spawn_x: i32,
spawn_y: i32,
spawn_z: i32,
#[serde(flatten)]
other: HashMap<String, Value>,
}
fn main() {
let args: Vec<_> = std::env::args_os().collect();
let file = std::fs::File::open(&args[1]).unwrap();
let mut decoder = GzDecoder::new(file);
let mut bytes = vec![];
decoder.read_to_end(&mut bytes).unwrap();
let mut leveldat: LevelDat = fastnbt::from_bytes(&bytes).unwrap();
leveldat.data.spawn_x = 250;
leveldat.data.spawn_y = 200;
leveldat.data.spawn_z = 250;
let new_bytes = fastnbt::to_bytes(&leveldat).unwrap();
let outfile = std::fs::File::create("level.dat").unwrap();
let mut encoder = GzEncoder::new(outfile, Compression::fast());
encoder.write_all(&new_bytes).unwrap();
}
Example: print player inventory
This example demonstrates printing out a players inventory and ender chest contents from the player dat files found in worlds. We
- use serde's renaming attribute to have rustfmt conformant field names,
- use lifetimes to save on string allocations, and
- use the
Value
type to deserialize a field we don't specify the exact structure of.
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
struct PlayerDat<'a> {
data_version: i32,
#[serde(borrow)]
inventory: Vec<InventorySlot<'a>>,
ender_items: Vec<InventorySlot<'a>>,
}
#[derive(Deserialize, Debug)]
struct InventorySlot<'a> {
id: &'a str, // We avoid allocating a string here.
tag: Option<Value>, // Also get the less structured properties of the object.
// We need to rename fields a lot.
#[serde(rename = "Count")]
count: i8,
}
fn main() {
let args: Vec<_> = std::env::args().skip(1).collect();
let file = std::fs::File::open(args[0].clone()).unwrap();
// Player dat files are compressed with GZip.
let mut decoder = GzDecoder::new(file);
let mut data = vec![];
decoder.read_to_end(&mut data).unwrap();
let player: Result<PlayerDat> = from_bytes(data.as_slice());
println!("{:#?}", player);
}
Usage
For the libraries
[dependencies]
fastnbt = "2"
fastanvil = "0.31"
For the anvil
executable
cargo install fastnbt-tools
lib.rs
:
For handling Minecraft's region format, Anvil. This crate is mostly to
support creating maps of Minecraft worlds and is not stable (per-1.0). The
Region
struct is probably the most generally useful part in this crate.
This crate also contains a JavaChunk
that allows deserializing 1.18
down to about 1.15 chunks into some structs. This doesn't record all
information from a chunk however, eg entities are lost. It is not suitable
for serializing back into a region.
You can create your own chunk structures to (de)serialize using fastnbt
.
Region
can be given a Read
, Write
and Seek
type eg a file in
order to read and write chunk data.
Crate features
- render - This feature is enabled by default and encapsulates all world-rendering related functionality.
Dependencies
~1.8–9.5MB
~101K SLoC