CHUNK LOADING KINDA WORKS

This commit is contained in:
lukas0008
2024-08-10 21:46:58 +02:00
parent b4abc6b297
commit a991ce214b
19 changed files with 273205 additions and 264 deletions

308
Cargo.lock generated
View File

@@ -67,12 +67,6 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]]
name = "bit_field"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
[[package]]
name = "bitflags"
version = "1.3.2"
@@ -135,9 +129,9 @@ checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
[[package]]
name = "cc"
version = "1.1.7"
version = "1.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc"
checksum = "504bdec147f2cc13c8b57ed9401fd8a147cc66b67ad5cb241394244f2c947549"
[[package]]
name = "cesu8"
@@ -170,12 +164,6 @@ dependencies = [
"inout",
]
[[package]]
name = "color_quant"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "colored"
version = "2.1.0"
@@ -301,6 +289,12 @@ dependencies = [
"crypto-common 0.2.0-rc.0",
]
[[package]]
name = "either"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "encoding_rs"
version = "0.8.34"
@@ -326,37 +320,6 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "fastanvil"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "619fbd9bb48f9e54a7ff88cdd0f7048b8ea519448518c690057f9d03d3e81ebf"
dependencies = [
"bit_field",
"byteorder",
"fastnbt 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"flate2",
"image 0.23.14",
"log",
"num_enum",
"once_cell",
"serde",
"serde_json",
"tar",
]
[[package]]
name = "fastnbt"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d4a73a95dc65551ccd98e1ecd1adb5d1ba5361146963b31f481ca42fc0520a3"
dependencies = [
"byteorder",
"cesu8",
"serde",
"serde_bytes",
]
[[package]]
name = "fastnbt"
version = "2.5.0"
@@ -396,18 +359,6 @@ dependencies = [
"simd-adler32",
]
[[package]]
name = "filetime"
version = "0.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"windows-sys 0.52.0",
]
[[package]]
name = "flate2"
version = "1.0.31"
@@ -448,6 +399,21 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "futures"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.30"
@@ -455,6 +421,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
@@ -463,6 +430,34 @@ version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
[[package]]
name = "futures-executor"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
[[package]]
name = "futures-macro"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.30"
@@ -481,10 +476,16 @@ version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]]
@@ -649,9 +650,9 @@ dependencies = [
[[package]]
name = "hyper-util"
version = "0.1.6"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956"
checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9"
dependencies = [
"bytes",
"futures-channel",
@@ -677,20 +678,6 @@ dependencies = [
"unicode-normalization",
]
[[package]]
name = "image"
version = "0.23.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1"
dependencies = [
"bytemuck",
"byteorder",
"color_quant",
"num-iter",
"num-rational",
"num-traits",
]
[[package]]
name = "image"
version = "0.25.2"
@@ -728,6 +715,15 @@ version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
[[package]]
name = "itertools"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.11"
@@ -885,7 +881,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn",
]
[[package]]
@@ -908,17 +904,6 @@ dependencies = [
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.19"
@@ -929,27 +914,6 @@ dependencies = [
"libm",
]
[[package]]
name = "num_enum"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9"
dependencies = [
"num_enum_derive",
]
[[package]]
name = "num_enum_derive"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "num_threads"
version = "0.1.7"
@@ -961,9 +925,9 @@ dependencies = [
[[package]]
name = "object"
version = "0.36.2"
version = "0.36.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e"
checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9"
dependencies = [
"memchr",
]
@@ -997,7 +961,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn",
]
[[package]]
@@ -1050,7 +1014,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn",
]
[[package]]
@@ -1120,16 +1084,6 @@ dependencies = [
"zerocopy",
]
[[package]]
name = "proc-macro-crate"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
dependencies = [
"once_cell",
"toml_edit 0.19.15",
]
[[package]]
name = "proc-macro2"
version = "1.0.86"
@@ -1146,7 +1100,7 @@ dependencies = [
"base64",
"crossbeam-channel",
"digest 0.11.0-pre.9",
"image 0.25.2",
"image",
"log",
"mio",
"num-bigint",
@@ -1184,7 +1138,7 @@ version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn",
]
[[package]]
@@ -1194,12 +1148,14 @@ dependencies = [
"aes",
"bytes",
"cfb8",
"fastnbt 2.5.0 (git+https://github.com/owengage/fastnbt.git)",
"fastnbt",
"flate2",
"itertools",
"log",
"num-derive",
"num-traits",
"pumpkin-macros",
"pumpkin-world",
"serde",
"serde_json",
"take_mut",
@@ -1211,7 +1167,7 @@ dependencies = [
name = "pumpkin-registry"
version = "0.1.0"
dependencies = [
"fastnbt 2.5.0 (git+https://github.com/owengage/fastnbt.git)",
"fastnbt",
"fastsnbt",
"pumpkin-protocol",
"serde",
@@ -1221,10 +1177,15 @@ dependencies = [
name = "pumpkin-world"
version = "0.1.0"
dependencies = [
"fastanvil",
"fastnbt 2.5.0 (git+https://github.com/owengage/fastnbt.git)",
"fastnbt",
"fastsnbt",
"pumpkin-protocol",
"flate2",
"futures",
"itertools",
"lazy_static",
"serde",
"serde_json",
"thiserror",
"tokio",
]
@@ -1267,15 +1228,6 @@ dependencies = [
"getrandom",
]
[[package]]
name = "redox_syscall"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "reqwest"
version = "0.12.5"
@@ -1407,9 +1359,9 @@ dependencies = [
[[package]]
name = "rustls-pki-types"
version = "1.7.0"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d"
checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0"
[[package]]
name = "rustls-webpki"
@@ -1462,9 +1414,9 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.204"
version = "1.0.205"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
checksum = "e33aedb1a7135da52b7c21791455563facbbcc43d0f0f66165b42c21b3dfb150"
dependencies = [
"serde_derive",
]
@@ -1480,13 +1432,13 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.204"
version = "1.0.205"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
checksum = "692d6f5ac90220161d6774db30c662202721e64aed9058d2c394f451261420c1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn",
]
[[package]]
@@ -1620,17 +1572,6 @@ version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.72"
@@ -1675,27 +1616,17 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60"
[[package]]
name = "tar"
version = "0.4.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb797dad5fb5b76fcf519e702f4a589483b5ef06567f160c392832c1f5e44909"
dependencies = [
"filetime",
"libc",
]
[[package]]
name = "tempfile"
version = "3.11.0"
version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8fcd239983515c23a32fb82099f97d0b11b8c72f654ed659363a95c3dad7a53"
checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
dependencies = [
"cfg-if",
"fastrand",
"once_cell",
"rustix",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -1715,7 +1646,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn",
]
[[package]]
@@ -1790,7 +1721,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn",
]
[[package]]
@@ -1836,7 +1767,7 @@ dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit 0.22.20",
"toml_edit",
]
[[package]]
@@ -1848,17 +1779,6 @@ dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.19.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
dependencies = [
"indexmap",
"toml_datetime",
"winnow 0.5.40",
]
[[package]]
name = "toml_edit"
version = "0.22.20"
@@ -1869,7 +1789,7 @@ dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"winnow 0.6.18",
"winnow",
]
[[package]]
@@ -2025,7 +1945,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.72",
"syn",
"wasm-bindgen-shared",
]
@@ -2059,7 +1979,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -2098,6 +2018,15 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
@@ -2219,15 +2148,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "0.5.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
dependencies = [
"memchr",
]
[[package]]
name = "winnow"
version = "0.6.18"
@@ -2265,7 +2185,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
"syn",
]
[[package]]

View File

@@ -6,6 +6,7 @@ edition.workspace = true
[dependencies]
pumpkin-macros = { path = "../pumpkin-macros" }
pumpkin-world = { path = "../pumpkin-world" }
bytes = "1.7"
@@ -29,3 +30,4 @@ fastnbt = { git = "https://github.com/owengage/fastnbt.git" }
aes = "0.8.4"
cfb8 = "0.8.1"
take_mut = "0.2.2"
itertools = "0.13.0"

View File

@@ -3,10 +3,20 @@ use serde::{
Deserialize, Deserializer, Serialize, Serializer,
};
use crate::{ClientPacket, ServerPacket, VarInt, VarIntType};
use crate::{BitSet, ClientPacket, ServerPacket, VarInt, VarIntType};
use super::{deserializer, serializer, ByteBuffer, DeserializerError};
impl Serialize for BitSet {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
// TODO: make this right
(self.0.clone(), self.1.clone()).serialize(serializer)
}
}
impl Serialize for VarInt {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where

View File

@@ -0,0 +1,10 @@
use pumpkin_macros::packet;
use crate::VarInt;
#[derive(serde::Serialize)]
#[packet(0x54)]
pub struct CCenterChunk {
pub chunk_x: VarInt,
pub chunk_z: VarInt
}

View File

@@ -0,0 +1,113 @@
use std::collections::HashMap;
use crate::{bytebuf::ByteBuffer, BitSet, ClientPacket, VarInt};
use fastnbt::LongArray;
use itertools::Itertools;
use pumpkin_macros::packet;
use pumpkin_world::chunk::ChunkData;
#[packet(0x27)]
pub struct CChunkData<'a>(pub &'a ChunkData);
impl<'a> ClientPacket for CChunkData<'a> {
fn write(&self, buf: &mut crate::bytebuf::ByteBuffer) {
// Chunk X
buf.put_i32(self.0.position.0);
// Chunk Z
buf.put_i32(self.0.position.1);
let heightmap_nbt =
fastnbt::to_bytes_with_opts(&self.0.heightmaps, fastnbt::SerOpts::network_nbt())
.unwrap();
// Heightmaps
buf.put_slice(&heightmap_nbt);
let mut data_buf = ByteBuffer::empty();
self.0
.blocks
.iter()
.chunks(16 * 16 * 16)
.into_iter()
.for_each(|chunk| {
let chunk = chunk.collect_vec();
let block_count = chunk
.iter()
.filter(|block| ***block != 0 && ***block != 12959 && ***block != 12958)
.count() as i16;
// Block count
data_buf.put_i16(block_count);
//// Block states
let palette = chunk.clone().into_iter().dedup().collect_vec();
let mut palette_map = HashMap::new();
let block_size = 8;
// let block_size = {
// let palette_bit_len = palette.len().leading_zeros();
// if palette_bit_len > 3 {
// palette_bit_len
// } else {
// 4
// }
// };
// Bits per entry
data_buf.put_u8(block_size as u8);
// Palette length
data_buf.put_var_int(&VarInt(palette.len() as i32));
palette.iter().enumerate().for_each(|(i, id)| {
palette_map.insert(*id, i);
// Palette
data_buf.put_var_int(&VarInt(**id as i32));
});
let mut block_data_array = Vec::new();
for block_clump in chunk.chunks(64 / block_size as usize) {
let mut out_long: i64 = 0;
let mut first = true;
for block in block_clump {
if first {
first = false;
} else {
out_long = out_long << block_size;
}
let index = palette_map
.get(block)
.expect("Its just got added, ofc it should be there");
out_long = out_long | *index as i64;
}
block_data_array.push(out_long);
}
// Data array length
data_buf.put_var_int(&VarInt(block_data_array.len() as i32));
// Data array
for data_int in block_data_array {
data_buf.put_i64(data_int);
}
//// Biomes
// TODO: make biomes work
data_buf.put_u8(0);
data_buf.put_var_int(&VarInt(0));
data_buf.put_var_int(&VarInt(0));
});
// Size
buf.put_var_int(&VarInt(data_buf.buf().len() as i32));
// Data
buf.put_slice(&data_buf.buf());
// TODO: block entities
buf.put_var_int(&VarInt(0));
// TODO
buf.put_bit_set(&BitSet(VarInt(1), vec![0]));
buf.put_bit_set(&BitSet(VarInt(1), vec![0]));
buf.put_bit_set(&BitSet(VarInt(1), vec![0]));
buf.put_bit_set(&BitSet(VarInt(1), vec![0]));
// buf.put_bit_set(&BitSet(VarInt(0), vec![]));
// buf.put_bit_set(&BitSet(VarInt(0), vec![]));
// buf.put_bit_set(&BitSet(VarInt(0), vec![]));
// buf.put_bit_set(&BitSet(VarInt(0), vec![]));
buf.put_var_int(&VarInt(0));
buf.put_var_int(&VarInt(0));
}
}

View File

@@ -1,4 +1,6 @@
mod c_center_chunk;
mod c_change_difficulty;
mod c_chunk_data;
mod c_chunk_data_update_light;
mod c_disguised_chat_message;
mod c_entity_animation;
@@ -20,7 +22,9 @@ mod c_update_entity_pos;
mod c_update_entity_rot;
mod player_action;
pub use c_center_chunk::*;
pub use c_change_difficulty::*;
pub use c_chunk_data::*;
pub use c_chunk_data_update_light::*;
pub use c_disguised_chat_message::*;
pub use c_entity_animation::*;

View File

@@ -32,7 +32,7 @@ impl PacketEncoder {
.encode(&mut writer)
.map_err(|_| PacketError::EncodeID)?;
packet.write(&mut packet_buf);
writer
.write(packet_buf.buf())
.map_err(|_| PacketError::EncodeFailedWrite)?;

View File

@@ -4,8 +4,14 @@ version.workspace = true
edition.workspace = true
[dependencies]
pumpkin-protocol = { path = "../pumpkin-protocol"}
fastanvil = "0.31"
# fastanvil = "0.31"
fastnbt = { git = "https://github.com/owengage/fastnbt.git" }
fastsnbt = "0.2"
tokio.workspace = true
tokio.workspace = true
itertools = "0.13.0"
thiserror = "1.0.63"
futures = "0.3.30"
flate2 = "1.0.31"
serde = { version = "1.0.205", features = ["derive"] }
lazy_static = "1.5.0"
serde_json = "1.0.122"

272408
pumpkin-world/blocks.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,58 @@
use std::collections::HashMap;
use lazy_static::lazy_static;
use crate::world::WorldError;
const BLOCKS_JSON: &'static str = include_str!("../blocks.json");
#[derive(serde::Deserialize, Debug, Clone, PartialEq, Eq)]
struct BlockDefinition {
#[serde(rename = "type")]
kind: String,
block_set_type: Option<String>,
}
#[derive(serde::Deserialize, Debug, Clone, PartialEq, Eq)]
struct BlockState {
default: Option<bool>,
id: i64,
properties: Option<HashMap<String, String>>,
}
#[derive(serde::Deserialize, Debug, Clone, PartialEq, Eq)]
struct BlocksElement {
definition: BlockDefinition,
properties: Option<HashMap<String, Vec<String>>>,
states: Vec<BlockState>,
}
lazy_static! {
static ref BLOCKS: HashMap<String, BlocksElement> =
serde_json::from_str(BLOCKS_JSON).expect("Could not parse block.json registry.");
}
pub fn block_id_and_properties_to_block_state_id(
block_id: &str,
properties: Option<&HashMap<String, String>>,
) -> Result<i64, WorldError> {
let block = match BLOCKS.get(block_id) {
Some(block) => block,
None => return Err(WorldError::BlockStateIdNotFound),
};
let block_state_id = match properties {
None => Ok(block
.states
.iter()
.find(|state| state.default.unwrap_or(false))
.expect("Each block should have at least one default state")
.id),
Some(properties) => block
.states
.iter()
.find(|state| state.properties.as_ref() == Some(properties))
.map(|state| state.id)
.ok_or(WorldError::BlockStateIdNotFound),
};
block_state_id
}

View File

@@ -1,26 +1,161 @@
use fastnbt::nbt;
// use fastnbt::nbt;
pub const BLOCKS_AND_BIOMES: [u8; 2000] = [0x80; 2000];
pub const SKY_LIGHT_ARRAYS: [FixedArray<u8, 2048>; 26] = [FixedArray([0xff; 2048]); 26];
// pub const BLOCKS_AND_BIOMES: [u8; 2000] = [0x80; 2000];
// pub const SKY_LIGHT_ARRAYS: [FixedArray<u8, 2048>; 26] = [FixedArray([0xff; 2048]); 26];
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[repr(transparent)]
pub struct FixedArray<T, const N: usize>(pub [T; N]);
// #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
// #[repr(transparent)]
// pub struct FixedArray<T, const N: usize>(pub [T; N]);
pub struct TestChunk {
pub heightmap: Vec<u8>,
// pub struct TestChunk {
// pub heightmap: Vec<u8>,
// }
// impl Default for TestChunk {
// fn default() -> Self {
// Self::new()
// }
// }
// impl TestChunk {
// pub fn new() -> Self {
// let bytes = fastnbt::to_bytes(&nbt!({"MOTION_BLOCKING": [L; 123, 256]})).unwrap();
// Self { heightmap: bytes }
// }
// }
use std::{collections::HashMap, io::Write};
use fastnbt::LongArray;
use itertools::Itertools;
use crate::{world::WorldError, WORLD_HEIGHT};
pub struct ChunkData {
pub blocks: Box<[i64; 16 * 16 * WORLD_HEIGHT]>,
pub position: (i32, i32),
pub heightmaps: ChunkHeightmaps,
}
impl Default for TestChunk {
fn default() -> Self {
Self::new()
}
#[derive(serde::Deserialize, Debug, Clone)]
#[serde(rename_all = "PascalCase")]
struct PaletteEntry {
name: String,
properties: Option<HashMap<String, String>>,
}
impl TestChunk {
pub fn new() -> Self {
let bytes = fastnbt::to_bytes(&nbt!({"MOTION_BLOCKING": [L; 123, 256]})).unwrap();
#[derive(serde::Deserialize, Debug, Clone)]
struct ChunkSectionBlockStates {
data: Option<LongArray>, // TODO: see if you can use u32 here
palette: Vec<PaletteEntry>,
}
#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)]
#[serde(rename_all = "UPPERCASE")]
pub struct ChunkHeightmaps {
motion_blocking: LongArray,
world_surface: LongArray,
}
#[derive(serde::Deserialize, Debug)]
struct ChunkSection {
#[serde(rename = "Y")]
y: i32,
block_states: Option<ChunkSectionBlockStates>,
}
#[derive(serde::Deserialize, Debug)]
struct ChunkNbt {
#[serde(rename = "DataVersion")]
data_version: usize,
sections: Vec<ChunkSection>,
#[serde(rename = "Heightmaps")]
heightmaps: ChunkHeightmaps,
}
impl ChunkData {
pub fn from_bytes(chunk_data: Vec<u8>, at: (i32, i32)) -> Result<Self, WorldError> {
let chunk_data = match fastnbt::from_bytes::<ChunkNbt>(chunk_data.as_slice()) {
Ok(v) => v,
Err(err) => return Err(WorldError::ErrorDeserializingChunk(err.to_string())),
};
// this needs to be boxed, otherwise it will cause a stack-overflow
let mut blocks = Box::new([0; 16 * 16 * WORLD_HEIGHT]);
for (k, section) in chunk_data.sections.into_iter().enumerate() {
let block_states = match section.block_states {
Some(states) => states,
None => continue, // this should instead fill all blocks with the only element of the palette
};
let palette = block_states
.palette
.iter()
.map(|entry| {
crate::block_registry::block_id_and_properties_to_block_state_id(
&entry.name,
entry.properties.as_ref(),
)
})
.collect::<Result<Vec<_>, _>>()?;
let block_data = match block_states.data {
None => continue,
Some(d) => d,
}
.into_inner();
let block_size = {
let size = 64 - (palette.len() - 1).leading_zeros();
if size >= 4 {
size
} else {
4
}
};
let mask = (1 << block_size) - 1;
println!("{block_size} {:#08b}", mask);
println!(
"{} {}",
block_size,
palette.iter().map(|v| v.to_string()).join(",")
);
let mut blocks_left = 16 * 16 * 16;
'block_loop: for (j, block) in block_data.iter().enumerate() {
for i in 0..64 / block_size {
if blocks_left <= 0 {
break 'block_loop;
}
let index = (block >> (i * block_size)) & mask;
let block = palette[index as usize];
blocks[k * 16 * 16 * 16 + j * ((64 / block_size) as usize) + i as usize] =
block;
blocks_left -= 1;
}
}
}
// std::fs::File::create_new("../mafaile.txt")
// .unwrap()
// .write_all(
// blocks
// .iter()
// .map(|v| v.to_string())
// .collect::<Vec<_>>()
// .join(",")
// .as_bytes(),
// );
Self { heightmap: bytes }
if at == (0, 0) {
println!(
"[{}]",
&blocks[0..10000].iter().map(|v| v.to_string()).join(",")
);
}
Ok(ChunkData {
blocks: blocks,
position: at,
heightmaps: chunk_data.heightmaps,
})
}
}

View File

@@ -0,0 +1,21 @@
use std::path::PathBuf;
use crate::world::Level;
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum Dimension {
OverWorld,
Nether,
End,
}
impl Dimension {
pub fn into_level(&self, mut base_directory: PathBuf) -> Level {
match self {
Dimension::OverWorld => {}
Dimension::Nether => base_directory.push("DIM-1"),
Dimension::End => base_directory.push("DIM1"),
}
Level::from_root_folder(base_directory)
}
}

View File

@@ -1,26 +1,6 @@
use std::{fs::File, path::Path};
use fastanvil::Region;
pub mod chunk;
pub struct World {
pub region: Region<File>,
}
impl World {
pub fn load<P: AsRef<Path>>(path: P) -> Self {
let file = std::fs::File::open(path).unwrap();
let region = Region::from_stream(file).unwrap();
Self { region }
}
/* pub fn load_chunk(&mut self, x: usize, y: usize) -> Chunk {
let data = self.region.read_chunk(x, y).unwrap().unwrap();
let complete_chunk = complete::Chunk::from_bytes(&data).unwrap();
}
*/
}
pub mod dimension;
pub const WORLD_HEIGHT: usize = 384;
pub const WORLD_Y_START_AT: i32 = -64; // TODO: make sure where it actually starts at (i think its at -64 but not sure)
mod world;
mod block_registry;

245
pumpkin-world/src/world.rs Normal file
View File

@@ -0,0 +1,245 @@
use std::{collections::VecDeque, future, io::Read, path::PathBuf, sync::Arc};
use flate2::bufread::ZlibDecoder;
use itertools::Itertools;
use thiserror::Error;
use tokio::{
fs::File,
io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt},
sync::Mutex,
};
use crate::{chunk::ChunkData, dimension::Dimension};
pub struct Level {
root_folder: PathBuf,
}
#[derive(Error, Debug)]
pub enum WorldError {
// using ErrorKind instead of Error, beacuse the function read_chunks and read_region_chunks is designed to return an error on a per-chunk basis, while std::io::Error does not implement Copy or Clone
#[error("Io error: {0}")]
IoError(std::io::ErrorKind),
#[error("Region not found")]
RegionNotFound,
#[error("Region is invalid")]
RegionIsInvalid,
#[error("Chunk not found")]
ChunkNotFound,
#[error("Compression scheme not recognised")]
UnknownCompression,
#[error("Error while working with zlib compression: {0}")]
ZlibError(std::io::Error),
#[error("Error deserializing chunk: {0}")]
ErrorDeserializingChunk(String),
#[error("The requested block state id does not exist")]
BlockStateIdNotFound,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Compression {
Gzip,
Zlib,
None,
LZ4,
}
impl Level {
pub fn from_root_folder(root_folder: PathBuf) -> Self {
Level { root_folder }
}
/// Read one chunk in the world
///
/// Do not use this function if reading many chunks is required, since in case those two chunks which are read seperately using `.read_chunk` are in the same region file, it will need to be opened and closed separately for both of them, leading to a performance loss.
pub async fn read_chunk(&self, chunk: (i32, i32)) -> Result<ChunkData, WorldError> {
self.read_chunks(vec![chunk])
.await
.pop()
.expect("Read chunks must return a chunk")
.1
}
/// Read many chunks in a world
///
/// Note: The order of the output chunks will almost never be in the same order as the order of input chunks
pub async fn read_chunks(
&self,
chunks: Vec<(i32, i32)>,
) -> Vec<((i32, i32), Result<ChunkData, WorldError>)> {
futures::future::join_all(
chunks
.into_iter()
// split chunks into their corresponding region files to be able to read all of them at once, instead of reopening the file multiple times
.chunk_by(|chunk| {
(
((chunk.0 as f32) / 32.0).floor() as i32,
((chunk.1 as f32) / 32.0).floor() as i32,
)
})
.into_iter()
.map(|(region, chunk_vec)| {
let mut path = self.root_folder.clone();
path.push("region");
path.push(format!("r.{}.{}.mca", region.0, region.1));
self.read_region_chunks(path, chunk_vec.collect_vec())
}),
)
.await
.into_iter()
.flatten()
.collect_vec()
}
async fn read_region_chunks(
&self,
region_file: PathBuf,
chunks: Vec<(i32, i32)>,
) -> Vec<((i32, i32), Result<ChunkData, WorldError>)> {
// dbg!(at);
println!(
"Getting chunks {:?}, from region file {}",
&chunks,
region_file.to_str().unwrap_or("")
);
// return different error when file is not found (because that means that the chunks have just not been generated yet)
let mut region_file = match File::open(region_file).await {
Ok(f) => f,
Err(err) => match err.kind() {
std::io::ErrorKind::NotFound => {
return chunks
.into_iter()
.map(|c| (c, Err(WorldError::RegionNotFound)))
.collect_vec()
}
_ => {
return chunks
.into_iter()
.map(|c| (c, Err(WorldError::IoError(err.kind()))))
.collect_vec()
}
},
};
let mut location_table: [u8; 4096] = [0; 4096];
let mut timestamp_table: [u8; 4096] = [0; 4096];
// fill the location and timestamp tables
{
match region_file.read_exact(&mut location_table).await {
Ok(_) => {}
Err(err) => {
return chunks
.into_iter()
.map(|c| (c, Err(WorldError::IoError(err.kind()))))
.collect_vec()
}
}
match region_file.read_exact(&mut timestamp_table).await {
Ok(_) => {}
Err(err) => {
return chunks
.into_iter()
.map(|c| (c, Err(WorldError::IoError(err.kind()))))
.collect_vec()
}
}
}
// println!("Location table: {:?}", &location_table);
// wrap file with arc mutex to allow for multithreading
let region_file = Arc::new(Mutex::new(region_file));
futures::future::join_all(chunks.into_iter().map(|(old_chunk_x, old_chunk_z)| {
let region_file = region_file.clone();
let modulus = |a: i32, b: i32| ((a % b) + b) % b;
let chunk_x = modulus(old_chunk_x, 32) as u32;
let chunk_z = modulus(old_chunk_z, 32) as u32;
async move {
let table_entry = (chunk_x + chunk_z * 32) * 4;
let mut offset = vec![0u8];
offset.extend_from_slice(
&location_table[table_entry as usize..table_entry as usize + 3],
);
let offset = u32::from_be_bytes(offset.try_into().unwrap()) as u64 * 4096;
let size = location_table[table_entry as usize + 3] as usize * 4096;
if offset == 0 && size == 0 {
return ((old_chunk_x, old_chunk_z), Err(WorldError::ChunkNotFound));
}
// Read the file using the offset and size
let mut file_buf = {
let mut region_file = region_file.lock().await;
let seek_result = region_file.seek(std::io::SeekFrom::Start(offset)).await;
if seek_result.is_err() {
return ((old_chunk_x, old_chunk_z), Err(WorldError::RegionIsInvalid));
}
let mut out = vec![0; size];
let read_result = region_file.read_exact(&mut out).await;
if read_result.is_err() {
return ((old_chunk_x, old_chunk_z), Err(WorldError::RegionIsInvalid));
}
out
};
// TODO: check checksum to make sure chunk is not corrupted
let header = file_buf.drain(0..5).collect_vec();
let compression = match header[4] {
1 => Compression::Gzip,
2 => Compression::Zlib,
3 => Compression::None,
4 => Compression::LZ4,
_ => return ((old_chunk_x, old_chunk_z), Err(WorldError::RegionIsInvalid)),
};
match compression {
Compression::Zlib => {}
_ => panic!(), // TODO: support other compression types
}
let size = u32::from_be_bytes(header[0..4].try_into().unwrap());
let chunk_data = file_buf.drain(0..size as usize).collect_vec();
let mut z = ZlibDecoder::new(&chunk_data[..]);
let mut chunk_data = Vec::new();
match z.read_to_end(&mut chunk_data) {
Ok(_) => {}
Err(err) => {
return ((old_chunk_x, old_chunk_z), Err(WorldError::ZlibError(err)))
}
}
// TODO: remove
// File::create_new(format!("./test-{}.{}.nbt", old_chunk_x, old_chunk_z))
// .await
// .unwrap()
// .write_all(&chunk_data)
// .await
// .unwrap();
(
(old_chunk_x, old_chunk_z),
ChunkData::from_bytes(chunk_data, (old_chunk_x, old_chunk_z)),
)
}
}))
.await
.into_iter()
.collect_vec()
}
}
#[test]
fn wawa() {
tokio::runtime::Runtime::new().unwrap().block_on(async {
Dimension::OverWorld
.into_level(
"C:\\Users\\lukza\\Desktop\\code\\rust\\vanilla_mc_server\\world"
.parse()
.unwrap(),
)
.read_chunk((0, 0))
.await
.unwrap();
});
}

BIN
pumpkin-world/test.nbt Normal file

Binary file not shown.

View File

@@ -217,7 +217,7 @@ impl Client {
self.send_packet(CFinishConfig::new());
}
pub fn handle_config_acknowledged(
pub async fn handle_config_acknowledged(
&mut self,
server: &mut Server,
_config_acknowledged: SAcknowledgeFinishConfig,
@@ -225,6 +225,6 @@ impl Client {
dbg!("config acknowledged");
self.connection_state = ConnectionState::Play;
// generate a player
server.spawn_player(self);
server.spawn_player(self).await;
}
}

View File

@@ -226,7 +226,7 @@ impl Client {
SAcknowledgeFinishConfig::PACKET_ID => self.handle_config_acknowledged(
server,
SAcknowledgeFinishConfig::read(bytebuf).unwrap(),
),
).await,
SKnownPacks::PACKET_ID => {
self.handle_known_packs(server, SKnownPacks::read(bytebuf).unwrap())
}

View File

@@ -13,7 +13,6 @@ use config::BasicConfiguration;
use server::Server;
// Setup some tokens to allow us to identify which event is for which socket.
const SERVER: Token = Token(0);
pub mod client;
pub mod commands;
@@ -25,6 +24,7 @@ pub mod util;
#[cfg(not(target_os = "wasi"))]
#[tokio::main]
async fn main() -> io::Result<()> {
const SERVER: Token = Token(0);
use std::{cell::RefCell, time::Instant};
let time = Instant::now();

View File

@@ -15,13 +15,13 @@ use pumpkin_protocol::{
client::{
config::CPluginMessage,
play::{
CChunkDataUpdateLight, CGameEvent, CLogin, CPlayerAbilities, CPlayerInfoUpdate,
CRemoveEntities, CSpawnEntity, PlayerAction,
CCenterChunk, CChunkData, CChunkDataUpdateLight, CGameEvent, CLogin, CPlayerAbilities,
CPlayerInfoUpdate, CRemoveEntities, CSpawnEntity, PlayerAction,
},
},
BitSet, ClientPacket, Players, Sample, StatusResponse, VarInt, Version, CURRENT_MC_PROTOCOL,
};
use pumpkin_world::chunk::TestChunk;
use pumpkin_world::dimension::Dimension;
use rsa::{traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey};
use serde::{Deserialize, Serialize};
@@ -123,7 +123,7 @@ impl Server {
// here is where the magic happens
// TODO: do this in a world
pub fn spawn_player(&mut self, client: &mut Client) {
pub async fn spawn_player(&mut self, client: &mut Client) {
// This code follows the vanilla packet order
let entity_id = self.new_entity_id();
log::debug!("spawning player, entity id {}", entity_id);
@@ -163,7 +163,7 @@ impl Server {
// teleport
let x = 10.0;
let y = 500.0;
let y = 120.0;
let z = 10.0;
client.teleport(x, y, z, 10.0, 10.0);
let gameprofile = client.gameprofile.as_ref().unwrap();
@@ -257,7 +257,7 @@ impl Server {
}
*/
// Server::spawn_test_chunk(client);
Server::spawn_test_chunk(client).await;
}
/// Sends a Packet to all Players
@@ -294,20 +294,49 @@ impl Server {
}
// TODO: do this in a world
fn _spawn_test_chunk(client: &mut Client) {
let test_chunk = TestChunk::new();
client.send_packet(CChunkDataUpdateLight::new(
10,
10,
test_chunk.heightmap,
Vec::new(),
Vec::new(),
BitSet(0.into(), Vec::new()),
BitSet(0.into(), Vec::new()),
BitSet(0.into(), Vec::new()),
Vec::new(),
Vec::new(),
));
async fn spawn_test_chunk(client: &mut Client) {
let mut wanted_chunks = Vec::new();
for i in -3i32..3 {
for j in -3i32..3 {
wanted_chunks.push((i, j))
}
}
let chunks = Dimension::OverWorld
.into_level(
"C:\\Users\\lukza\\Desktop\\code\\rust\\vanilla_mc_server\\world"
.parse()
.unwrap(),
)
.read_chunks(wanted_chunks)
.await;
client.send_packet(CCenterChunk {
chunk_x: 0.into(),
chunk_z: 0.into(),
});
chunks.iter().for_each(|chunk| match &chunk.1 {
Err(err) => println!(
"Chunk loading failed for chunk ({},{}): {}",
chunk.0 .0, chunk.0 .1, err
),
Ok(data) => client.send_packet(CChunkData(data)),
});
// let test_chunk = TestChunk::new();
// client.send_packet(CChunkDataUpdateLight::new(
// 10,
// 10,
// test_chunk.heightmap,
// Vec::new(),
// Vec::new(),
// BitSet(0.into(), Vec::new()),
// BitSet(0.into(), Vec::new()),
// BitSet(0.into(), Vec::new()),
// Vec::new(),
// Vec::new(),
// ));
}
// move to world