added some imperfect loading of worlds

This commit is contained in:
T-x-T 2025-06-19 21:15:13 +02:00
parent 20d1b0510d
commit 659d023ea5
14 changed files with 577 additions and 107 deletions

1
.gitignore vendored
View file

@ -2,3 +2,4 @@
official_server
.DS_Store
flamegraph.svg
/server/world

41
lib/Cargo.lock generated
View file

@ -2,13 +2,54 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "cfg-if"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
[[package]]
name = "crc32fast"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
dependencies = [
"cfg-if",
]
[[package]]
name = "data"
version = "0.1.0"
[[package]]
name = "flate2"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "lib"
version = "0.1.0"
dependencies = [
"data",
"flate2",
]
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
]

View file

@ -5,3 +5,4 @@ edition = "2024"
[dependencies]
data = {path = "../data", version = "*"}
flate2 = "1.1.2"

View file

@ -108,15 +108,15 @@ pub fn slot(data: &mut Vec<u8>) -> Result<Slot, Box<dyn Error>> {
for _ in 0..number_of_components_to_add {
let component_id = varint(data)?;
components_to_add.push(match component_id {
0 => SlotComponent::CustomData(nbt(data)?),
0 => SlotComponent::CustomData(nbt_network(data)?),
1 => SlotComponent::MaxStackSize(varint(data)?),
2 => SlotComponent::MaxDamage(varint(data)?),
3 => SlotComponent::Damage(varint(data)?),
4 => SlotComponent::Unbreakable,
5 => SlotComponent::CustomName(nbt(data)?),
6 => SlotComponent::ItemName(nbt(data)?),
5 => SlotComponent::CustomName(nbt_network(data)?),
6 => SlotComponent::ItemName(nbt_network(data)?),
7 => SlotComponent::ItemModel(string(data)?),
8 => SlotComponent::Lore((0..varint(data)?).map(|_| nbt(data).unwrap()).collect()),
8 => SlotComponent::Lore((0..varint(data)?).map(|_| nbt_network(data).unwrap()).collect()),
9 => SlotComponent::Rarity(data.remove(0)),
10 => SlotComponent::Enchantments((0..varint(data)?).map(|_| (varint(data).unwrap(), varint(data).unwrap())).collect()),
11 => todo!(), //SlotComponent::CanPlaceOn,
@ -127,7 +127,7 @@ pub fn slot(data: &mut Vec<u8>) -> Result<Slot, Box<dyn Error>> {
16 => SlotComponent::RepairCost(varint(data)?),
17 => SlotComponent::CreativeSlotLock,
18 => SlotComponent::EnchantmentGlintOverride(boolean(data)?),
19 => SlotComponent::IntangibleProjectile(nbt(data)?),
19 => SlotComponent::IntangibleProjectile(nbt_network(data)?),
20 => SlotComponent::Food(varint(data)?, float(data)?, boolean(data)?),
21 => todo!(), //SlotComponent::Consumable,
22 => SlotComponent::UseRemainder(slot(data)?),
@ -146,7 +146,7 @@ pub fn slot(data: &mut Vec<u8>) -> Result<Slot, Box<dyn Error>> {
35 => SlotComponent::DyedColor(int(data)?),
36 => SlotComponent::MapColor(int(data)?),
37 => SlotComponent::MapId(varint(data)?),
38 => SlotComponent::MapDecorations(nbt(data)?),
38 => SlotComponent::MapDecorations(nbt_network(data)?),
39 => SlotComponent::MapPostProcessing(data.remove(0)),
40 => SlotComponent::ChargedProjectiles((0..varint(data)?).map(|_| slot(data).unwrap()).collect()),
41 => SlotComponent::BundleContents((0..varint(data)?).map(|_| slot(data).unwrap()).collect()),
@ -156,16 +156,16 @@ pub fn slot(data: &mut Vec<u8>) -> Result<Slot, Box<dyn Error>> {
45 => SlotComponent::WritableBookContent((0..varint(data)?).map(|_| (string(data).unwrap(), if boolean(data).unwrap() {Some(string(data).unwrap())} else {None})).collect()),
46 => SlotComponent::WrittenBookContent((0..varint(data)?).map(|_| (string(data).unwrap(), if boolean(data).unwrap() {Some(string(data).unwrap())} else {None})).collect()),
47 => todo!(), //SlotComponent::Trim,
48 => SlotComponent::DebugStickState(nbt(data)?),
49 => SlotComponent::EntityData(nbt(data)?),
50 => SlotComponent::BucketEntityData(nbt(data)?),
51 => SlotComponent::BlockEntityData(nbt(data)?),
48 => SlotComponent::DebugStickState(nbt_network(data)?),
49 => SlotComponent::EntityData(nbt_network(data)?),
50 => SlotComponent::BucketEntityData(nbt_network(data)?),
51 => SlotComponent::BlockEntityData(nbt_network(data)?),
52 => todo!(), //SlotComponent::Instrument,
53 => todo!(), //SlotComponent::ProvidesTrimMaterial,
54 => SlotComponent::OminousBottleAmplifier(data.remove(0)),
55 => todo!(), //SlotComponent::JukeboxPlayable,
56 => SlotComponent::ProvidesBannerPatterns(string(data)?),
57 => SlotComponent::Recipes(nbt(data)?),
57 => SlotComponent::Recipes(nbt_network(data)?),
58 => SlotComponent::LodestoneTracker(boolean(data)?, string(data)?, position(data)?, boolean(data)?),
59 => todo!(), //SlotComponent::FireworkExplosion,
60 => todo!(), //SlotComponent::Fireworks,
@ -176,9 +176,9 @@ pub fn slot(data: &mut Vec<u8>) -> Result<Slot, Box<dyn Error>> {
65 => SlotComponent::PotDecorations((0..varint(data)?).map(|_| varint(data).unwrap()).collect()),
66 => SlotComponent::Container((0..varint(data)?).map(|_| varint(data).unwrap()).collect()),
67 => SlotComponent::BlockState((0..varint(data)?).map(|_| (string(data).unwrap(), string(data).unwrap())).collect()),
68 => SlotComponent::Bees((0..varint(data)?).map(|_| (nbt(data).unwrap(), varint(data).unwrap(), varint(data).unwrap())).collect()),
69 => SlotComponent::Lock(nbt(data)?),
70 => SlotComponent::ContainerLoot(nbt(data)?),
68 => SlotComponent::Bees((0..varint(data)?).map(|_| (nbt_network(data).unwrap(), varint(data).unwrap(), varint(data).unwrap())).collect()),
69 => SlotComponent::Lock(nbt_network(data)?),
70 => SlotComponent::ContainerLoot(nbt_network(data)?),
71 => todo!(), //SlotComponent::BreakSound,
72 => todo!(), //SlotComponent::VillagerVariant,
73 => todo!(), //SlotComponent::WolfVariant,
@ -249,11 +249,15 @@ pub fn varint(data: &mut Vec<u8>) -> Result<i32, Box<dyn Error>> {
return Ok(value);
}
pub fn nbt(data: &mut Vec<u8>) -> Result<NbtTag, Box<dyn Error>> {
//println!("deserialize nbt:\n\n{data:?}\n\n\n\n");
pub fn nbt_network(data: &mut Vec<u8>) -> Result<NbtTag, Box<dyn Error>> {
return nbt_tag_compound(data, false, true);
}
pub fn nbt_disk(data: &mut Vec<u8>) -> Result<NbtTag, Box<dyn Error>> {
return nbt_tag_compound(data, true, true);
}
fn nbt_byte_array_value(data: &mut Vec<u8>) -> Result<Vec<u8>, Box<dyn Error>> {
let len = int(data)?;
let mut bytes: Vec<u8> = Vec::new();
@ -279,9 +283,8 @@ pub fn nbt_string_value(data: &mut Vec<u8>) -> Result<String, Box<dyn Error>> {
}
fn nbt_list(data: &mut Vec<u8>, has_description: bool, has_id: bool) -> Result<NbtTag, Box<dyn Error>> {
data.reverse();
if has_id {
data.pop();
data.remove(0);
}
let description: Option<String> = if has_description {
@ -290,127 +293,105 @@ fn nbt_list(data: &mut Vec<u8>, has_description: bool, has_id: bool) -> Result<N
None
};
let id = data.pop().unwrap();
let id = data.remove(0);
let len = int(data)?;
if len == 0 {
return Ok(NbtTag::List(description, Vec::new()));
}
let output: NbtTag = match id {
0x01 => {
let mut list: Vec<NbtTag> = Vec::new();
for _ in 0..len {
list.push(NbtTag::Byte(None, data.pop().unwrap()));
list.push(NbtTag::Byte(None, data.remove(0)));
}
NbtTag::List(description, list)
},
0x02 => {
let mut list: Vec<NbtTag> = Vec::new();
data.reverse();
for _ in 0..len {
list.push(NbtTag::Short(None, short(data)?));
}
data.reverse();
NbtTag::List(description, list)
},
0x03 => {
let mut list: Vec<NbtTag> = Vec::new();
data.reverse();
for _ in 0..len {
list.push(NbtTag::Int(None, int(data)?));
}
data.reverse();
NbtTag::List(description, list)
},
0x04 => {
let mut list: Vec<NbtTag> = Vec::new();
data.reverse();
for _ in 0..len {
list.push(NbtTag::Long(None, long(data)?));
}
data.reverse();
NbtTag::List(description, list)
},
0x05 => {
let mut list: Vec<NbtTag> = Vec::new();
data.reverse();
for _ in 0..len {
list.push(NbtTag::Float(None, float(data)?));
}
data.reverse();
NbtTag::List(description, list)
},
0x06 => {
let mut list: Vec<NbtTag> = Vec::new();
data.reverse();
for _ in 0..len {
list.push(NbtTag::Double(None, double(data)?));
}
data.reverse();
NbtTag::List(description, list)
},
0x07 => {
let mut list: Vec<NbtTag> = Vec::new();
data.reverse();
for _ in 0..len {
list.push(NbtTag::ByteArray(None, nbt_byte_array_value(data)?));
}
data.reverse();
NbtTag::List(description, list)
},
0x08 => {
let mut list: Vec<NbtTag> = Vec::new();
data.reverse();
for _ in 0..len {
list.push(NbtTag::String(None, nbt_string_value(data)?));
}
data.reverse();
NbtTag::List(description, list)
},
0x09 => {
todo!("this isnt implemented yet lol");
//let mut list: Vec<NbtTag> = Vec::new();
//data.reverse();
//for _ in 0..len {
// list.push(NbtTag::String(None, nbt_string_value(&mut data)?));
//}
//data.reverse();
//NbtTag::List(description, list)
let mut list: Vec<NbtTag> = Vec::new();
for _ in 0..len {
list.push(nbt_list(data, false, false).unwrap());
}
NbtTag::List(description, list)
},
0x0a => {
todo!("this isnt implemented yet lol");
//let mut list: Vec<NbtTag> = Vec::new();
//data.reverse();
//for _ in 0..len {
// list.push(NbtTag::String(None, nbt_string_value(&mut data)?));
//}
//data.reverse();
//NbtTag::List(description, list)
let mut list: Vec<NbtTag> = Vec::new();
for _ in 0..len {
list.push(nbt_tag_compound(data, false, false).unwrap());
}
NbtTag::List(description, list)
},
0x0b => {
let mut list: Vec<NbtTag> = Vec::new();
data.reverse();
for _ in 0..len {
list.push(NbtTag::IntArray(None, nbt_int_array_value(data)?));
}
data.reverse();
NbtTag::List(description, list)
},
0x0c => {
let mut list: Vec<NbtTag> = Vec::new();
data.reverse();
for _ in 0..len {
list.push(NbtTag::LongArray(None, nbt_long_array_value(data)?));
}
data.reverse();
NbtTag::List(description, list)
},
@ -419,13 +400,11 @@ fn nbt_list(data: &mut Vec<u8>, has_description: bool, has_id: bool) -> Result<N
}
};
data.reverse();
return Ok(output);
}
fn nbt_tag_compound(data: &mut Vec<u8>, has_description: bool, has_id: bool) -> Result<NbtTag, Box<dyn Error>> {
if has_id {
if has_id {
data.remove(0);
}
@ -439,7 +418,6 @@ fn nbt_tag_compound(data: &mut Vec<u8>, has_description: bool, has_id: bool) ->
loop {
let id = data.remove(0);
match id {
0x00 => break,
0x01 => {
@ -577,6 +555,61 @@ mod test {
let mut nbt_bytes: Vec<u8> = vec![10,2,0,17,77,97,120,78,101,97,114,98,121,69,110,116,105,116,105,101,115,0,6,2,0,19,82,101,113,117,105,114,101,100,80,108,97,121,101,114,82,97,110,103,101,0,16,2,0,10,83,112,97,119,110,67,111,117,110,116,0,4,10,0,9,83,112,97,119,110,68,97,116,97,10,0,6,101,110,116,105,116,121,8,0,2,105,100,0,16,109,105,110,101,99,114,97,102,116,58,115,112,105,100,101,114,0,0,2,0,13,77,97,120,83,112,97,119,110,68,101,108,97,121,3,32,2,0,10,83,112,97,119,110,82,97,110,103,101,0,4,2,0,5,68,101,108,97,121,0,20,2,0,13,77,105,110,83,112,97,119,110,68,101,108,97,121,0,200,0];
assert_eq!(nbt(&mut nbt_bytes).unwrap(), nbt_parsed);
assert_eq!(nbt_network(&mut nbt_bytes).unwrap(), nbt_parsed);
}
#[test]
fn nbt_with_list_of_compounds_network() {
let nbt = NbtTag::TagCompound(None, vec![
NbtTag::List(Some("this_is_a_list".to_string()), vec![
NbtTag::TagCompound(None, vec![
NbtTag::String(Some("a".to_string()), "b".to_string()),
]),
NbtTag::TagCompound(None, vec![
NbtTag::String(Some("a".to_string()), "b".to_string()),
]),
NbtTag::TagCompound(None, vec![
NbtTag::String(Some("a".to_string()), "b".to_string()),
]),
]),
]);
let mut nbt_bytes = crate::serialize::nbt_network(nbt.clone());
println!("nbt_bytes\n{nbt_bytes:?}");
assert_eq!(nbt_network(&mut nbt_bytes).unwrap(), nbt);
}
#[test]
fn nbt_with_list_of_compounds_disk() {
let nbt = NbtTag::TagCompound(Some("".to_string()), vec![
NbtTag::List(Some("this_is_a_list".to_string()), vec![
NbtTag::TagCompound(None, vec![
NbtTag::String(Some("a".to_string()), "b".to_string()),
]),
NbtTag::TagCompound(None, vec![
NbtTag::String(Some("a".to_string()), "b".to_string()),
]),
NbtTag::TagCompound(None, vec![
NbtTag::String(Some("a".to_string()), "b".to_string()),
]),
]),
]);
let mut nbt_bytes = crate::serialize::nbt_disk(nbt.clone());
println!("nbt_bytes\n{nbt_bytes:?}");
assert_eq!(nbt_disk(&mut nbt_bytes).unwrap(), nbt);
}
#[test]
fn nbt_with_empty_list_disk() {
let nbt = NbtTag::TagCompound(Some("".to_string()), vec![
NbtTag::List(Some("this_is_a_list".to_string()), vec![
]),
]);
let mut nbt_bytes = crate::serialize::nbt_disk(nbt.clone());
println!("nbt_bytes\n{nbt_bytes:?}");
assert_eq!(nbt_disk(&mut nbt_bytes).unwrap(), nbt);
}
}

View file

@ -81,7 +81,7 @@ impl TryFrom<RegistryData> for Vec<u8> {
data.append(&mut crate::serialize::string(&x.entry_id));
data.append(&mut crate::serialize::boolean(x.has_data));
if x.has_data {
data.append(&mut crate::serialize::nbt(x.clone().data.unwrap()));
data.append(&mut crate::serialize::nbt_network(x.clone().data.unwrap()));
}
});
@ -103,7 +103,7 @@ impl TryFrom<Vec<u8>> for RegistryData {
let entry_id = crate::deserialize::string(&mut value)?;
let has_data = crate::deserialize::boolean(&mut value)?;
let data: Option<crate::nbt::NbtTag> = if has_data {
Some(crate::deserialize::nbt(&mut value)?)
Some(crate::deserialize::nbt_network(&mut value)?)
} else {
None
};

View file

@ -451,7 +451,7 @@ impl TryFrom<ChunkDataAndUpdateLight> for Vec<u8> {
output.append(&mut crate::serialize::short(x.y));
output.append(&mut crate::serialize::varint(x.block_entity_type));
if x.data.is_some() {
output.append(&mut crate::serialize::nbt(x.data.unwrap()));
output.append(&mut crate::serialize::nbt_network(x.data.unwrap()));
} else {
output.push(0x00);
}
@ -628,7 +628,7 @@ impl TryFrom<Vec<u8>> for ChunkDataAndUpdateLight {
let data = if *value.first().unwrap() == 0 {
None
} else {
Some(crate::deserialize::nbt(&mut value)?)
Some(crate::deserialize::nbt_network(&mut value)?)
};
block_entities.push(BlockEntity {
packed_xz,
@ -1161,7 +1161,7 @@ impl TryFrom<PlayerChatMessage> for Vec<u8> {
});
if value.unsigned_content.is_some() {
output.append(&mut crate::serialize::boolean(true));
output.append(&mut crate::serialize::nbt(value.unsigned_content.unwrap()));
output.append(&mut crate::serialize::nbt_network(value.unsigned_content.unwrap()));
} else {
output.append(&mut crate::serialize::boolean(false));
}
@ -1170,9 +1170,9 @@ impl TryFrom<PlayerChatMessage> for Vec<u8> {
output.append(&mut crate::serialize::bitset(&value.filter_type_bits));
}
output.append(&mut crate::serialize::varint(value.chat_type));
output.append(&mut crate::serialize::nbt(value.sender_name));
output.append(&mut crate::serialize::nbt_network(value.sender_name));
if value.target_name.is_some() {
output.append(&mut crate::serialize::nbt(value.target_name.unwrap()));
output.append(&mut crate::serialize::nbt_network(value.target_name.unwrap()));
}
output.push(0); //not sure why this is needed
@ -1208,7 +1208,7 @@ impl TryFrom<Vec<u8>> for PlayerChatMessage {
}).collect();
let unsigned_content_present = crate::deserialize::boolean(&mut value)?;
let unsigned_content = if unsigned_content_present {
Some(crate::deserialize::nbt(&mut value)?)
Some(crate::deserialize::nbt_network(&mut value)?)
} else {
None
};
@ -1219,10 +1219,10 @@ impl TryFrom<Vec<u8>> for PlayerChatMessage {
Vec::new()
};
let chat_type = crate::deserialize::varint(&mut value)?;
let sender_name = crate::deserialize::nbt(&mut value)?;
let sender_name = crate::deserialize::nbt_network(&mut value)?;
let target_name_present = crate::deserialize::boolean(&mut value)?;
let target_name = if target_name_present {
Some(crate::deserialize::nbt(&mut value)?)
Some(crate::deserialize::nbt_network(&mut value)?)
} else {
None
};
@ -1365,7 +1365,7 @@ impl TryFrom<PlayerInfoUpdate> for Vec<u8> {
PlayerAction::UpdateDisplayName(display_name) => {
output.append(&mut crate::serialize::boolean(display_name.is_some()));
if let Some(display_name) = display_name {
output.append(&mut crate::serialize::nbt(display_name));
output.append(&mut crate::serialize::nbt_network(display_name));
}
},
PlayerAction::UpdateListPriority(priority) => output.append(&mut crate::serialize::varint(priority)),
@ -1441,7 +1441,7 @@ impl TryFrom<Vec<u8>> for PlayerInfoUpdate {
if actions & 0x20 != 0 {
let display_name = if crate::deserialize::boolean(&mut value)? {
Some(crate::deserialize::nbt(&mut value)?)
Some(crate::deserialize::nbt_network(&mut value)?)
} else {
None
};
@ -1729,12 +1729,12 @@ impl TryFrom<SetEntityMetadata> for Vec<u8> {
EntityMetadataValue::Varlong(_) => todo!(),
EntityMetadataValue::Float(a) => output.append(&mut crate::serialize::float(a)),
EntityMetadataValue::String(a) => output.append(&mut crate::serialize::string(&a)),
EntityMetadataValue::TextComponent(a) => output.append(&mut crate::serialize::nbt(a)),
EntityMetadataValue::TextComponent(a) => output.append(&mut crate::serialize::nbt_network(a)),
EntityMetadataValue::OptionalTextComponent(a) => {
match a {
Some(a) => {
output.push(0x01);
output.append(&mut crate::serialize::nbt(a));
output.append(&mut crate::serialize::nbt_network(a));
},
None => {
output.push(0x00);
@ -1769,7 +1769,7 @@ impl TryFrom<SetEntityMetadata> for Vec<u8> {
},
EntityMetadataValue::BlockState(a) => output.append(&mut crate::serialize::varint(a)),
EntityMetadataValue::OptionalBlockState(a) => output.append(&mut crate::serialize::varint(a)),
EntityMetadataValue::Nbt(a) => output.append(&mut crate::serialize::nbt(a)),
EntityMetadataValue::Nbt(a) => output.append(&mut crate::serialize::nbt_network(a)),
EntityMetadataValue::Particle(_) => todo!(),
EntityMetadataValue::Particles(_, _) => todo!(),
EntityMetadataValue::VillagerData(_, _, _) => todo!(),
@ -1833,11 +1833,11 @@ impl TryFrom<Vec<u8>> for SetEntityMetadata {
2 => todo!(),
3 => EntityMetadataValue::Float(crate::deserialize::float(&mut value)?),
4 => EntityMetadataValue::String(crate::deserialize::string(&mut value)?),
5 => EntityMetadataValue::TextComponent(crate::deserialize::nbt(&mut value)?),
5 => EntityMetadataValue::TextComponent(crate::deserialize::nbt_network(&mut value)?),
6 => {
let nbt_present = crate::deserialize::boolean(&mut value)?;
let nbt = if nbt_present {
Some(crate::deserialize::nbt(&mut value)?)
Some(crate::deserialize::nbt_network(&mut value)?)
} else {
None
};
@ -1860,7 +1860,7 @@ impl TryFrom<Vec<u8>> for SetEntityMetadata {
13 => todo!(),
14 => EntityMetadataValue::BlockState(crate::deserialize::varint(&mut value)?),
15 => EntityMetadataValue::OptionalBlockState(crate::deserialize::varint(&mut value)?),
16 => EntityMetadataValue::Nbt(crate::deserialize::nbt(&mut value)?),
16 => EntityMetadataValue::Nbt(crate::deserialize::nbt_network(&mut value)?),
17 => todo!(),
18 => todo!(),
19 => EntityMetadataValue::VillagerData(crate::deserialize::varint(&mut value)?, crate::deserialize::varint(&mut value)?, crate::deserialize::varint(&mut value)?),
@ -1910,7 +1910,7 @@ impl TryFrom<SystemChatMessage> for Vec<u8> {
fn try_from(value: SystemChatMessage) -> Result<Self, Box<dyn Error>> {
let mut output: Vec<u8> = Vec::new();
output.append(&mut crate::serialize::nbt(value.content));
output.append(&mut crate::serialize::nbt_network(value.content));
output.append(&mut crate::serialize::boolean(value.overlay));
return Ok(output);
@ -1922,7 +1922,7 @@ impl TryFrom<Vec<u8>> for SystemChatMessage {
fn try_from(mut value: Vec<u8>) -> Result<Self, Box<dyn Error>> {
return Ok(Self {
content: crate::deserialize::nbt(&mut value)?,
content: crate::deserialize::nbt_network(&mut value)?,
overlay: crate::deserialize::boolean(&mut value)?,
});
}

View file

@ -96,15 +96,15 @@ pub fn slot(input: &Slot) -> Vec<u8> {
for component_to_add in &input.components_to_add {
output.append(&mut varint(component_to_add.into()));
output.append(&mut match component_to_add.clone() {
SlotComponent::CustomData(a) => nbt(a),
SlotComponent::CustomData(a) => nbt_network(a),
SlotComponent::MaxStackSize(a) => varint(a),
SlotComponent::MaxDamage(a) => varint(a),
SlotComponent::Damage(a) => varint(a),
SlotComponent::Unbreakable => vec![],
SlotComponent::CustomName(a) => nbt(a),
SlotComponent::ItemName(a) => nbt(a),
SlotComponent::CustomName(a) => nbt_network(a),
SlotComponent::ItemName(a) => nbt_network(a),
SlotComponent::ItemModel(a) => string(&a),
SlotComponent::Lore(a) => a.into_iter().flat_map(nbt).collect(),
SlotComponent::Lore(a) => a.into_iter().flat_map(nbt_network).collect(),
SlotComponent::Rarity(a) => vec![a],
SlotComponent::Enchantments(a) => a.into_iter().flat_map(|(x, y)| vec![varint(x), varint(y)]).flatten().collect(),
SlotComponent::CanPlaceOn => todo!(),
@ -115,7 +115,7 @@ pub fn slot(input: &Slot) -> Vec<u8> {
SlotComponent::RepairCost(a) => varint(a),
SlotComponent::CreativeSlotLock => vec![],
SlotComponent::EnchantmentGlintOverride(a) => boolean(a),
SlotComponent::IntangibleProjectile(a) => nbt(a),
SlotComponent::IntangibleProjectile(a) => nbt_network(a),
SlotComponent::Food(a, b, c) => vec![varint(a), float(b), boolean(c)].into_iter().flatten().collect(),
SlotComponent::Consumable => todo!(),
SlotComponent::UseRemainder(a) => slot(&a),
@ -134,7 +134,7 @@ pub fn slot(input: &Slot) -> Vec<u8> {
SlotComponent::DyedColor(a) => int(a),
SlotComponent::MapColor(a) => int(a),
SlotComponent::MapId(a) => varint(a),
SlotComponent::MapDecorations(a) => nbt(a),
SlotComponent::MapDecorations(a) => nbt_network(a),
SlotComponent::MapPostProcessing(a) => vec![a],
SlotComponent::ChargedProjectiles(a) => a.into_iter().flat_map(|x| slot(&x)).collect(),
SlotComponent::BundleContents(a) => a.into_iter().flat_map(|x| slot(&x)).collect(),
@ -144,16 +144,16 @@ pub fn slot(input: &Slot) -> Vec<u8> {
SlotComponent::WritableBookContent(a) => a.into_iter().flat_map(|(x, y)| vec![string(&x), if y.is_some() {vec![vec![0x01], string(&y.unwrap())].into_iter().flatten().collect()} else {vec![0x00]}]).flatten().collect(),
SlotComponent::WrittenBookContent(a) => a.into_iter().flat_map(|(x, y)| vec![string(&x), if y.is_some() {vec![vec![0x01], string(&y.unwrap())].into_iter().flatten().collect()} else {vec![0x00]}]).flatten().collect(),
SlotComponent::Trim => todo!(),
SlotComponent::DebugStickState(a) => nbt(a),
SlotComponent::EntityData(a) => nbt(a),
SlotComponent::BucketEntityData(a) => nbt(a),
SlotComponent::BlockEntityData(a) => nbt(a),
SlotComponent::DebugStickState(a) => nbt_network(a),
SlotComponent::EntityData(a) => nbt_network(a),
SlotComponent::BucketEntityData(a) => nbt_network(a),
SlotComponent::BlockEntityData(a) => nbt_network(a),
SlotComponent::Instrument => todo!(),
SlotComponent::ProvidesTrimMaterial => todo!(),
SlotComponent::OminousBottleAmplifier(a) => vec![a],
SlotComponent::JukeboxPlayable => todo!(),
SlotComponent::ProvidesBannerPatterns(a) => string(&a),
SlotComponent::Recipes(a) => nbt(a),
SlotComponent::Recipes(a) => nbt_network(a),
SlotComponent::LodestoneTracker(a, b, c, d) => vec![boolean(a), string(&b), position(&c), boolean(d)].into_iter().flatten().collect(),
SlotComponent::FireworkExplosion => todo!(),
SlotComponent::Fireworks => todo!(),
@ -164,9 +164,9 @@ pub fn slot(input: &Slot) -> Vec<u8> {
SlotComponent::PotDecorations(a) => a.into_iter().flat_map(varint).collect(),
SlotComponent::Container(a) => a.into_iter().flat_map(varint).collect(),
SlotComponent::BlockState(a) => a.into_iter().flat_map(|(x, y)| vec![string(&x), string(&y)]).flatten().collect(),
SlotComponent::Bees(a) => a.into_iter().flat_map(|(x, y, z)| vec![nbt(x), varint(y), varint(z)]).flatten().collect(),
SlotComponent::Lock(a) => nbt(a),
SlotComponent::ContainerLoot(a) => nbt(a),
SlotComponent::Bees(a) => a.into_iter().flat_map(|(x, y, z)| vec![nbt_network(x), varint(y), varint(z)]).flatten().collect(),
SlotComponent::Lock(a) => nbt_network(a),
SlotComponent::ContainerLoot(a) => nbt_network(a),
SlotComponent::BreakSound => todo!(),
SlotComponent::VillagerVariant => todo!(),
SlotComponent::WolfVariant => todo!(),
@ -213,10 +213,22 @@ pub fn prefixed_array(mut data: Vec<u8>, len: i32) -> Vec<u8> {
return output;
}
pub fn nbt(input: NbtTag) -> Vec<u8> {
let mut nbt = nbt_tag_compound(None, vec![input], false);
nbt.pop(); //Otherwise we have one 0x00 byte too much at the end
return nbt;
pub fn nbt_network(input: NbtTag) -> Vec<u8> {
match input {
NbtTag::TagCompound(_, p) => {
return nbt_tag_compound(None, p, true);
},
_ => panic!("root node must be a tag compound"),
}
}
pub fn nbt_disk(input: NbtTag) -> Vec<u8> {
match input {
NbtTag::TagCompound(_, p) => {
return nbt_tag_compound(Some("".to_string()), p, true);
},
_ => panic!("root node must be a tag compound"),
}
}
fn nbt_byte(description: Option<String>, payload: u8, include_id: bool) -> Vec<u8> {
@ -362,6 +374,7 @@ fn nbt_list(description: Option<String>, payload: Vec<NbtTag>, include_id: bool)
}
if payload.is_empty() {
output.append(&mut vec![0;5]);
return output;
}

View file

@ -19,3 +19,128 @@ impl Default for NbtTag {
return NbtTag::TagCompound(None, Vec::new());
}
}
impl NbtTag {
pub fn get_children(&self) -> Vec<NbtTag> {
match self {
NbtTag::TagCompound(_, p) => return p.clone(),
NbtTag::List(_, p) => return p.clone(),
_ => return Vec::new(),
}
}
pub fn get_child(&self, description: &str) -> Option<NbtTag> {
match self {
NbtTag::TagCompound(_, p) => {
for tag in p {
if tag.get_description().unwrap_or_default().as_str() == description {
return Some(tag.clone());
}
}
return None;
},
_ => return None,
}
}
pub fn get_description(&self) -> Option<String> {
return match self {
NbtTag::Byte(d, _) => d.clone(),
NbtTag::Short(d, _) => d.clone(),
NbtTag::Int(d, _) => d.clone(),
NbtTag::Long(d, _) => d.clone(),
NbtTag::Float(d, _) => d.clone(),
NbtTag::Double(d, _) => d.clone(),
NbtTag::ByteArray(d, _) => d.clone(),
NbtTag::String(d, _) => d.clone(),
NbtTag::List(d, _) => d.clone(),
NbtTag::TagCompound(d, _) => d.clone(),
NbtTag::IntArray(d, _) => d.clone(),
NbtTag::LongArray(d, _) => d.clone(),
};
}
pub fn as_byte(&self) -> u8 {
match self {
NbtTag::Byte(_, p) => return *p,
_ => panic!("wrong type of Tag!"),
}
}
pub fn as_short(&self) -> i16 {
match self {
NbtTag::Short(_, p) => return *p,
_ => panic!("wrong type of Tag!"),
}
}
pub fn as_int(&self) -> i32 {
match self {
NbtTag::Int(_, p) => return *p,
_ => panic!("wrong type of Tag!"),
}
}
pub fn as_long(&self) -> i64 {
match self {
NbtTag::Long(_, p) => return *p,
_ => panic!("wrong type of Tag!"),
}
}
pub fn as_float(&self) -> f32 {
match self {
NbtTag::Float(_, p) => return *p,
_ => panic!("wrong type of Tag!"),
}
}
pub fn as_double(&self) -> f64 {
match self {
NbtTag::Double(_, p) => return *p,
_ => panic!("wrong type of Tag!"),
}
}
pub fn as_byte_array(&self) -> Vec<u8> {
match self {
NbtTag::ByteArray(_, p) => return p.clone(),
_ => panic!("wrong type of Tag!"),
}
}
pub fn as_string(&self) -> String {
match self {
NbtTag::String(_, p) => return p.clone(),
_ => panic!("wrong type of Tag!"),
}
}
pub fn as_list(&self) -> Vec<NbtTag> {
match self {
NbtTag::List(_, p) => return p.clone(),
_ => panic!("wrong type of Tag!"),
}
}
pub fn as_tag_compound(&self) -> Vec<NbtTag> {
match self {
NbtTag::TagCompound(_, p) => return p.clone(),
_ => panic!("wrong type of Tag!"),
}
}
pub fn as_int_array(&self) -> Vec<i32> {
match self {
NbtTag::IntArray(_, p) => return p.clone(),
_ => panic!("wrong type of Tag!"),
}
}
pub fn as_long_array(&self) -> Vec<i64> {
match self {
NbtTag::LongArray(_, p) => return p.clone(),
_ => panic!("wrong type of Tag!"),
}
}
}

View file

@ -0,0 +1,8 @@
pub mod vanilla;
use super::*;
pub trait WorldLoader {
fn load_chunk(&self, x: i32, z: i32) -> super::Chunk;
fn is_initialized(&self) -> bool;
}

View file

@ -0,0 +1,140 @@
use std::{fs, io::prelude::*, path::PathBuf, str::FromStr};
use flate2::read::ZlibDecoder;
use super::*;
#[derive(Debug)]
pub struct Loader {
pub path: PathBuf
}
impl super::WorldLoader for Loader {
fn load_chunk(&self, x: i32, z: i32) -> Chunk {
let region = chunk_to_region(x, z);
let mut region_file_path = self.path.clone();
region_file_path.push(PathBuf::from_str("region").unwrap());
region_file_path.push(PathBuf::from_str(format!("r.{}.{}.mca", region.0, region.1).as_str()).unwrap());
if !fs::exists(region_file_path.clone()).unwrap() {
return Chunk::new(x, z);
}
//TODO: only read in necessary ranges and not whole file
let region_file = fs::read(region_file_path).unwrap();
let chunk_pos_in_header = 4 * ((x & 31) + (z & 31) * 32);
let chunk_location_bytes = &region_file[(chunk_pos_in_header as usize)..=((chunk_pos_in_header+2) as usize)];
let chunk_offset = i32::from_be_bytes([0, chunk_location_bytes[0], chunk_location_bytes[1], chunk_location_bytes[2]]) * 4096;
let chunk_length_padded = region_file[(chunk_pos_in_header+3) as usize] as i32 * 4096;
if chunk_offset == 0 && chunk_length_padded == 0 {
return Chunk::new(x, z);
}
let actual_chunk_length_bytes = &region_file[(chunk_offset as usize)..=(chunk_offset+3) as usize];
let actual_chunk_length = i32::from_be_bytes([actual_chunk_length_bytes[0], actual_chunk_length_bytes[1], actual_chunk_length_bytes[2], actual_chunk_length_bytes[3]]);
let compression_scheme = region_file[(chunk_offset+4) as usize];
let compressed_data: &[u8] = &region_file[((chunk_offset+5) as usize)..=(chunk_offset+actual_chunk_length) as usize];
let mut uncompressed_data: Vec<u8> = Vec::new();
if compression_scheme == 2 {
let mut decoder: ZlibDecoder<&[u8]> = ZlibDecoder::new(compressed_data);
decoder.read_to_end(&mut uncompressed_data).unwrap();
} else {
panic!("unknown chunk compression scheme {compression_scheme}");
}
let chunk_nbt = crate::deserialize::nbt_disk(&mut uncompressed_data).unwrap();
if chunk_nbt.get_child("Status").unwrap().as_string() != "minecraft:full" {
return Chunk::new(x, z);
}
let block_states = data::blocks::get_blocks();
let sections: Vec<super::ChunkSection> = chunk_nbt.get_child("sections").unwrap().as_list().into_iter().map(|x| {
let palette = x.get_child("block_states").unwrap().get_child("palette").unwrap().as_list();
if palette.len() == 1 {
return ChunkSection { blocks: vec![block_states.get(&palette[0].get_child("Name").unwrap().as_string()).unwrap().states.iter().find(|x| x.default).unwrap().id; 4096] }
}
let bits_per_entry = match palette.len() {
0..=16 => 4,
17..=32 => 5,
33..=64 => 6,
65..=128 => 7,
129..=256 => 8,
257..=512 => 9,
513..=1024 => 10,
1025..=2048 => 11,
_ => 12,
};
let long_array = x.get_child("block_states").unwrap().get_child("data").unwrap().as_long_array();
let mut data_array: Vec<i32> = Vec::new();
for value in long_array {
let entries_per_long = 64 / bits_per_entry;
for i in 0..entries_per_long {
if data_array.len() == 4096 {
break;
}
let entry = value as u64 >> (64 - ((i+1) * bits_per_entry));
let entry = entry & (u64::MAX >> (64 - bits_per_entry));
data_array.push(entry as i32);
}
}
assert_eq!(data_array.len(), 4096);
return ChunkSection { blocks: data_array };
}).collect();
return Chunk {
x: chunk_nbt.get_child("xPos").unwrap().as_int(),
z: chunk_nbt.get_child("zPos").unwrap().as_int(),
sections,
};
}
fn is_initialized(&self) -> bool {
let mut level_dat_path = self.path.clone();
level_dat_path.push(PathBuf::from_str("level.dat").unwrap());
return std::fs::exists(level_dat_path).unwrap();
}
}
fn chunk_to_region(x: i32, z: i32) -> (i32, i32) {
return ((x as f32 / 32.0).floor() as i32, (z as f32 / 32.0).floor() as i32)
}
#[cfg(test)]
mod test {
use super::*;
mod chunk_to_region {
use super::*;
#[test]
fn works_positive() {
assert_eq!(chunk_to_region(6, 5), (0, 0));
}
#[test]
fn works_positive_large() {
assert_eq!(chunk_to_region(12345, 321), (385, 10));
}
#[test]
fn works_zero() {
assert_eq!(chunk_to_region(0, 0), (0, 0));
}
#[test]
fn works_negative() {
assert_eq!(chunk_to_region(-10, -20), (-1, -1));
}
#[test]
fn works_negative_large() {
assert_eq!(chunk_to_region(-1000, -2000), (-32, -63));
}
}
}

View file

@ -1,3 +1,5 @@
pub mod loader;
use std::{collections::HashMap, error::Error};
use crate::types::position::Position;
@ -27,12 +29,18 @@ pub struct ChunkSection {
impl World {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
println!("create new world");
let mut dimensions: HashMap<String, Dimension> = HashMap::new();
dimensions.insert("minecraft:overworld".to_string(), Dimension::new());
println!("creation of new world finished");
pub fn new(loader: impl loader::WorldLoader) -> Self {
let mut dimensions: HashMap<String, Dimension> = HashMap::new();
if loader.is_initialized() {
let now = std::time::Instant::now();
println!("loading existing world");
dimensions.insert("minecraft:overworld".to_string(), Dimension::new_from_loader(loader));
println!("finished loading existing world in {:.2?}", now.elapsed());
} else {
println!("create new world");
dimensions.insert("minecraft:overworld".to_string(), Dimension::new());
println!("creation of new world finished");
}
return Self { dimensions };
}
}
@ -53,6 +61,20 @@ impl Dimension {
};
}
pub fn new_from_loader(loader: impl loader::WorldLoader) -> Self {
let mut chunks: Vec<Chunk> = Vec::new();
for x in -20..=20 {
for z in -20..=20 {
chunks.push(loader.load_chunk(x, z));
}
}
return Self {
chunks,
}
}
pub fn get_chunk_from_position_mut(&mut self, position: Position) -> Option<&mut Chunk> {
let chunk_coordinates = position.convert_to_coordinates_of_chunk();

41
proxy/Cargo.lock generated
View file

@ -2,15 +2,56 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "cfg-if"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
[[package]]
name = "crc32fast"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
dependencies = [
"cfg-if",
]
[[package]]
name = "data"
version = "0.1.0"
[[package]]
name = "flate2"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "lib"
version = "0.1.0"
dependencies = [
"data",
"flate2",
]
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
]
[[package]]

41
server/Cargo.lock generated
View file

@ -2,15 +2,56 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "cfg-if"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
[[package]]
name = "crc32fast"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
dependencies = [
"cfg-if",
]
[[package]]
name = "data"
version = "0.1.0"
[[package]]
name = "flate2"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "lib"
version = "0.1.0"
dependencies = [
"data",
"flate2",
]
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
]
[[package]]

View file

@ -2,8 +2,8 @@
use std::collections::HashMap;
use std::net::{TcpListener, SocketAddr, TcpStream};
use std::path::Path;
use std::sync::{Arc, Mutex};
use lib::types::world::World;
use types::*;
mod packet_handlers;
@ -19,10 +19,14 @@ fn main() {
fn initialize_server() {
let listener = TcpListener::bind("0.0.0.0:25565").unwrap();
let world_loader = lib::world::loader::vanilla::Loader {
path: Path::new("./world").to_owned(),
};
let connections: Arc<Mutex<HashMap<SocketAddr, Connection>>> = Arc::new(Mutex::new(HashMap::new()));
let mut game = Game {
players: Vec::new(),
world: World::new(),
world: World::new(world_loader),
last_created_entity_id: 0,
chat_message_index: 0,
commands: Vec::new(),