Skip to content

Commit

Permalink
adapt to new nbtx lib #6
Browse files Browse the repository at this point in the history
  • Loading branch information
theaddonn committed Sep 15, 2024
1 parent 2a80d8f commit 308aec5
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 242 deletions.
30 changes: 14 additions & 16 deletions crates/world/src/level_dat/abilities.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use std::collections::HashMap;

use bedrockrs_nbt as nbt;

use bedrockrs_shared::world::permissions_level::PermissionLevel;

use crate::error::WorldError;
Expand Down Expand Up @@ -55,13 +53,13 @@ pub struct LevelDatAbilities {

impl LevelDatAbilities {
/// Parses a given [`nbt::Value`] into a [`LevelDatAbilities`] object.
pub fn parse(tag: nbt::Value) -> Result<Self, WorldError> {
pub fn parse(tag: nbtx::Value) -> Result<Self, WorldError> {
fn get_byte_as_bool(
map: &mut HashMap<String, nbt::Value>,
map: &mut HashMap<String, nbtx::Value>,
key: &str,
) -> Result<bool, WorldError> {
match map.remove(key) {
Some(nbt::Value::Byte(v)) => Ok(v != 0),
Some(nbtx::Value::Byte(v)) => Ok(v != 0),
Some(other) => Err(WorldError::FormatError(format!(
"Expected `{}` in LevelDat abilities to be of type Byte, got {:?}",
key, other
Expand All @@ -74,11 +72,11 @@ impl LevelDatAbilities {
}

fn get_byte_as_bool_option(
map: &mut HashMap<String, nbt::Value>,
map: &mut HashMap<String, nbtx::Value>,
key: &str,
) -> Result<Option<bool>, WorldError> {
match map.remove(key) {
Some(nbt::Value::Byte(v)) => Ok(Some(v != 0)),
Some(nbtx::Value::Byte(v)) => Ok(Some(v != 0)),
Some(other) => Err(WorldError::FormatError(format!(
"Expected `{}` in LevelDat abilities to be of type Byte, got {:?}",
key, other
Expand All @@ -87,9 +85,9 @@ impl LevelDatAbilities {
}
}

fn get_int32(map: &mut HashMap<String, nbt::Value>, key: &str) -> Result<i32, WorldError> {
fn get_int32(map: &mut HashMap<String, nbtx::Value>, key: &str) -> Result<i32, WorldError> {
match map.remove(key) {
Some(nbt::Value::Int(v)) => Ok(v),
Some(nbtx::Value::Int(v)) => Ok(v),
Some(other) => Err(WorldError::FormatError(format!(
"Expected `{}` in LevelDat abilities to be of type Int32, got {:?}",
key, other
Expand All @@ -102,11 +100,11 @@ impl LevelDatAbilities {
}

fn get_int32_option(
map: &mut HashMap<String, nbt::Value>,
map: &mut HashMap<String, nbtx::Value>,
key: &str,
) -> Result<Option<i32>, WorldError> {
match map.remove(key) {
Some(nbt::Value::Int(v)) => Ok(Some(v)),
Some(nbtx::Value::Int(v)) => Ok(Some(v)),
Some(other) => Err(WorldError::FormatError(format!(
"Expected `{}` in LevelDat abilities to be of type Int32, got {:?}",
key, other
Expand All @@ -115,9 +113,9 @@ impl LevelDatAbilities {
}
}

fn get_int64(map: &mut HashMap<String, nbt::Value>, key: &str) -> Result<i64, WorldError> {
fn get_int64(map: &mut HashMap<String, nbtx::Value>, key: &str) -> Result<i64, WorldError> {
match map.remove(key) {
Some(nbt::Value::Long(v)) => Ok(v),
Some(nbtx::Value::Long(v)) => Ok(v),
Some(other) => Err(WorldError::FormatError(format!(
"Expected `{}` in LevelDat abilities to be of type Int64, got {:?}",
key, other
Expand All @@ -129,9 +127,9 @@ impl LevelDatAbilities {
}
}

fn get_f32(map: &mut HashMap<String, nbt::Value>, key: &str) -> Result<f32, WorldError> {
fn get_f32(map: &mut HashMap<String, nbtx::Value>, key: &str) -> Result<f32, WorldError> {
match map.remove(key) {
Some(nbt::Value::Float(v)) => Ok(v),
Some(nbtx::Value::Float(v)) => Ok(v),
Some(other) => Err(WorldError::FormatError(format!(
"Expected `{}` in LevelDat abilities to be of type Float32, got {:?}",
key, other
Expand All @@ -144,7 +142,7 @@ impl LevelDatAbilities {
}

match tag {
nbt::Value::Compound(mut map) => Ok(Self {
nbtx::Value::Compound(mut map) => Ok(Self {
attack_mobs: get_byte_as_bool(&mut map, "attackmobs")?,
attack_players: get_byte_as_bool(&mut map, "attackplayers")?,
redstone_interact: get_byte_as_bool(&mut map, "doorsandswitches")?,
Expand Down
219 changes: 0 additions & 219 deletions crates/world/src/level_dat/level_dat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ use std::fs::File;
use std::io::{Cursor, Read};
use std::path::{Path, PathBuf};

use bedrockrs_nbt::endian::little_endian::NbtLittleEndian;
use bedrockrs_nbt::NbtTag;
use byteorder::{LittleEndian, ReadBytesExt};

use bedrockrs_shared::world::difficulty::Difficulty;
Expand Down Expand Up @@ -68,220 +66,3 @@ pub struct LevelDat {
/// If cheats are on. (NBT entry: `commandsEnabled`)
pub cheats: bool,
}

impl LevelDat {
/// Opens the `level.dat` file from a given Minecraft Bedrock world directory.
///
/// Returns the header of the `level.dat` file and a [`LevelDat`] object, the header consists
/// of two `i32` integers which represent the version of the Minecraft Bedrock world
/// (which currently is `10`) and the length of the `level.dat` file excluding the header size.
pub fn open(directory: &Path) -> Result<(i32, i32, Self), WorldError> {
// Open the level.dat file
// TODO find out why there is a level.dat_old file as well and how it can be utilised
let mut file = File::open(directory.join("level.dat"))
.map_err(|e| WorldError::FormatError(e.to_string()))?;

// Read the entire level.dat file
let mut data = vec![];
match file.read_to_end(&mut data) {
Ok(_) => {}
Err(e) => {
return Err(WorldError::FormatError(e.to_string()));
}
};

// Build a ByteStreamRead with the file contents
let mut stream = Cursor::new(&data);

// Read the worlds format version
let version = match stream.read_i32::<LittleEndian>() {
Ok(v) => v,
Err(e) => {
return Err(WorldError::FormatError(e.to_string()));
}
};

// Read the level.dat size (without the header)
let length = match stream.read_i32::<LittleEndian>() {
Ok(v) => v,
Err(e) => {
return Err(WorldError::FormatError(e.to_string()));
}
};

// Read the uncompressed nbt tag

let pos = stream.position(); // TODO: utility function for this
let mut new_cur = Cursor::new(stream.into_inner().as_slice());
new_cur.set_position(pos);

let (_, nbt) = match NbtTag::nbt_deserialize::<NbtLittleEndian>(&mut new_cur) {
Ok(v) => v,
Err(e) => {
return Err(WorldError::NbtError(e));
}
};

// Parse the nbt tag into the LevelDat struct
let level_dat = match Self::parse(nbt) {
Ok(v) => v,
Err(e) => {
return Err(e);
}
};

Ok((version, length, level_dat))
}

/// Parses a given [`NbtTag`] into a [`LevelDat`] object.
pub fn parse(tag: NbtTag) -> Result<Self, WorldError> {
fn get_string(map: &mut HashMap<String, NbtTag>, key: &str) -> Result<String, WorldError> {
match map.remove(key) {
Some(NbtTag::String(v)) => Ok(v),
Some(other) => Err(WorldError::FormatError(format!(
"Expected `{}` in LevelDat to be of type String, got {:?}",
key, other
))),
None => Err(WorldError::FormatError(format!(
"Missing field `{}` in LevelDat",
key
))),
}
}

fn get_byte_as_bool(
map: &mut HashMap<String, NbtTag>,
key: &str,
) -> Result<bool, WorldError> {
match map.remove(key) {
Some(NbtTag::Byte(v)) => Ok(v != 0),
Some(other) => Err(WorldError::FormatError(format!(
"Expected `{}` in LevelDat to be of type Byte, got {:?}",
key, other
))),
None => Err(WorldError::FormatError(format!(
"Missing field `{}` in LevelDat",
key
))),
}
}

fn get_int32(map: &mut HashMap<String, NbtTag>, key: &str) -> Result<i32, WorldError> {
match map.remove(key) {
Some(NbtTag::Int32(v)) => Ok(v),
Some(other) => Err(WorldError::FormatError(format!(
"Expected `{}` in LevelDat to be of type Int32, got {:?}",
key, other
))),
None => Err(WorldError::FormatError(format!(
"Missing field `{}` in LevelDat",
key
))),
}
}

fn get_int32_option(
map: &mut HashMap<String, NbtTag>,
key: &str,
) -> Result<Option<i32>, WorldError> {
match map.remove(key) {
Some(NbtTag::Int32(v)) => Ok(Some(v)),
Some(other) => Err(WorldError::FormatError(format!(
"Expected `{}` in LevelDat to be of type Int32, got {:?}",
key, other
))),
None => Ok(None),
}
}

fn get_int64(map: &mut HashMap<String, NbtTag>, key: &str) -> Result<i64, WorldError> {
match map.remove(key) {
Some(NbtTag::Int64(v)) => Ok(v),
Some(other) => Err(WorldError::FormatError(format!(
"Expected `{}` in LevelDat to be of type Int64, got {:?}",
key, other
))),
None => Err(WorldError::FormatError(format!(
"Missing field `{}` in LevelDat",
key
))),
}
}

fn get_compound(
map: &mut HashMap<String, NbtTag>,
key: &str,
) -> Result<HashMap<String, NbtTag>, WorldError> {
match map.remove(key) {
Some(NbtTag::Compound(v)) => Ok(v),
Some(other) => Err(WorldError::FormatError(format!(
"Expected `{}` in LevelDat to be of type Compound, got {:?}",
key, other
))),
None => Err(WorldError::FormatError(format!(
"Missing field `{}` in LevelDat",
key
))),
}
}

match tag {
// It must be a compound tag
NbtTag::Compound(mut map) => Ok(Self {
level_name: get_string(&mut map, "LevelName")?,
format_version: get_int32(&mut map, "StorageVersion")?,
abilities: LevelDatAbilities::parse(match map.remove("abilities") {
Some(v) => v,
None => Err(WorldError::FormatError(format!(
"Missing field `abilities` in LevelDat"
)))?,
})?,
experiments: {
let nbt = get_compound(&mut map, "experiments")?;
let mut experiments = HashMap::new();

for (name, tag) in nbt.iter() {
experiments.insert(name.clone(), match tag {
NbtTag::Byte(v) => { *v != 0 }
other => { Err(WorldError::FormatError(format!("Expected `{}` in LevelDat experiments to be of type Byte, got {:?}", name, other)))? }
});
}

experiments
},
difficulty: match get_int32(&mut map, "Difficulty")? {
0 => Difficulty::Peaceful,
1 => Difficulty::Easy,
2 => Difficulty::Normal,
3 => Difficulty::Hard,
other => Err(WorldError::FormatError(format!(
"Value for `Difficulty` is out of bounds, got {:?}",
other
)))?,
},
dimension: match get_int32_option(&mut map, "Dimension")? {
Some(1) => Some(Dimension::Overworld),
Some(2) => Some(Dimension::Nether),
Some(3) => Some(Dimension::End),
Some(other) => Err(WorldError::FormatError(format!(
"Value for `Dimension` is out of bounds, got {:?}",
other
)))?,
None => None,
},
bonus_chest_enabled: get_byte_as_bool(&mut map, "bonusChestEnabled")?,
bonus_chest_spawned: get_byte_as_bool(&mut map, "bonusChestSpawned")?,
seed: get_int64(&mut map, "RandomSeed")?,
current_tick: get_int64(&mut map, "currentTick")?,
spawn_x: get_int32(&mut map, "SpawnX")?,
spawn_y: get_int32(&mut map, "SpawnY")?,
spawn_z: get_int32(&mut map, "SpawnZ")?,
cheats: get_byte_as_bool(&mut map, "commandsEnabled")?,
}),
other => Err(WorldError::FormatError(format!(
"Expected root tag in LevelDat to be of type Compound, got {:?}",
other
))),
}
}
}
15 changes: 8 additions & 7 deletions crates/world/src/world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@ impl World {

// Read the world settings/specifications from the level.dat file.
// (in world/level.dat and world/level.dat_old)
let (version, _, level_dat) = match LevelDat::open(&directory) {
Ok(v) => v,
Err(e) => {
return Err(e);
}
};
todo!("Help");
// let (version, _, level_dat) = match LevelDat::open(&directory) {
// Ok(v) => v,
// Err(e) => {
// return Err(e);
// }
// };

// Read the world name from the levelname.txt file
// (in world/levelname.txt)
Expand All @@ -43,7 +44,7 @@ impl World {
}
},
level_dat,
format_version: version,
format_version: 2,
})
}
}

0 comments on commit 308aec5

Please sign in to comment.